Extern

ceer
extern
<justify>Jedno ze słów kluczowych oraz specyfikatorów (kwalifikatorów, modyfikatorów) klas pamięci dla deklarowanych obiektów. Oznacza, że deklaracja nie jest deklaracją w sensie fizycznym, a jedynie odwołaniem do deklaracji znajdującej się w innej jednostce kompilacji (module, pliku, bibliotece - przyp. autora). Jednym słowem, jest to sposób na poinformowanie kompilatora, by nie szukał danej zmiennej globalnej w aktualnym pliku. Jednak na tym nie koniec możliwości tego modyfikatora.</justify> Specyfikatory <font color="blue" style="font-family: 'Courier New',Courier,monospace;">extern</span> i Static wzajemnie się wykluczają, dlatego nie zaleca się tworzenia deklaracji zawierających obydwa te słowa jednocześnie. Ponadto zabroniona jest inicjalizacja zmiennej zadeklarowanej z użyciem modyfikatora <font color="blue" style="font-family: 'Courier New',Courier,monospace;">extern</span>!

     1 Znaczenie dla zmiennych i stałych
     2 Znaczenie dla funkcji
     3 Znaczenie dla fragmentów kodu
     4 Konsolidacja języka C/C++ z innymi językami

Znaczenie dla zmiennych i stałych

<justify> Jak już zostało wspomniane, jeżeli <font color="blue" style="font-family: 'Courier New',Courier,monospace;">extern</span> poprzedza deklarację nie zainicjalizowanej zmiennej (globalnej lub lokalnej) albo stałej, oznacza to wówczas, że deklarowany obiekt nie zawiera się w danym pliku, a w innej jednostce kompilacji (może to być inny moduł, nagłówek, biblioteka statyczna lub dynamiczna itp.):</justify>
/* ==== plik1.c ===== */
#include <stdio.h>
#include <stdlib.h>
 
   int liczba;  /* deklaracja zmiennej globalnej */
 
   /* ... */
 
   liczba = 13;
 
   /* ... */
/* ==== plik2.c ===== */
#include <stdio.h>
#include <stdlib.h>
#include "plik1.h"
 
   extern int liczba;  /* deklaracja zmiennej globalnej, znajdującej się w pliku 'plik1.c' */
 
int main (int argc, char** argv)
{
   printf("liczba=%d\n", liczba);  /* → wypisze na wyjściu: liczba=13 */
   return 0;
}
<justify> Jeżeli specyfikator ten poprzedza deklarację stałej zainicjalizowanej, oznacza to, że taka stała posiada łączność zewnętrzną (więc z kolei <font color="blue" style="font-family: 'Courier New',Courier,monospace;">extern</span> przed stałą niezainicjalizowaną importuje taką stałą). W odróżnieniu od zmiennych, które można powstrzymać przed eksportowaniem symbolu przez specyfikator Static, stałe globalne wymagają <font color="blue" style="font-family: 'Courier New',Courier,monospace;">extern</span>, żeby eksportować symbol na zewnątrz.</justify> Obiekty stają się globalne dla całego programu, gdy ominie się specyfikację klasy pamięci lub zastosuje słowo kluczowe <font color="blue" style="font-family: 'Courier New',Courier,monospace;">extern</span>, co nadaje im łączność zewnętrzną.

Znaczenie dla funkcji

<justify> Wewnątrz funkcji deklaracje ze specyfikatorem <font color="blue" style="font-family: 'Courier New',Courier,monospace;">extern</span> również wskazują, że pamięć dla deklarowanych obiektów będzie zarezerwowana gdzie indziej. Jeżeli deklaracja obiektu wewnątrz bloku (pętli, funkcji itp. - przyp. autora) nie zawiera specyfikatora <font color="blue" style="font-family: 'Courier New',Courier,monospace;">extern</span>, wówczas obiekt ten nie ma łączności i jest unikalny w funkcji. W przeciwnym wypadku, gdy w zasięgu otaczającym dany blok obowiązuje zewnętrzna deklaracja tego samego obiektu, wówczas ma on taka samą łączność, jak w deklaracji zewnętrznej i odnosi się do tego samego obiektu:</justify>
/* ===== external.c ===== */
#include <stdio.h>
#include <stdlib.h>
 
   extern int liczba;   /* zmienna zadeklarowana w innym module */
 
int main (int argc, char** argv)
{
    int liczba;
    {
       extern int liczba;   /* deklaracja odwołuje się do zmiennej liczba, zadeklarowanej w obrębie najbliższego bloku */
 
    }
    return 0;
}
<justify> Nierzadko można natrafić na deklarację prototypu funkcji, której typ wartości zwracanej poprzedzony jest specyfikatorem <font color="blue" style="font-family: 'Courier New',Courier,monospace;">extern</span>. Oznaczać to może chociażby deklarację funkcji, której ciało (oraz pierwotna deklaracja) znajduje się w innym module. Opcjonalnie można stosować ten specyfikator przed deklaracjami prototypu funkcji w tym samym module, zwłaszcza, gdy ciało danej funkcji umieszczona zostało poniżej funkcji głównej:</justify>
#include <stdio.h>
#include <stdlib.h>
 
   extern int kwadrat (int liczba);
 
int main (int argc, char** argv)
{
    printf("Kwadrat liczby 8 wynosi %d\n", kwadrat(8));
    return 0;
}
 
