Organizacja plików C

Jestem przyzwyczajony do robienia całego kodu w jednym pliku C. Pracuję jednak nad projektem na tyle dużym, że staje się to niepraktyczne. Byłem #wliczając je razem, ale natknąłem się na przypadki, gdzie jestem #wliczając niektóre pliki wiele razy, itp. Słyszałem o tym .pliki h, ale nie jestem pewien, jaka jest ich funkcja (lub dlaczego posiadanie 2 plików jest lepsze niż 1).

Jakich strategii używać do organizowania kodu? Czy można oddzielić funkcje "publiczne" od "prywatnych" dla konkretne akta?

To pytanie przyspieszyło moje zapytanie. Herbata.plik h nie odnosi się do herbaty.plik C. Czy kompilator" wie", że każdy .plik h ma odpowiedni .plik c?

Author: Community, 2008-09-07

8 answers

Powinieneś to wziąć pod uwagę .pliki h jako pliki interfejsu twojego .plik C. Każdy .plik c reprezentuje moduł o określonej funkcjonalności. If funkcjonuje w a .pliki c są używane przez inne moduły (np. inne .pliki c) umieścić prototyp funkcji w .plik interfejsu H. Poprzez włączenie pliku interfejsu do oryginalnych modułów .plik c i każdy inny .plik c, w którym potrzebujesz funkcji, udostępniasz tę funkcję innym modułom.

Jeśli potrzebujesz tylko funkcji w jasne .plik c (nie w żadnym innym module), deklaruje jego zakres statyczny. Oznacza to, że może być wywołany tylko z pliku c, w którym jest zdefiniowany.

To samo dotyczy zmiennych, które są używane w wielu modułach. Powinny one wejść do pliku nagłówkowego i tam muszą być oznaczone słowem kluczowym 'extern'. Uwaga: dla funkcji słowo kluczowe 'extern' jest opcjonalne. Funkcje są zawsze uważane za "zewnętrzne".

Osłony włączenia w plikach nagłówkowych pomagają nie dołączać tego samego pliku nagłówkowego wiele razy.

Na przykład:

Module1.c:

    #include "Module1.h"

    static void MyLocalFunction(void);
    static unsigned int MyLocalVariable;    
    unsigned int MyExternVariable;

    void MyExternFunction(void)
    {
        MyLocalVariable = 1u;       

        /* Do something */

        MyLocalFunction();
    }

    static void MyLocalFunction(void)
    {
      /* Do something */

      MyExternVariable = 2u;
    }

Module1.h:

    #ifndef __MODULE1.H
    #define __MODULE1.H

    extern unsigned int MyExternVariable;

    void MyExternFunction(void);      

    #endif

Module2.c

    #include "Module.1.h"

    static void MyLocalFunction(void);

    static void MyLocalFunction(void)
    {
      MyExternVariable = 1u;
      MyExternFunction();
    }
 35
Author: cschol,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-02 20:16:17

Spróbuj zrobić każdy .c skoncentruj się na określonym obszarze funkcjonalności. Użyj odpowiedniego .plik h do deklarowania tych funkcji.

Każdy .plik h powinien mieć osłonę "nagłówka" wokół jego zawartości. Na przykład:

#ifndef ACCOUNTS_H
#define ACCOUNTS_H
....
#endif

W ten sposób możesz dołączyć " konta.h " tyle razy, ile chcesz, a po raz pierwszy widziany w konkretnej jednostce kompilacji będzie jedynym, który rzeczywiście przyciąga jego zawartość.

 10
Author: Andrew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-06 23:02:43

Kompilator

Możesz zobaczyć przykład C 'modułu' w ten temat - zauważ, że są dwa pliki-nagłówek Tea.h, i herbata kodowa.C. deklarujesz wszystkie publiczne definicje, zmienne i prototypy funkcji, do których inne programy mają mieć dostęp w nagłówku. W głównym projekcie będziesz # include i że kod może teraz uzyskać dostęp do funkcji i zmiennych modułu tea, które są wymienione w nagłówku.

Potem robi się trochę bardziej skomplikowane. Jeśli korzystasz z Visual Studio i wielu innych Idów, które zarządzają Twoim kompilacją, a następnie ignorujesz tę część - zajmują się kompilowaniem i łączeniem obiektów.

Linker

Kiedy kompilujesz dwa oddzielne pliki C, kompilator tworzy pojedyncze pliki obiektowe - so main.c staje się głównym.o, i herbaty.c staje się herbatą.o. zadaniem linkera jest przeglądanie wszystkich plików obiektowych (Twojego głównego.o i herbata.o), i dopasować referencje - więc gdy wywołujesz funkcję tea W main, linker modyfikuje to wywołanie więc to faktycznie wywołuje właściwą funkcję w herbacie. Linker tworzy plik wykonywalny.

