Dołączasz jeden plik źródłowy C do innego?


Odpowiedzi:


113

Używana właściwie, może to być przydatna technika.

Załóżmy, że masz złożony, krytyczny pod względem wydajności podsystem z dość małym interfejsem publicznym i dużą ilością kodu implementacyjnego, którego nie można ponownie wykorzystać. Kod zawiera kilka tysięcy wierszy, około stu funkcji prywatnych i sporo prywatnych danych. Jeśli pracujesz z nietrywialnymi systemami wbudowanymi, prawdopodobnie masz do czynienia z taką sytuacją wystarczająco często.

Twoje rozwiązanie będzie prawdopodobnie warstwowe, modułowe i odsprzężone, a aspekty te można pożytecznie przedstawić i wzmocnić poprzez kodowanie różnych części podsystemu w różnych plikach.

Dzięki C możesz wiele stracić, robiąc to. Prawie wszystkie zestawy narzędzi zapewniają przyzwoitą optymalizację dla pojedynczej jednostki kompilacji, ale są bardzo pesymistyczni w stosunku do wszystkiego, co zadeklarowano jako zewnętrzne.

Jeśli umieścisz wszystko w jednym module źródłowym C, otrzymasz -

  • Ulepszenia wydajności i rozmiaru kodu - wywołania funkcji będą w wielu przypadkach wbudowane. Nawet bez wstawiania, kompilator ma możliwości tworzenia bardziej wydajnego kodu.

  • Ukrywanie danych i funkcji na poziomie łącza.

  • Unikanie zanieczyszczenia przestrzeni nazw i jego następstw - możesz używać mniej nieporęcznych nazw.

  • Szybsza kompilacja i łączenie.

Ale jeśli chodzi o edycję tego pliku, dostajesz także przeklęty bałagan i tracisz domniemaną modułowość. Można temu zaradzić, dzieląc źródło na kilka plików i włączając je w celu utworzenia pojedynczej jednostki kompilacji.

Musisz jednak narzucić pewne konwencje, aby właściwie sobie z tym poradzić. Zależą one do pewnego stopnia od twojego zestawu narzędzi, ale niektóre ogólne wskazówki to:

  • Umieść interfejs publiczny w osobnym pliku nagłówkowym - i tak powinieneś to zrobić.

  • Miej jeden główny plik .c, który zawiera wszystkie pomocnicze pliki .c. Może to również obejmować kod interfejsu publicznego.

  • Użyj zabezpieczeń kompilatora, aby upewnić się, że prywatne nagłówki i moduły źródłowe nie są uwzględniane przez zewnętrzne jednostki kompilacji.

  • Wszystkie prywatne dane i funkcje należy zadeklarować jako statyczne.

  • Zachowaj koncepcyjne rozróżnienie między plikami .c i .h. Wykorzystuje to istniejące konwencje. Różnica polega na tym, że w nagłówkach będziesz mieć wiele statycznych deklaracji.

  • Jeśli Twój łańcuch narzędzi nie narzuca żadnego powodu, aby tego nie robić, nazwij prywatne pliki implementacji jako .c i .h. Jeśli używasz osłon włączających, nie wygenerują one żadnego kodu i nie wprowadzą nowych nazw (możesz skończyć z kilkoma pustymi segmentami podczas łączenia). Ogromną zaletą jest to, że inne narzędzia (np. IDE) będą odpowiednio traktować te pliki.


1
+1 to wciąż rzeczywistość, podczas gdy lepsze kompilatory sprawią, że ta metoda stanie się z czasem przestarzała. GCC 4.5 z optymalizacją czasu łącza to duży krok na drodze.
u0b34a0f6ae

Widziałem, jak robi to wiele programów i denerwuje mnie to, gdy próbuję ponownie wykorzystać ich kod. Dziękuję za wyjaśnienie, dlaczego to robią i zasugerowanie użycia osłony (co często się nie robi).
Joey Adams,

W programowaniu wbudowanym dla C51 użycie plików extern i wielu plików C powodowało tylko bóle głowy. Przechodzę na jeden plik C zawierający wszystkie inne, aby odzyskać mój czas.
mahesh

Dla informacji: GCC obsługuje optymalizację w różnych jednostkach tłumaczeniowych, zobacz ten wątek SO i sekcję podręcznika GCC dotyczącą optymalizacji czasu łącza .
Twonky

60

czy to jest w porządku? tak, będzie się kompilować

czy to jest zalecane? no - pliki .c kompilują się do plików .obj, które są łączone ze sobą po kompilacji (przez konsolidator) do pliku wykonywalnego (lub biblioteki), więc nie ma potrzeby dołączania jednego pliku .c do innego. Zamiast tego prawdopodobnie chcesz utworzyć plik .h, który zawiera listę funkcji / zmiennych dostępnych w innym pliku .c, i dołączyć plik .h


14
Warto również zauważyć, że nawet jeśli się skompiluje, może nie połączyć się, jeśli plik #included .c jest również skompilowany, a dwa pliki obiektowe są połączone ze sobą - możesz skończyć z wielokrotnie zdefiniowanymi symbolami.
Nick Meyer,

