Wstępne definicje w C99 i powiązanie

Rozważmy program C składający się z dwóch plików,

F1.c:

int x;

F2.c:

int x=2;

Moje czytanie paragrafu 6.9.2 standardu C99 jest takie, że ten program powinien zostać odrzucony. W mojej interpretacji 6.9.2 zmienna x jest wstępnie zdefiniowana w f1.c, ale ta wstępna definicja staje się rzeczywistą definicją na końcu jednostki translacyjnej i (moim zdaniem) powinna zachowywać się tak, jakby f1.c zawierała definicję int x=0;.

Ze wszystkimi kompilatorami (i, co ważne, linkerami) udało mi się spróbować, tak się nie dzieje. Wszystkie platformy kompilacyjne próbowałem połączyć powyższe dwa pliki, a wartość x jest 2 w obu plikach.

Wątpię, aby stało się to przypadkiem, lub po prostu jako "łatwa" funkcja do zapewnienia oprócz tego, czego wymaga standard. Jeśli się nad tym zastanowić, oznacza to, że w linkerze jest specjalne Wsparcie dla tych globalnych zmiennych, które nie mają inicjalizatora, w przeciwieństwie do tych jawnie zainicjowany na zero. Ktoś mi powiedział, że funkcja linkera może być konieczna do kompilacji Fortrana. To byłoby rozsądne wyjaśnienie.

Jakieś przemyślenia na ten temat? Inne interpretacje standardu? Nazwy platform, na których plikach f1.c i f2.c nie można połączyć?

Uwaga: jest to ważne, ponieważ pytanie występuje w kontekście analizy statycznej. Jeśli oba pliki mogą odmówić połączenia na jakiejś platformie, analizator powinien narzekać, ale jeśli każda platforma kompilacyjna go akceptuje, to nie ma powodu, aby o tym ostrzegać.

Author: ire_and_curses, 2009-09-29

3 answers

Zobacz także Jakie są zmienne zewnętrzne w C . Jest to wymienione w normie C w załączniku j jako wspólne rozszerzenie:

J. 5. 11 Multiple external definitions

Może istnieć więcej niż jedna zewnętrzna definicja identyfikatora obiektu, z lub bez wyraźnego użycia słowa kluczowego extern; jeśli definicje się nie zgadzają lub jest inicjalizowana więcej niż jedna, zachowanie jest niezdefiniowane (6.9.2).

Ostrzeżenie

Jako punkty @ litb tutaj i jak stwierdzono w mojej odpowiedzi na powiązane pytanie, używanie wielu definicji zmiennej globalnej prowadzi do niezdefiniowanego zachowania, co jest standardowym sposobem powiedzenia "wszystko może się zdarzyć". Jedną z rzeczy, które mogą się zdarzyć, jest to, że program zachowuje się tak, jak oczekujesz; a J. 5.11 mówi w przybliżeniu: "możesz mieć szczęście częściej niż na to zasługujesz". Ale program, który opiera się na wielu definicjach zmiennej extern - ze słowem kluczowym "extern" lub bez niego-jest nie jest to ściśle zgodny program i nie ma gwarancji, że będzie działać wszędzie. Równoważnie: zawiera błąd , który może się pojawić lub nie.

 27
Author: Jonathan Leffler,
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:30:04

Istnieje coś, co nazywa się "common extension" do standardu, gdzie definiowanie zmiennych wiele razy jest dozwolone, o ile zmienna jest inicjalizowana tylko raz. Zobacz http://c-faq.com/decl/decldef.html

Na podlinkowanej stronie jest napisane, że dotyczy to platform uniksowych-myślę, że jest to to samo dla c99 co c89-choć może zostało przyjęte przez więcej kompilatorów do utworzenia pewnego rodzaju standardu defacto. Interesujące.

 9
Author: olovb,
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
2009-09-29 05:42:55

To jest wyjaśnienie mojej odpowiedzi na komentarz olovb:

Wyjście nm dla pliku obiektowego skompilowanego z "int x;". Na tej platformie symbole są poprzedzone znakiem'_', czyli zmienna x pojawia się jako _x.

00000000 T _main
         U _unknown
00000004 C _x
         U dyld_stub_binding_helper

Wyjście nm dla pliku obiektowego skompilowanego z "int x=1;"

00000000 T _main
         U _unknown
000000a0 D _x
         U dyld_stub_binding_helper

Wyjście nm dla pliku obiektowego skompilowanego z "int x=0;"

00000000 T _main
         U _unknown
000000a0 D _x
         U dyld_stub_binding_helper

Wyjście nm dla pliku obiektowego skompilowanego z "extern int x;"

00000000 T _main
         U _unknown
         U dyld_stub_binding_helper

EDIT: wyjście nm dla obiektu plik skompilowany z "extern int X;" gdzie x jest faktycznie używany w jednej z funkcji

00000000 T _main
         U _unknown
         U _x
         U dyld_stub_binding_helper
 7
Author: Pascal Cuoq,
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
2009-09-29 07:31:52