Обобщённое программирование позволяет писать полностью обобщённые и повторно используемые алгоритмы, эффективность которых такая же, как у алгоритмов, предназначенных для конкретного типа данных. В C такого обобщённого программирования можно достичь определёнными способами. Здесь мы представляем один метод — использование указателей типа void.void*。

Посмотрите на следующую функцию swap, которая меняет содержимое двух элементов, используя целочисленный int в качестве примера:

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

Когда вы хотите поменять местами два значения типа char, вам всё равно придётся переписать функцию с параметром типа char. Можем ли мы использовать указатель void в качестве параметра? Смотрите следующие изменения:

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

Этот код неверен и не скомпилируется. Во-первых, переменные нельзя объявлять какvoidvoid. И вы не знаете, какого типа параметр, переданный этой функции, поэтому не можете определить объявление типа. В то же время вы не можете применить*его к указателю void, потому что система не имеет информации о размере объекта, на который указывает этот адрес. Во время компиляции компилятор не может знать тип параметра, переданного этой функции. Чтобы реализовать здесь обобщённую функцию, нужно передать в месте вызова размер адресного пространства size объектов, которые нужно поменять местами, и использовать функцию memcpy(), определённую в заголовочном файле string.h. Изменения следующие:

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

При вызове этой функции её можно вызвать следующим образом (это также применимо к другим типам x, y):

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

Далее давайте рассмотрим функцию с другой функциональностью:

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

Эта функция ищет элемент key в массиве array. Если найден, возвращает его индекс; в противном случае возвращает -1.

Как указано выше, также можно реализовать обобщённые функции:

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

Строка 3 кода: приведение первого адреса массива к указателю на тип char использует особенность, что тип char имеет размер 1 байт, так что elemAddr указывает на первый адрес i-1-го элемента этого «обобщённого» массива. Как упоминалось ранее, в этот момент вы не знаете, какого типа данные вы передали, и система не может определить, какова длина одного элемента этого массива или сколько байтов нужно пропустить до следующего элемента. Поэтому, приведя к указателю на char и добавив произведение переданной информации о размере элемента и накопленного счётчика i, т. е. адрес смещения, можно получить первый адрес i-1-го элемента этого массива. Таким образом, независимо от того, на какой тип указывает переданный параметр, вы можете получить указатель на правильный элемент, тем самым реализуя обобщённое программирование.

Прототип функции memcmp(): int memcmp(void *dest, const void *src, int n), сравнивает содержимое двух областей памяти длиной n, первые адреса которых — dest и src соответственно.

Эта функция ищет ключевой элемент в массиве base. Если он найден, она возвращает информацию о его адресе; в противном случае она возвращает NULL.

Если используются указатели на функции, можно достичь обобщённости её поведения:

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

Затем определите функцию, которую нужно вызвать:

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

Посмотрите на следующий вызов:

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 также может реализовать определённую степень обобщённого программирования, но это небезопасно, и система выполняет лишь ограниченные проверки этого. При программировании нужно быть особенно осторожным.

Исходный URL: https://www.cnblogs.com/wuyudong/p/c-general-function1.html