Generische Programmierung ermöglicht es Ihnen, vollständig verallgemeinerte und wiederverwendbare Algorithmen zu schreiben, deren Effizienz dieselbe ist wie die von Algorithmen, die für einen bestimmten Datentyp entwickelt wurden. In C kann solche generische Programmierung durch bestimmte Mittel erreicht werden. Hier stellen wir eine Methode vor — die Verwendung von void-Zeigern.void*。

Betrachten Sie die folgende Funktion swap, die die Inhalte zweier Elemente vertauscht, am Beispiel des Integer-Typs int:

void swap(int* i1,int* i2){  
         int temp;  
         temp = *i1;  
         *i1 = *i2;  
         *i2 = temp;  
}  

Wenn Sie zwei char-Typen vertauschen möchten, müssen Sie trotzdem eine Funktion mit dem Parametertyp char neu schreiben. Können wir einen void-Zeiger als Parameter verwenden? Sehen Sie sich die folgenden Änderungen an:

void swap(void *vp1,void *vp2){  
       void temp = *vp1;  
       *vp1 = *vp2;  
       *vp2 = temp;  
}  

Dieser Code ist falsch und wird nicht kompiliert. Erstens können Variablen nicht alsvoidvoid deklariert werden. Und Sie wissen nicht, welchen Typs der an diese Funktion übergebene Parameter ist, daher können Sie keine Typdeklaration bestimmen. Gleichzeitig können Sie nicht*es auf einen void-Zeiger anwenden, weil das System keine Information über die Größe des Objekts hat, auf das diese Adresse zeigt. Zum Kompilierzeitpunkt kann der Compiler den Typ des an diese Funktion übergebenen Parameters nicht kennen. Um hier eine generische Funktion zu implementieren, müssen Sie an der Aufrufstelle die Adressraumgröße size der zu vertauschenden Objekte übergeben und die in der Header-Datei string.h definierte Funktion memcpy() verwenden. Die Änderungen sind wie folgt:

void swap(void *vp1,void *vp2,int size){  
      char buffer[size];//注意此处gcc编译器是允许这样声明的
      memcpy(buffer,vp1,size);  
      memcpy(vp1,vp2,size);  
      memcpy(vp2,buffer,size);  
}  

Beim Aufruf dieser Funktion können Sie sie wie folgt aufrufen (gilt auch für andere Typen von x, y):

int x = 27,y = 2;  
swap(&x,&y,sizeof(int));

Als Nächstes sehen wir uns eine Funktion mit einer anderen Funktionalität an:

int lsearch(int key,int array[],int size){
      for(int i = 0;i < size; ++i)
                  if(array[i] == key)
                           return i;
     return -1;
}

Diese Funktion sucht im Array array nach dem Element key. Wenn sie gefunden wird, gibt sie dessen Index zurück; andernfalls gibt sie -1 zurück.

Wie oben dargestellt, können auch generische Funktionen implementiert werden:

void* lsearch(void* key, void *base, int n, int elemSize){
    for(int i = 0;i < n; ++i){
        void *elemAddr = (char *)base+i*elemSize;
        if(memcmp(key, elemAddr, elemSize) == 0)
            return elemAddr;
    }
    return NULL;
}

Zeile 3 des Codes: Das Casten der ersten Adresse des Arrays in einen Zeiger auf den Typ char nutzt die Eigenschaft, dass der Typ char 1 Byte groß ist, sodass elemAddr auf die erste Adresse des (i-1)-ten Elements dieses "generischen" Arrays zeigt. Wie bereits erwähnt, wissen Sie zu diesem Zeitpunkt nicht, welchen Datentyp Sie übergeben haben, und das System kann nicht bestimmen, wie lang ein Element dieses Arrays ist oder wie viele Bytes übersprungen werden müssen, um zum nächsten Element zu gelangen. Daher können Sie durch das Casten in einen Zeiger auf char und das Addieren des Produkts aus der übergebenen Elementgrößeninformation und dem akkumulierten Zähler i, also der Offset-Adresse, die erste Adresse des (i-1)-ten Elements dieses Arrays erhalten. Auf diese Weise können Sie, unabhängig davon, auf welchen Typ von Zeiger der übergebene Parameter zeigt, einen Zeiger auf das richtige Element erhalten und so generische Programmierung umsetzen.

Funktionsprototyp von memcmp(): int memcmp(void *dest, const void *src, int n), vergleicht die Inhalte zweier Speicherbereiche der Länge n, deren erste Adressen jeweils dest und src sind.

Diese Funktion sucht das Schlüsselelement im Array base. Wenn es gefunden wird, gibt sie seine Adressinformation zurück; andernfalls gibt sie NULL zurück.

Wenn Funktionszeiger verwendet werden, kann die Generizität seines Verhaltens erreicht werden:

void *lsearch(void *key,void *base,int n,int elemSize,int(*cmpfn)(void*,void*,int)){
    for(int i = 0;i < n; ++i){
        void *elemAddr = (char *)base+i*elemSize;
        if(cmpfn(key,elemAddr,elemSize) == 0)
            return elemAddr;
    }
    return NULL;
}

Definieren Sie dann eine aufzurufende Funktion:

int intCmp(void* elem1,void* elem2){
        int* ip1 = elem1;
        int* ip2 = elem2;
        return *ip1-*ip2;
}

Betrachten Sie den folgenden Aufruf:

int array[] = {1,2,3,4,5,6};
int size = 6;
int number = 3;
int *found = lsearch(&number,array,size,sizeof(int),intCmp);
if(found == NULL)
         printf("NO\n");
else
         printf("YES\n");

C kann auch ein gewisses Maß an generischer Programmierung umsetzen, aber es ist unsicher, und das System führt nur begrenzte Prüfungen durch. Beim Programmieren müssen Sie besonders vorsichtig sein.

Original-URL: https://www.cnblogs.com/wuyudong/p/c-general-function1.html