Extern

ceer
extern

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.

Specyfikatory extern 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 extern!

     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

Jak już zostało wspomniane, jeżeli extern 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.):

/* ==== 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;
}

Jeżeli specyfikator ten poprzedza deklarację stałej zainicjalizowanej, oznacza to, że taka stała posiada łączność zewnętrzną (więc z kolei extern 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ą extern, żeby eksportować symbol na zewnątrz.

Obiekty stają się globalne dla całego programu, gdy ominie się specyfikację klasy pamięci lub zastosuje słowo kluczowe extern, co nadaje im łączność zewnętrzną.

Znaczenie dla funkcji

Wewnątrz funkcji deklaracje ze specyfikatorem extern 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 extern, 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:

/* ===== 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;
}

Nierzadko można natrafić na deklarację prototypu funkcji, której typ wartości zwracanej poprzedzony jest specyfikatorem extern. 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:

#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;
}

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 extern 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).

Znaczenie dla fragmentów kodu

Znaczenie zgoła inne niż dotychczas słowo kluczowe extern 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:

  extern "C" {
     typedef struct stDrzewo {
        int data;
        struct stDrzewo *next;
    } drzewoBST;
  }

Fragment kodu z powyższego przykładu zostanie skompilowany zgodnie z regułami kompilacji języka ANSI C.

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)

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ść:

  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 extern "C" 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 extern"C" 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>

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

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

<span style style="color: gray">Witaj świecie!</span>

<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 extern przed deklaracją tej funkcji w danym module.</justify>

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


Zobacz też:

0 komentarzy