1
Małe pytanie. Mam plik nagłówkowy deklarujący metodę struct +, a także odpowiedni plik .c do ich zdefiniowania. Jeśli ta metoda przyjmuje strukturę jako parametr, w jaki sposób mógłbym uniknąć umieszczania pliku .c w innym pliku .c, w którym zdefiniowano metodę główną?
wyjście

12

Nie.

W zależności od środowiska kompilacji (nie określasz), może się okazać, że działa dokładnie tak, jak chcesz.

Jednak istnieje wiele środowisk (zarówno IDE, jak i wiele ręcznie tworzonych plików Makefile), które spodziewają się skompilować * .c - jeśli tak się stanie, prawdopodobnie skończysz z błędami linkera z powodu zduplikowanych symboli.

Z zasady należy unikać tej praktyki.

Jeśli absolutnie musisz #include source (i ogólnie należy tego unikać), użyj innego rozszerzenia pliku dla pliku.


9

Pomyślałem, że podzielę się sytuacją, w której mój zespół zdecydował się dołączyć pliki .c. Nasza architektura w dużej mierze składa się z modułów, które są odseparowane za pomocą systemu wiadomości. Te programy obsługi komunikatów są publiczne i wywołują wiele lokalnych statycznych funkcji roboczych, aby wykonać swoją pracę. Problem pojawił się podczas próby uzyskania pokrycia dla naszych przypadków testów jednostkowych, ponieważ jedynym sposobem wykonania tego prywatnego kodu implementacji było pośrednie użycie publicznego interfejsu wiadomości. Z niektórymi funkcjami pracowniczymi po kolana okazało się to koszmarem osiągnięcia odpowiedniego pokrycia.

Dołączenie plików .c umożliwiło nam dotarcie do trybu zębatego maszyny, którą interesowaliśmy podczas testowania.


6

Możesz użyć kompilatora gcc w systemie Linux, aby połączyć dwa pliki c w jednym wyjściu. Załóżmy, że masz dwa pliki c, jeden to „main.c”, a drugi to „support.c”. Więc polecenie połączenia tych dwóch to

gcc main.c support.c -o main.out

W ten sposób dwa pliki zostaną połączone z jednym wyjściem main.out Aby uruchomić wyjście, polecenie będzie

./main.out

Jeśli używasz funkcji w main.c, która jest zadeklarowana w pliku support.c, to powinieneś zadeklarować ją w main również używając zewnętrznej klasy pamięci.


5

Rozszerzenie pliku nie ma znaczenia dla większości kompilatorów C, więc będzie działać.

Jednak w zależności od ustawień makefile lub projektu dołączony plik c może wygenerować oddzielny plik obiektowy. W przypadku łączenia może to prowadzić do podwójnie zdefiniowanych symboli.


3

Możesz poprawnie dołączyć pliki .C lub .CPP do innych plików źródłowych. W zależności od twojego IDE, zwykle możesz zapobiec podwójnemu powiązaniu, patrząc na właściwości plików źródłowych, które chcesz dołączyć, zwykle klikając je prawym przyciskiem myszy i klikając właściwości, i odznacz / sprawdź kompiluj / link / wyklucz z kompilacji lub jakąkolwiek inną opcję może. Lub nie możesz dołączyć pliku do samego projektu, przez co IDE nawet nie będzie wiedział, że istnieje i nie będzie próbowało go skompilować. W przypadku plików makefile po prostu nie umieścisz w nim pliku do kompilacji i linkowania.

EDYCJA: Przepraszam, że to odpowiedź zamiast odpowiedzi na inne odpowiedzi :(


1

Język C nie zabrania tego rodzaju #include, ale wynikowa jednostka tłumaczeniowa nadal musi być poprawna C.

Nie wiem, jakiego programu używasz z plikiem .prj. Jeśli używasz czegoś takiego jak „make”, Visual Studio lub cokolwiek innego, po prostu upewnij się, że ustawiłeś listę plików do skompilowania bez tego, który nie może kompilować się niezależnie.


0

Włączanie pliku C do innego pliku jest legalne, ale nie jest zalecane, chyba że dokładnie wiesz, dlaczego to robisz i co próbujesz osiągnąć.
Jestem prawie pewien, że jeśli zamieścisz tutaj powód, dla którego za twoim pytaniem społeczność znajdzie inny, bardziej odpowiedni sposób osiągnięcia twojego celu (zwróć uwagę na "prawie", ponieważ jest możliweże jest to rozwiązanie biorąc pod uwagę kontekst ).

Nawiasem mówiąc, przegapiłem drugą część pytania. Jeśli plik C jest dołączony do innego pliku i w tym samym czasie dołączony do projektu, prawdopodobnie skończysz z problemem powielonego symbolu, dlaczego łączenie obiektów, tj. Ta sama funkcja zostanie zdefiniowana dwukrotnie (chyba że wszystkie są statyczne).


-6

powinieneś dodać taki nagłówek

#include <another.c>

uwaga: oba pliki należy umieścić w tym samym miejscu

Używam tego w codevison AVR dla mikrokontrolerów ATMEGA i działa naprawdę, ale nie działa w normalnym pliku języka C.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.