Istnieje świetny samouczek , który zagłębia się w ten temat, w tym zakres i inne kwestie, które napotkasz.

Powodzenia!

- Adam

 7
Author: Adam Davis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:29:36

Kilka prostych zasad na początek:

  1. Umieść te deklaracje, które chcesz upublicznić w pliku nagłówkowym dla pliku implementacji C, który tworzysz.
  2. tylko #zawiera pliki nagłówkowe w pliku C, które są potrzebne do implementacji pliku C.
  3. Dołączanie plików nagłówkowych do pliku nagłówkowego tylko wtedy, gdy jest to wymagane dla deklaracji w tym pliku nagłówkowym.

  4. użyj metody include guard opisanej przez Andrzeja lub użyj #pragma raz jeśli kompilator obsługuje go (co robi to samo-czasami wydajniej)
 7
Author: denis phillips,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-06 23:34:42

Aby odpowiedzieć na dodatkowe pytanie:

To pytanie przyspieszyło moje zapytanie. Na herbata.plik h nie odwołuje się do herbata.plik C. Czy kompilator " wie" to wszystko .plik h ma odpowiedni .plik c?

Kompilator nie zajmuje się przede wszystkim plikami nagłówkowymi. Każde wywołanie kompilatora kompiluje źródło (.c) plik do obiektu (.o) plik. Za kulisami (np. w pliku make lub pliku projektu) wiersz poleceń równoważny to jest generowane:

compiler --options tea.c

Plik źródłowy #includeto wszystkie pliki nagłówkowe zasobów, do których się odwołuje, czyli w jaki sposób kompilator znajduje pliki nagłówkowe.

(omawiam tu kilka szczegółów. Jest wiele do nauczenia się o projektach budowlanych C.)

 3
Author: smh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:48

Jak również odpowiedzi dostarczone powyżej, małą zaletą dzielenia kodu na moduły (osobne pliki) Jest to, że jeśli musisz mieć jakieś zmienne globalne, możesz ograniczyć ich zakres do pojedynczego modułu za pomocą słowa kluczowego 'static'. (Można to również zastosować do funkcji). Zauważ, że to użycie 'static' różni się od jego użycia wewnątrz funkcji.

 3
Author: David L Morris,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-07 02:20:47

Twoje pytanie wyjaśnia, że tak naprawdę nie zrobiłeś wiele poważnego rozwoju. Zwykle jest tak, że Twój kod będzie na ogół zbyt duży, aby zmieścić się w jednym pliku. Dobrą zasadą jest to, że należy podzielić funkcjonalność na jednostki logiczne (.pliki c) i każdy plik powinien zawierać nie więcej niż to, co można łatwo trzymać w głowie na raz.

Dany produkt oprogramowania zazwyczaj zawiera wyjście z wielu różnych .pliki C. Jak to zwykle robi się jest to, że kompilator produkuje wiele plików obiektowych (w systemach unix ".o " pliki, VC generuje .pliki obj). Celem "linkera" jest skomponowanie tych plików obiektowych do wyjścia (biblioteki współdzielonej lub wykonywalnej).

Ogólnie twoja implementacja (.c) pliki zawierają rzeczywisty kod wykonywalny, natomiast pliki nagłówkowe (.h) posiadają deklaracje funkcji publicznych w tych aktach wykonawczych. Można dość łatwo mieć więcej plików nagłówkowych niż plików implementacyjnych, a czasami pliki nagłówkowe mogą również zawierać kod wbudowany.

Jest to na ogół dość nietypowe, aby pliki implementacyjne zawierały się nawzajem. Dobrą praktyką jest upewnienie się, że każdy plik wdrażania oddziela swoje obawy od innych plików.

Polecam ściągnąć i zajrzeć do źródła jądra Linuksa. Jest dość masywny jak na program w języku C, ale dobrze zorganizowany w osobne obszary funkcjonalności.

 1
Author: 1800 INFORMATION,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-06 23:25:14

The .pliki h powinny być używane do definiowania prototypów dla funkcji. Jest to konieczne, aby można było dołączyć prototypy, które są potrzebne w pliku C bez deklarowania wszystkich funkcji, które są potrzebne w jednym pliku.

Na przykład, gdy #include <stdio.h>, to zapewnia prototypy dla printf i innych funkcji IO. Symbole tych funkcji są domyślnie ładowane przez kompilator. Możesz spojrzeć na system .h files under / usr / include if you ' re interested in the normalne idiomy związane z tymi plikami.

Jeśli piszesz tylko trywialne aplikacje z niewielką ilością funkcji, nie jest tak naprawdę konieczne modularyzowanie wszystkiego na logiczne grupy procedur. Jeśli jednak masz potrzebę opracowania dużego systemu, musisz wziąć pod uwagę, gdzie zdefiniować każdą z twoich funkcji.

 0
Author: hoyhoy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-07 00:56:59