2015-10-18 18 views
19

Rozważ zastosowanie następującego programu. Czy spowoduje to błędy kompilacji?Jakie jest uzasadnienie wstępnych definicji w C?

#include <stdio.h> 
int s=5; 
int s; 
int main(void) 
{ 
    printf("%d",s); 
} 

Na pierwszy rzut oka wydaje się, że kompilator podaje błąd redefinicji, ale program jest całkowicie poprawny zgodnie ze standardem C. (Zobacz prezentację na żywo tutaj: http://ideone.com/Xyo5SY).

Wstępna definicja to dowolna zewnętrzna deklaracja danych, która nie ma specyfikatora klasy pamięci i żadnego inicjalizatora.

C99 6.9.2/2

Oświadczenie o stwierdziliśmy er dla obiektu, który ma fi l zakres bez inicjatora i bez magazynowania klasy, określonym ER lub z magazynowania klasy, określonym er statycznego stanowi wstępną definicję. Jeżeli jednostka tłumaczeniowa zawiera jedno lub więcej wstępnych definicji dla identyfikatora , a jednostka tłumaczeniowa nie zawiera żadnej zewnętrznej definicji dla tego identyfikatora, to zachowanie to jest dokładnie tak, jak jednostka tłumaczenia zawiera deklarację zakresu pliku tego identyfikatora, z typu kompozyt na koniec jednostki tłumaczeniowej, z inicjatora równa 0.

Moje pytanie brzmi, co jest uzasadnieniem dla pozwalając definicje wstępne? Czy jest jakieś wykorzystanie tego w C? Dlaczego C zezwala na wstępne definicje?

+2

nie sądzę są tam jakieś cenne powody, bo C++ sam nigdy nie miał, wśród wielu inne języki programowania. – edmz

Odpowiedz

6

Definicje wstępne zostały stworzone jako sposób na mostkowanie niekompatybilnych modeli, które istniały przed C89. Jest to ujęte w C99 rationale sekcji 6.9.2 zewnętrznych definicji obiektów, które mówi:

Przed C90 implementacje bardzo zróżnicowane w odniesieniu do przekazania odwołujące identyfikatory z łącznikiem wewnętrznym (patrz §6.2.2). Komitet C89 wynalazł koncepcję wstępnej definicji do obsługi tej sytuacji . Wstępna definicja jest deklaracją, która może, ale nie musi, być zgodna z definicją: Jeśli aktualna definicja znajduje się później w jednostce tłumaczeniowej , wówczas wstępna definicja działa jedynie jako deklaracja . Jeśli nie, to wstępna definicja działa jako rzeczywista definicja . Ze względu na spójność te same zasady obowiązują dla identyfikatorów z łączem zewnętrznym, mimo że nie są one ściśle wymagane: .

i sekcja 6.2.2 z uzasadnieniem C99 mówi:

Model definicji mają być stosowane do obiektów z zewnętrznym łącznikiem był poważnym problemem standaryzacja C89. Podstawowym problemem było ustalenie, które deklaracje obiektu definiują pamięć dla obiektu, oraz , które jedynie odwołują się do istniejącego obiektu. Powiązany problem to , czy dozwolone są wielokrotne definicje pamięci, czy tylko jeden jest akceptowalny. Pre-C89 implementacje wykazują co najmniej cztery różne modele, wymienione poniżej w kolejności rosnącej restrykcyjności:

+0

Jak dobrze znasz i rozumiesz standard C & C++? Jesteś naprawdę geniuszem. – Destructor

+0

@PravasiMeet Znam go dobrze, ponieważ spędzam dużo czasu czytając standard i związane z nim dokumenty oraz pytania SO. Chodzi tylko o praktykę i doświadczenie. Im więcej masz doświadczenia, tym więcej ciekawych problemów rozwiążesz i zaczniesz je budować. –

4

Oto przykład przypadku, gdy jest to przydatne:

void (*a)(); 

void bar(); 
void foo() 
{ 
    a = bar; 
} 

static void (*a)() = foo; 

/* ... code that uses a ... */ 

Kluczową kwestią jest to, że definicja foo musi odnosić się do a i definicja a musi odnosić się do foo. Podobne przykłady z inicjalizowanymi strukturami również powinny być możliwe.

+2

W tym konkretnym przypadku można uniknąć konieczności wstępnej deklaracji, dodając "extern" do pierwszej linii (co jest tylko deklaracją). Gdzie naprawdę potrzebujesz wstępnej deklaracji, jeśli chcesz, aby 'a' było' statyczne' (zakres pliku) –

+0

@ChrisDodd: Tak, przegapiłem to. Zmienię to. Dzięki. –

+0

Nie, jest to wskaźnik funkcji. –

Powiązane problemy