Cerca nel sito:
ricerca
avanzata

Frasi Celebri...

Di tutte le invenzioni dell'uomo dubito che ve ne sia qualcuna pi? facilmente realizzata di quella del Paradiso.

Georg Christoph Lichtenberg 

Sondaggio:

In quale citt? vi piacerebbe vivere?

New York
Londra
Roma
Madrid
Parigi
Milano
Tokyo
Berlino
Altro

visualizza risultati


 

Linkage

Abbiamo gia` visto che ad ogni identificatore e` associato uno scope e una lifetime, ma gli identificatori di variabili, costanti e funzioni possiedono anche un linkage.
Per comprendere meglio il concetto e` necessario sapere che in C e in C++ l'unita` di compilazione e` il file, un programma puo` consistere di piu` file che vengono compilati separatamente e poi linkati (collegati) per ottenere un file eseguibile. Quest'ultima operazione e` svolta dal linker e possiamo pensare al concetto di linkage sostanzialmente come a una sorta di scope dal punto di vista del linker. Facciamo un esempio:

 

// File a.cpp
int a = 5;

// File b.cpp
extern int a;

int GetVar() {
  return a;
}

 

Il primo file dichiara una variabile intera e la inizializza, il secondo (trascuriamone per ora la prima riga di codice) dichiara una funzione che ne restituisce il valore. La compilazione del primo file non e` un problema, ma nel secondo file GetVar() deve utilizzare un nome dichiarato in un altro file; perche` la cosa sia possibile bisogna informare il compilatore che tale nome e` dichiarato da qualche altra parte e che il riferimento a tale nome non puo` essere risolto se non quando tutti i file sono stati compilati, solo il linker quindi puo` risolvere il problema collegando insieme i due file. Il compilatore deve dunque essere informato dell'esistenza della variabile al fine di non generare un messaggio di errore; tale operazione viene effettuata tramite la keyword extern.
In effetti la riga extern int a; non dichiara un nuovo identificatore, ma dice "La variabile intera a e` dichiarata da qualche altra parte, lascia solo lo spazio per risolvere il riferimento". Se la keyword extern fosse stata omessa il compilatore avrebbe interpretato la riga come una nuova dichiarazione e avrebbe risolto il riferimento in GetVar() in favore di tale definizione; in fase di linking comunque si sarebbe verificato un errore perche` a sarebbe stata definita due volte (una per file), il perche` di tale errore sara` chiaro piu` avanti.
Naturalmente extern si puo` usare anche con le funzioni (anche se come vedremo e` ridondante):

 

// File a.cpp
int a = 5;

int f(int c) {
  return a+c;
}


// File b.cpp
extern int f(int);

int GetVar() {
  return f(5);
}

 

Si noti che e` necessario che extern sia seguita dal prototipo completo della funzione, al fine di consentire al compilatore di generare codice corretto e di eseguire i controlli di tipo sui parametri e il valore restituito.

Come gia` detto, il C++ ha un'alta compatibilita` col C, tant'e` che e` possibile interfacciare codice C++ con codice C; anche in questo caso l'aiuto ci viene dalla keyword extern. Per poter linkare un modulo C con un modulo C++ e` necessario indicare al compilatore le nostre intenzioni:

 

// Contenuto file C++
extern "C" int CFunc(char*);
extern "C" char* CFunc2(int);

// oppure per risparmiare tempo
extern "C" {
  void CFunc1(void);
  int* CFunc2(int, char);
  char* strcpy(char*, const char*);
}

 

La presenza di "C" serve a indicare che bisogna adottare le convenzioni del C sulla codifica dei nomi (in quanto il compilatore C++ codifica internamente i nomi degli identificatori in modo assai diverso).
Un altro uso di extern e` quello di ritardare la definizione di una variabile o di una funzione all'interno dello stesso file, ad esempio per realizzare funzioni mutuamente ricorsive:

 

extern int Func2(int);

int Func1(int c) {
  if (c==0) return 1;
  return Func2(c-1);
}

int Func2(int c) {
  if (c==0) return 2;
  return Func1(c-1);
}

 

Tuttavia nel caso delle funzioni non e` necessario l'uso di extern, il solo prototipo e` sufficiente, e` invece necessario ad esempio per le variabili:

 

int Func2(int); // extern non necessaria
extern int a;   // extern necessaria


int Func1(int c) {
  if (c==0) return 1;
  return Func2(c-1);
}

int Func2(int c) {
  if (c==0) return a;
  return Func1(c-1);
}

int a = 10;   // definisce la variabile
              // precedentemente dichiarata

 

I nomi che sono visibili all'esterno di un file sono detti avere linkage esterno; tutte le variabili globali hanno linkage esterno, cosi` come le funzioni globali non inline; le funzioni inline, tutte le costanti e le dichiarazioni fatte in un blocco hanno invece linkage interno (cioe` non sono visibili all'esterno del file); i nomi di tipo non hanno alcun linkage, ma devono riferire ad una unica definizione:

 

// File 1.cpp
enum Color { Red, Green, Blue };

extern void f(Color);


// File2.cpp
enum Color { Red, Green, Blue };

void f(Color c) { /* ... */ }

 

Una situazione di questo tipo e` illecita, ma molti compilatori potrebbero non accorgersi dell'errore.
Per quanto concerne i nomi di tipo, fanno eccezione quelli definiti tramite typedef in quanto non sono veri tipi, ma solo abbreviazioni.
E` possibile forzare un identificatore globale ad avere linkage interno utilizzando la keyword static:

 

// File a.cpp
static int a = 5;    // linkage interno

int f(int c) {       // linkage esterno
  return a+c;
}


// File b.cpp
extern int f(int);

static int GetVar() { // linkage interno
  return f(5);
}

 

Si faccia attenzione al significato di static: nel caso di variabili locali static serve a modificarne la lifetime (durata), nel caso di nomi globali invece modifica il linkage.
L'importanza di poter restringere il linkage e` ovvia; supponete di voler realizzare una libreria di funzioni, alcune serviranno solo a scopi interni alla libreria e non serve (anzi e` pericoloso) esportarle, per fare cio` basta dichiarare static i nomi globali che volete incapsulare.

 

successivo
–«  INDICE  »–

 

 

 

 
Powered by paper&pencil (carta&matita ) - Copyright © 2001-2022 Cataldo Sasso