2010-04-13 3 views
30

Pracuję w C od tak dawna, że ​​fakt, że kompilatory zazwyczaj dodają podkreślenie na początku extern jest po prostu zrozumiałe ... Jednak, another SO question today zastanawiałem się nad prawdziwym powodem dodania podkreślenia. A twierdzi, że powodem jest:Dlaczego kompilatory języka C wstawiają podkreślenia do nazw zewnętrznych?

To było powszechną praktyką kompilatory C do poprzedzić wiodącego podkreślenia do wszystkich zewnętrznych identyfikatorów programu zakres w celu uniknięcia kolizji z wkładów wsparcia językowego Runtime

Myślę, że przynajmniej jądro prawdy do tego, ale także nie wydaje się tak naprawdę odpowiedzieć na to pytanie, ponieważ jeśli podkreślenie zostanie dodane do wszystkich elementów zewnętrznych, nie pomoże to w zapobieganiu konfliktom.

Czy ktoś ma dobre informacje na temat racji wiodącego podkreślenia?

Czy dodana część podkreślenia jest powodem, dla którego wywołanie systemowe systemu Unix creat() nie kończy się na "e"? Słyszałem, że wczesne łączniki na niektórych platformach miały limit 6 znaków dla nazw. W takim przypadku dodanie podkreślenia do nazw zewnętrznych wydaje się być szalonym pomysłem (teraz mam tylko 5 postaci do grania ...).

+2

Należy zauważyć, że takie zachowanie nie jest praktykowane na nowoczesnych systemach ELF. Było to powszechne w platformach aout/coff, najwyraźniej. –

+0

Dlaczego Clang robi to na OS X? jak mogę to wyłączyć? – MarcusJ

Odpowiedz

17

To było powszechną praktyką kompilatory C do prepend wiodącego podkreślenia do wszystkich zewnętrznych identyfikatorów programu zakres aby uniknąć starcia z wkładów wsparcia językowego Runtime

Jeżeli wsparcie Runtime jest przez kompilator, ty pomyślałbym, że bardziej sensownym byłoby, aby zamiast tego dodać podkreślenie do kilku zewnętrznych identyfikatorów w środowisku wykonawczym!

Kiedy pojawiły się kompilatory C, podstawową alternatywą dla programowania w C na tych platformach było programowanie w języku asemblera, a było (i czasami nadal jest) przydatne łączenie plików obiektów zapisanych w asembler i C. Tak naprawdę (IMHO) wiodącym podkreśleniem dodanym do zewnętrznych identyfikatorów C było uniknięcie konfliktów z identyfikatorami w swoim własnym kodzie montażu.

(Patrz również GCC's asm label extension; i zauważ, że ten dołączany na początku podkreślenia można uznać prosta forma nazwy maglowania bardziej skomplikowanych języków takich jak C++ używać nazwy bardziej skomplikowany maglowania, ale to gdzie to się zaczęło.).

+0

Podoba mi się sarkastyczny "GCC nie ma jeszcze możliwości przechowywania zmiennych statycznych w rejestrach. Być może zostanie to dodane." komentarz w połączonym dokumencie. –

+0

@ Michael Hellur: to prawdopodobnie nie jest sarkastyczny. W niektórych systemach można zarezerwować rejestr globalny jako wskaźnik do pewnego obszaru pamięci (np. 'R9' w niektórych odmianach ARM EABI dla statycznego wskaźnika podstawowego). –

3

Z tego, co zawsze słyszę, należy unikać konfliktów nazw. Nie dla innych zewnętrznych zmiennych, ale bardziej, że przy korzystaniu z biblioteki nie będzie, rzecz jasna, konfliktu z nazwami zmiennych kodu użytkownika.

1

Od Wikipedia:

To było powszechną praktyką kompilatory C do poprzedzić wiodącym podkreślenia do wszystkich zewnętrznych identyfikatorów programu zakres aby uniknąć starcia z wkładów wsparcia językowego wykonawcze. Ponadto, gdy kompilator C/C++ był potrzebny do wprowadzenia nazw do zewnętrznego powiązania w ramach procesu tłumaczenia, nazwy te były często rozróżniane za pomocą kombinacji wielu poprzedzających lub końcowych podkreśleń.
Ta praktyka została później skodyfikowana jako część standardów językowych C i C++, w których użycie wiodących podkreśleń zostało zarezerwowane dla implementacji.
3

Główna funkcja nie jest rzeczywistym punktem wejścia do pliku wykonywalnego. Niektóre statycznie połączone pliki mają prawdziwy punkt wejścia, który ostatecznie wywołuje funkcję main, a statycznie połączone pliki posiadają przestrzeń nazw, która nie zaczyna się od podkreślenia. W moim systemie w/usr/lib istnieją między innymi gcrt1.o, crt1.o i dylib1.o. Każda z nich ma funkcję "startową" bez podkreślenia, która ostatecznie wywoła "główny punkt wejścia". Wszystko inne oprócz tych plików ma zasięg zewnętrzny. Historia ma związek z mieszaniem asemblera i C w projekcie, gdzie wszystkie C były uważane za zewnętrzne.

5

jeśli kompilator c zawsze poprzedzał znak podkreślenia przed każdym symbolem, , wówczas kod uruchomienia/c-runtime (który jest zwykle zapisywany w zespole) może bezpiecznie używać etykiet i symboli, które nie zaczynają się od znaku podkreślenia (takiego jak symbol " początek').

nawet jeśli napiszesz funkcję start() w kodzie c, zostanie ona wygenerowana jako _start w wyjściu obiekt/asm. (zauważ, że w tym przypadku nie ma możliwości, aby kod c wygenerował symbol, który nie zaczyna się od podkreślenia), więc koder startowy nie musi się martwić o wynalezienie niejasnych nieprawdopodobnych symboli (takich jak $ _dontuse42% $) dla każdego z nich. jego/globalne zmienne/etykiety.

, więc linker nie będzie narzekać na konflikt nazwy, a programista jest zadowolony. :)

Poniższa lista różni się od praktyki kompilatora poprzedzającego podkreślenie w formatach wyjściowych.

Praktyka ta została później skodyfikowane w ramach norm językowych ++ C i C, w których korzystanie z wiodących podkreślenia zostało zarezerwowanych na realizację.

Konwencja, która obowiązuje, w przypadku bibliotek systemu i innych komponentów systemu. (i dla rzeczy takich jak __FILE__ itp.).

(należy pamiętać, że taki symbol (np _time) może skutkować 2 wiodących podkreślenia (__time) w generowanym wyjściu)