Programowanie w języku C/C++

Extern

  • 2008-05-12 11:16
  • 0 komentarzy
  • 33259 odsłon
  • Oceń ten tekst jako pierwszy
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 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!
Spis treści

          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 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.):</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 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.</justify>
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


<justify>     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:</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 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:</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 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).</justify>

Znaczenie dla fragmentów kodu


<justify>     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:</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 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, 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 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 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.</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>
<font color=gray>Witaj świecie!</font>

<justify>     Należy zauważyć, że w powyższym przykładzie językiem wiodącym jest 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>


na podstawie wikibooks.org


Zobacz też: