Newsgroup: it.comp.lang.c
Titolo: La keyword extern serve ancora?

Perchè anche se non uso la keyword extern nello header, ma metto,
magari solo:
int i;
tutto funziona?
Perchè non ottengo un errore per definizione multipla?
A questo punto mi sembra che la keyword extern sia superflua visto che
funziona uguale, no?
Come mai funziona anche senza la keyword, è una implementazione
recente per consentire maggiore flessibilità?
Grazie a tutti.



I compilatori ed i linker più recenti sono abbastanza intelligenti per 
riconoscere che una variabile dello stesso tipo e dello stesso nome 
anche se definita più volte indica la stessa locazione di memoria. 
Tuttavia è una prassi abominevole mettere delle definizioni di 
variabili in un header file.  Alcuni compilatori/linker potrebbero 
dare risultati sbagliati o, in tutti penso, vi sarebbe un inutile 
spreco di memoria, se l'header viene usato in un programma che poi non 
faccia uso della variabile definita.
È prassi, in certi progetti, usare negli headers
EXTERN int i;
e far precedere l'inclusione dell'header da
#define EXTERN extern
escluso un solo file di programma (generalmente dove è definito il 
main()) dove la define è
#define EXTERN
In questo modo la variabile è definta una sola volta.
Ciao
Giovanni
-- 
   A computer is like an air conditioner,
   it stops working when you open Windows.
   < 
http://giovanni.homelinux.net/
 >

 recente per consentire maggiore flessibilità?
Ti consiglio di seguire lo Standard C e non l'implementazione.
Lo Standard dice che usare la _dichiarazione_ "extern int i;" serve a
indicare che la variabile "i" è stata o sarà _definita_ in questo o in
un altro sorgente/header (meglio inserire definizioni in un sorgente).
L'inizializzazione trasforma una semplice dichiarazione in una definizione:
a questo punto all'oggetto verrà riservato dello spazio in memoria.
Lo specificatore "extern" sarà ignorato nel caso compaia in una dichia-
razione con inizializzatore:
int i; /* dichiarazione */
int i = 0; /* definizione */
extern int i; /* dichiarazione */
extern int i = 0; /* definizione ("extern" viene ignorato) */
Questo è tutto quello che devi sapere nel tuo caso.
Usa lo specificatore "extern" nella accezione specificata dallo Standard.
In particolare, come consiglia l'Harbison & Steele:
per garantire la compatibilità con la maggior parte dei compilatori:
1. Disporre un'unica definizione in un unico sorgente per ogni variabile
    esterna. Omettere lo specificatore "extern" e dotare la definizione
    di inizializzatore:
    int i = 0;
2. In ogni sorgente o header che utilizzerà una variabile esterna
    definita altrove, usare lo specificatore "extern" senza includere
    alcun inizializzatore:
    extern int i;
L'uso di "extern" è quindi raccomandato e ha un significato preciso.
E' tra l'altro consigliato anche l'uso di tools come "lint" per il
controllo di dichiarazioni consistenti all'interno di più sorgenti.
-- 
Non puoi insegnare qualcosa ad un uomo. You cannot teach a man anything.
Lo puoi solo aiutare   --  Galileo  Galilei  --   You can only help him
a scoprirla dentro di sé.                        discover it in himself.
Vincenzo Mercuri

 extern int i = 0; /* definizione ("extern" viene ignorato) */
Questa dichiarazione dovrebbe dare errore.



Vincenzo, che è un frequentatore di comp.lang.c, ha ovviamente
ragione.
Nella terminologia dello standard, "int i" è una "tentative
definition", che può o meno diventare una definition dopo la
traduzione della translation unit.

 traduzione della translation unit.
Esatto.


 traduzione della translation unit.
E cosa ho scritto io?


 traduzione della translation unit.
È quello che ho scritto.
La singola unità di compilazione la tratta come definition.  È il 
linker che deve accorgersi che si tratta dello stesso identificatore.


 E cosa ho scritto io?
Hai scritto:
     "int i;" è una definition e tutti i compilatori la trattano come
     tale allocando in memoria spazio per la variabile.
     Se poi alcuni compilatori la trattano come declaration quando
     coincidono sia il nome che il tipo delle definizioni multiple è
     una scelta implementativa.
