Посмотрите на следующую функцию 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