2013-06-13 22 views
18

Ten kod kompiluje grzywny:Dlaczego inicjowanie zmiennej zewnętrznej lokalnie w funkcji powoduje błąd?

extern int i = 10; 

void test() 
{ 
    std::cout << "Hi" << i << std::endl; 
} 

Chociaż ten daje błąd:

void test() 
{ 
    extern int i = 10; 
    std::cout << "Hi" << i << std::endl; 
} 

error: 'i' has both 'extern' and initializer

czytałem w C++ Primer

Any declaration that includes an explicit initializer is a definition. We can provide an initializer on a variable defined as extern, but doing so overrides the extern. An extern that has an initializer is a definition. It is an error to provide an initializer on an extern inside a function.

ktoś może stanowić wyjaśnienie, dlaczego ta powinna być błędem, jeśli jest wykonywane lokalnie w funkcji, podczas gdy to samo dotyczy leciał na globalnym zasięgu?

+1

ponieważ standard twierdzi, że jest nieprawidłowy? –

+0

Zaskakuje mnie, że pierwszy kompiluje ... czy możesz podać więcej tła? Używam G ++ v4.2.1 – ChrisCM

+1

@JonatanGoebel Kiedy standard zdefiniowałby taką rzecz, mieliby (głównie) racjonalne uzasadnienie. Chciałem poznać możliwy powód takiego ograniczenia. [Coś na linii, jak zachowanie czegoś niezdefiniowanego pomaga kompilatorowi]. –

Odpowiedz

14

Powodem definiowania zmiennej zewnętrznej wewnątrz funkcji nie ma sensu jest następujący:

Kiedy deklarujesz symbolem extern, mówisz kompilatora, aby połączyć wszystkie te wystąpienia tej wartości w tym samym symbolem. Wszelkie wystąpienia extern int i; w twoim programie łączyłoby się z zewnętrznie zdefiniowanym i. Spójrz na ten przykład:

#include <iostream> 
using namespace std; 

extern int i; 
int i = 10; 
void test() 
{ 
    std::cout << "Hi" << i << std::endl; 
} 

int main() 
{ 
    extern int i; 
    i++; 
    test(); 
} 

Ten przykład powinien wypisać hi11. HOwever, jeśli usuniemy zewnętrzny plik wewnątrz głównego, wyprowadzi on 10. To dlatego, że bez zewnętrznego, nie łączę się z globalnym i, ale tworzę jego własną lokalną kopię i.

Powód, dla którego zdefiniowanie extern i wewnątrz funkcji nie ma sensu, jest tym, o ile pozwolimy jakiejkolwiek funkcji "zdefiniować". Która funkcja działa pierwsza? Kiedy zostanie zdefiniowany?

Załóżmy następujący przykład, aby był ważny, jaki byłby wynik ???

#include <iostream> 
using namespace std; 

extern int i; 
int i = 10; 
void test() 
{ 
    std::cout << "Hi" << i << std::endl; 
} 

void test2() { 
    extern int i = 1000; 
    std::cout<< "HI" << i << std::endl; 
} 

void test3() { 
    extern int i; 
    i = 1000; 
    std::cout<< "HI" << i << std::endl; 
} 

int main() 
{ 
    extern int i; 
    i++; 
    test(); 
    i = 0; 
    test2(); 
} 

Czy wyjście test2 powinno wynosić 0, czy 1000? Spójrz też na mój test3, tutaj zwięźle mówimy, połącz moje i do zewnętrznego zdefiniowanego i, i przypisz mu wartość jako 1000. To bardzo różni się od próby "zainicjowania" wartości.

W skrócie, zmienne zewnętrzne mają sens tylko jako globale i powinny być zdefiniowane w zasięgu globalnym. W twoich przykładach pierwsza wersja też mnie nie kompiluje. Uważam to za interesujące. Warto przyjrzeć się dokumentom standardowym, aby zobaczyć, czy jest to zwięźle określone, lub czy Twój kompilator może obsługiwać to w sposób zaprojektowany w celu dodania dodatkowej ochrony ...

+1

W swoim pierwszym programie daje mi Hi11 nawet po usunięciu extern z funkcji – cbinder

6

Po dodaniu inicjatora do deklaracji staje się on definicja zmiennej globalnej. Jest to odpowiednik tej samej definicji bez numeru extern, co oznacza Twoja książka, gdy mówi, że "zastępuje wersję zewnętrzną".

Podczas gdy zmienne globalne można zadeklarować (używając extern) wewnątrz funkcji, nie można ich tam zdefiniować, tylko w zakresie przestrzeni nazw. Właśnie dlatego drugi fragment jest błędem.

Jeśli chcesz wiedzieć, dlaczego projektanci C (skąd te zasady przyszły do ​​C++) zdecydowali się na zezwalanie na deklaracje, ale nie na definicje, obawiam się, że nie znam historii języka na tyle szczegółowo, aby odpowiedzieć.

4

Po pierwsze, należy zapoznać się z koncepcją powiązania i rozumieniu extern powiązania Państwo:

A name is said to have linkage when it might denote the same object, reference, function, type, template, namespace or value as a name introduced by a declaration in another scope:

When a name has external linkage, the entity it denotes can be referred to by names from scopes of other translation units or from other scopes of the same translation unit.
--3.5.6.2 n3242

Funkcja static który różni się od extern, extern jest tylko prośba, static jest polecenie.

The name of a function declared in block scope and the name of a variable declared by a block scope extern declaration have linkage.

  • If there is a visible declaration of an entity with linkage having the same name and type, ignoring entities declared outside the innermost enclosing namespace scope, the block scope declaration declares that same entity and receives the linkage of the previous declaration.
  • If there is more than one such matching entity, the program is ill-formed.
  • Otherwise, if no matching entity is found, the block scope entity receives external linkage.

--3.5.6.6 n3242

Dlatego w ramach bloku poniżej procedura jest zalecane do zrobienia:

 extern int i;//declare it,request the linkage according to 3.5.6.6 above 
    i = 10;//modify it when has link to a defination 

dla globalnej extern oświadczenia ewentualnie przekształcić postać

 extern int i =10; 

do

 extern int i;//include in .hpp is recommended 
    int i =10;//global or namespace variable defination 
0

The sim sposób postępowania:

Celem słowa kluczowego extern jest zadeklarowanie obiektu bez jego definiowania. Definiując go, zasadniczo mówisz kompilatorowi "Nie przypisuj wartości, ale przypisz wartość". To nie ma sensu - nigdy nie powinno się tego robić, wewnątrz funkcji lub. Większość kompilatorów ostrzega i mimo wszystko kontynuuje, albo nie będzie kompilatora i będzie podawać błąd.

Choć to poza zakres tej kwestii wyjaśnić szczegółowo coextern robi, może się okazać, że warto przeczytać odpowiedzi na this question.

0

extern zmienne są inicjowane zanim funkcja działa: en.cppreference.com/w/cpp/language/initialization#Non-local_variables

Jeżeli został on uznany static zamiast extern w bloku funkcyjnego, to jeszcze statyczny czas przechowywania, ale jego podnośnik byłoby lokalna dla tej funkcji kontra zewnętrzna. Więc to być inicjowane, gdy wykonanie najpierw przechodzi przez tę linię w funkcję: en.cppreference.com/w/cpp/language/storage_duration#Static_local_variables

tak to OK, aby zainicjować static zmiennych w bloku funkcję, ale nie OK, aby zainicjować extern zmiennych tam.