int kwadrat (int liczba)
{
    return liczba*liczba;
}
<justify> Jak jednak zostało wspominane, nie jest to zabieg konieczny, a co za tym idzie, większość kompilatorów ignoruje ten specyfikator przed nazwą funkcji (każda deklaracja funkcji posiada ten kwalifikator domyślnie). Często jest to jednak wymóg w przypadkach, gdy w skład projektu wchodzą m.in. pliki asemblerowskie. Dzięki deklaracji z użyciem <font color="blue" style="font-family: 'Courier New',Courier,monospace;">extern</span> można się odwoływać do funkcji napisanych w assemblerze, a nigdzie wcześniej niezadeklarowanych (trochę więcej na ten temat w tej części artykułu).</justify>

Znaczenie dla fragmentów kodu

<justify> Znaczenie zgoła inne niż dotychczas słowo kluczowe <font color="blue" style="font-family: 'Courier New',Courier,monospace;">extern</span> ma w języku C++ dla objętych specjalnym blokiem fragmentów kodu. Dzięki wspomnianemu blokowi możliwe jest jawne określenie języka nadającego reguły kompilacji dla danego bloku:</justify>
  extern "C" {
     typedef struct stDrzewo {
        int data;
        struct stDrzewo *next;
    } drzewoBST;
  }
<justify> Fragment kodu z powyższego przykładu zostanie skompilowany zgodnie z regułami kompilacji języka ANSI C.</justify> Język C/C++ może być bardzo łatwo konsolidowany (łączony) z wieloma innymi językami, które kompilowane są bezpośrednio do kodu maszynowego (m.in.: Assembler, Fortran oraz C++). Ponadto dzięki specjalnym bibliotekom można go łączyć z językami bardzo wysokiego poziomu (takimi jak np. Python czy też Ruby) <justify> Dzięki tej metodzie można równie dobrze ograniczyć niektóre, dość irytujące aspekty języka, które pierwotnie nie występowały w czystym C/C++, a pojawiły się w C++. Przykładem może tu być różnica działania typu wyliczeniowego Enum dla obu języków. Ponadto jest to doskonały sposób na dołączenie biblioteki, czy też modułu napisanego w całości w czystym C, bez obawy o niekompatybilność:</justify>
  extern "C" {
    #include "tablice_c.h";
  }
<justify>a także na określenie języka nadającego reguły kompilacji dla danej funkcji:</justify>
     extern "C" void kwadrat (void);
Funkcja poprzedzona <font color="blue" style="font-family: 'Courier New',Courier,monospace;">extern</span> <font color="red">"C"</span> nie może podlegać przeciążaniu (co nie oznacza, że nie może być wiele funkcji o tej samej nazwie; ograniczenie to tyczy się zastosowania specyfikatora <font color="blue" style="font-family: 'Courier New',Courier,monospace;">extern</span> <font color="red">"C"</span> jedynie dla jednej deklaracji funkcji o danej nazwie). <justify>Na podobnej zasadzie umieszcza się w kodzie wstawki asemblerowskie:</justify>
#include <cstdio>
 
int main(void)
{
   asm {
    mov eax, 1
   }
   return 0;
}

Konsolidacja języka C/C++ z innymi językami

<justify> Jak już zostało wcześniej wspominane, kod źródłowy języka C/C++ można bez problemu konsolidować ze źródłami innych języków, pod warunkiem, że ich kompilacja następuje bezpośrednio do kodu maszynowego. Rozważmy program składający się z dwóch modułów - jeden jako plik źródłowy poleceń asemblerowskich, drugi jako zwykły kod języka C/C++.</justify>

plik_asm.S

  .text
  .globl _funkcja ; deklaracja funkcji o nazwie 'funkcja' 
_funkcja:
  pushl %ebp
  movl %esp, %ebp
  movl $4, %eax ; 4 to funkcja systemowa "write"
  movl $1, %ebx ; 1 to stdout
  movl $tekst, %ecx ; adres naszego napisu
  movl $len, %edx ; długość napisu w bajtach
  int $0x80 ; wywołanie przerwania systemowego
  popl %ebp
  ret
 
  .data
tekst:
  .string "Witaj \305\233wiecie!\n"
  len = . - tekst

plik_c.c

    extern void funkcja (void); /* musimy użyć słowa extern */
 
int main ()
{
    funkcja ();
    return 0;
}
<justify>po skompilowaniu (np. przy użyciu uniksowego GCC):</justify>

<font color="gray">as plik_asm.S -o plik_asm.o
gcc plik_c.c -c -o plik_c.o
gcc plik_c.o plik_asm.o -o program</span>

<justify>powinniśmy otrzymać program wypisujący na ekranie frazę:</justify>

<font color=gray>Witaj świecie!</font>

<justify> Należy zauważyć, że w powyższym przykładzie językiem wiodącym jest C/C++. Ponadto w kodzie użyta jest funkcja, której ciało znajduje się w innym module (mało tego - dodatkowo w innym języku), a nigdzie nie występuje deklaracja jej prototypu, dlatego konieczne jest tutaj użycie słowa kluczowego <font color="blue" style="font-family: 'Courier New',Courier,monospace;">extern</span> przed deklaracją tej funkcji w danym module.</justify>

<font size="1">na podstawie wikibooks.org</span>


Zobacz też:

0 komentarzy