Non si tratta di una scelta implementativa e non è corretto dire
che "int i;" è una "definition". Che dia luogo a una definizione
è dovuto al fatto che sia l'unica dichiarazione con linkage esterno
per quella variabile. :-)
Quindi nel caso della sola dichiarazione "int i;", questa è a tutti
gli effetti equivalente alla definizione "int i = 0;". In questo hai
ragione, ed è quanto dettato dallo Standard.


 ragione, ed è quanto dettato dallo Standard.
ROTFL   Se non è zuppa pan bagnato!


 ROTFL Se non è zuppa pan bagnato!
[...]
Eh, se intendi che abbiamo detto la stessa cosa in modi diversi ti
sbagli..bisogna sempre considerare il caso generale e soprattutto non
bisogna specializzarsi al proprio compilatore, ma allo Standard.


 linker che deve accorgersi che si tratta dello stesso identificatore.
non mi pare che sia esattamente la stessa cosa. Ad es se un'unità di
traduzione contiene (a file scope)
int x;
/* ... */
 int x = 3;
la prima dichiarazione di x è una tentative definition che NON si
trasforma in definition, in quanto la seconda dichiarazione è una
definition. Infatti x viene inizializzato a 3, mentre le tentative
definition che diventano definition generano una variabile
inizializzata a 0.

 Questa dichiarazione dovrebbe dare errore.
a quale ti riferisci? mi sembrano tutte e 4 corrette.
In particolare, se è quella a cui ti riferivi,
extern int i = 0;
è corretta se appare a file scope (e lo sarebbe anche per un diverso
valore dell'inizializzatore naturalmente). Il motivo per cui un noto
compilatore open source produce un warning è che in numerose
implementazioni Unix pre (o semplicemente non) standard, ciò era un
errore. Tali implementazioni utilizzavano un modello di linkage spesso
chiamato "Relaxed Ref/Def".
Lo standard C ha adottato un modello diverso, nel quale una
dichiarazione a file scope con extern e un inizializzatore è una
corretta definizione per l'identificatore.

 Standard.
Intendevo proprio dire che diciamo la stessa cosa.
'tentative definition' è una locuzione introdotta dallo standard che 
secondo me è solo un modo fumoso di dire quello che ho detto io e 
serve solo per giustificare quello che, nella mia esperienza, era un 
errore molto comune.
La definizione "int var;" alloca spazio in memoria per l'oggetto a 
livello di modulo compilabile.  Se questa allocazione è identica per 
più moduli il compilatore (in fase di link) decide che è una sola.
La 'tentative definition' "int var;" alloca spazio in memoria per 
l'oggetto a livello di modulo compilabile.  Se esistono più 'tentative 
definitions' per lo stesso oggetto ne risulta una sola definizione.
Forse è perché io vedo le cose dal punto di vista pratico e lo 
standard dà definizioni astratte, ma quale differenza c'è fra queste 
due frasi?
Forse è perché la 'tentative definition' non era nel linguaggio 
originale e questo me la rende ostica.  Francamente preferisco il 
vecchio comportamento che dava un bell'errore (o almeno un warning) di 
definizioni multiple.  In grossi progetti dove lavoravano qualche 
decina di persone è stato, nella mia esperienza, un vantaggio,


 corretta definizione per l'identificatore.
Non solo il gcc dà errore, ma tutti i compilatori pre standard C99 
davano errore.


 davano errore.
credo proprio di no, le regole sul linkage sono le stesse sia in C89
che in C99.
il gcc, se eseguito in modalità conforme (es. gcc -pedantic -std=c89)
non da errore (non può dare errore, altrimenti non sarebbe conforme)
ma da un warning e compila correttamente.
Se nel 198x hanno deciso di NON adottare il modello di linkage
prevalente su Unix è perchè c'erano altri sistemi che ne usavano
altri. In particolare, il compilatore C originale usava il modello
"common".

 davano errore.
ah, dimenticavo di aggiungere che molti compilatori, non danno neppure
un warning con le opzioni di warning di default.
In questo momento non posso controllare, ma mi pare che si comprtino
così vbcc, Digital mars compiler e il SAS C (per Amiga).

 di persone è stato, nella mia esperienza, un vantaggio,
De gustibus non disputandum est.


 decina di persone è stato, nella mia esperienza, un vantaggio,
non capisco a quale *vecchio* comportamento ti riferisci. Le cose
stanno così sin dalla pubblicazione dell' ANSI C (C89), la cui bozza
quasi-finale risale al 1983.
Ti riferisci a un compilatore ancora più vecchio? Se sì, raccontaci le
caratteristiche dei sistemi in uso all'epoca, mi interessano
moltissimo!
Luca

 vengono sostituite da un'unica definition della forma int var = 0;
Vedi su. A parte che definire la stessa variabile con lo stesso
scope nello stesso file mi pare un comportamento decisamente
bizzarro.
a.


lo è, ma può accadere includendo molti header.
Luca

 Questo, in base a 6.9.2 par.2 e 6.9 par.5, č undefined behavior.
Non  mi pare proprio, che standard stai citando ?
Potrei sbagliare ma il C99 non dice nulla del genere, anzi:
====================================================================
6.9.2 External object definitions
Semantics
1 If the declaration of an identifier for an object has file scope
   and an initializer, the declaration is an external definition for
   the identifier.
2 A declaration of an identifier for an object that has file scope
   without an initializer, and without a storage-class specifier or
   with the storage-class specifier static, constitutes a tentative
   definition. If a translation unit contains one or more tentative
   definitions for an identifier, and the translation unit contains
   no external definition for that identifier, then the behavior is
   exactly as if the translation unit contains a file scope declaration
   of that identifier, with the composite type as of the end of the
   translation unit, with an initializer equal to 0.
3 If the declaration of an identifier for an object is a tentative
   definition and has internal linkage, the declared type shall not
   be an incomplete type.
4 EXAMPLE 1
   int i1 = 1;       // definition, external linkage
   static int i2= 2; // definition, internal linkage   <============
   extern int i3= 3; // definition, external linkage
   int i4;           // tentative definition, external linkage
   static int i5;    // tentative definition, internal linkage <======
   int i1;           // valid tentative definition, refers to previous
   int i2;           // 6.2.2 renders undefined, linkage disagreement <==
   int i3;           // valid tentative definition, refers to previous
   int i4;           // valid tentative definition, refers to previous
   int i5;           // 6.2.2 renders undefined, linkage disagreement <==
   extern int i1; // refers to pre vious, whose linkage is external
   extern int i2; // refers to pre vious, whose linkage is internal
   extern int i3; // refers to pre vious, whose linkage is external
   extern int i4; // refers to pre vious, whose linkage is external
   extern int i5; // refers to pre vious, whose linkage is internal
====================================================================
Come vedi dagli esempi, e' perfettamente legale dichiarare/definire in
moduli (file) diversi la stessa variabile,
(anche se io uso definire le variabili pubbliche in un solo modulo e
  negli header usare *sempre* la keyword extern.)
Ci sono problemi se una variabile a livello di modulo (file) e'
dichiarata anche *static* (scope locale) e poi viene dichiarata pubblica 
in altri file...(esempi int2 e int5)
 > lo č, ma puň accadere includendo molti header.
certo, ma IMHO e' sempre una pessima cosa dichiarare
variabili in include senza usare la keywork "extern"...
ciao
A.

 variabili in include senza usare la keywork "extern"...
probabilmente hai ragione, non mi viene in mente alcun motivo utile
per fare altrimenti.
Però, se è permesso dalle regole, un motivo c'è, e penso sia perchè in
alcune implementazioni pre standard la keyword "extern" era
completamente opzionale, cioè non mettere nessuno specificatore di
classe di memorizzazione era come mettere extern. Nota che ciò è quel
che accade per le funzioni, è estremamente plausibile (e logico) che
su alcune implementazioni pre-standard l'uso di extern per funzioni e
oggetti fosse lo stesso. Altre implementazioni hanno fatto scelte
diverse e lo standard, quando è arrivato, ha dovuto adottare regole
che accontentassero tutti quanti.