2016-05-30 4 views
8

У меня возникли проблемы с пониманием того, почему работает часть кода. Ниже приводится функция сравнения для реализации STDLIB о QSort:Проблема с указателями и указателями на указатели

int scmp(const void *p1, const void * p2) 
{ 
    char *v1, *v2; 

    v1 = (char *) p1; 
    v2 = (char *) p2; 
    return strcmp(v1,v2); 
} 

Естественно, это работает только для строк. Мой вопрос: почему работает код ниже?

int scmp(const void *p1, const void * p2) 
{ 
    char *v1, *v2; 

    v1 = *(char **) p1; 
    v2 = *(char **) p2; 
    return strcmp(v1,v2); 
} 

Мне кажется, что во второй версии, я с силой литья, что, безусловно, является char* к char**. Проблема в том, что переменная по-прежнему будет содержать адрес переменной char. Когда я применяю *, я понимаю, что C обработает эту команду, извлекая содержимое p1, а затем прочитав 8 байтов (на моей арке), следуя за адресом, хранящимся внутри, так что он в конечном итоге получит значение типа char*. Это должно, на мой взгляд, привести к объединению 8 символов в неправильный адрес памяти.

Тем не менее, обе функции работают одинаково хорошо. Где я иду не так?

+1

Вторая версия кажется правильным для меня.Трудно сказать, как работает первая версия, не видя остальной части вашего кода. –

+1

как вы называете эту функцию? Если вы вызываете эту функцию как 'scmp (« hello »,« hello »),' будет работать только первая версия: http://ideone.com/P96Wmj – mch

+1

Возможно, это работает в некоторых случаях, но это не будет работа для всех случаев. Если, например, p1 указывает на строку «abcdefgh» и p2 на другую строку «abcdefgh». Теперь строки равны, поэтому оба они интерпретируются как один и тот же адрес (назовем его p). Затем strcmp будет сравнивать строку в p с строкой в ​​p, и поскольку оба параметра указывают на один и тот же адрес, содержимое по определению одно и то же. –

ответ

6

Предположим, вы хотите отсортировать массив из int с помощью qsort.

int numbers[] = {10, 50, 35, 62, 22}; 

Во-первых, необходимо создать функцию, которая может сравнить два int с.

int intCompare(void* p1, void* p2) 
{ 
    int n1 = *(int*)p1; 
    int n2 = *(int*)p2; 
    return (n1 < n2); 
} 

Затем, вы можете использовать:

qsort(numbers, 5, sizeof(int), intCompare); 

Когда numbers передается qsort, она затухает к int* и передается как void*. Когда нам нужно извлечь номер из void* в intCompare, нам нужно отдать его int*, прежде чем мы разыграем указатель и сравним значения.

Принимая аналогию строк, скажем, вы хотите отсортировать:

char* strings[] = { "abc", "xyz", "def" }; 

Вызов qsort будет:

qsort(strings, 3, sizeof(char*), scmp); 

Когда strings передается qsort, она затухает к char** и передан как void*. Нижеуказанные типы указателей, передаваемых на scmp, на qsort будут иметь тип char**, а не char*. Следовательно, правильно использовать:

int scmp(const void *p1, const void * p2) 
{ 
    char *v1, *v2; 

    v1 = *(char **) p1; 
    v2 = *(char **) p2; 
    return strcmp(v1,v2); 
} 

Первая версия работает из-за случайного совпадения в некоторых случаях. Вот примерная программа, которая показывает пару случаев, когда она не работает, пока вторая версия всегда должна работать.

#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 

// First version of scmp 
int scmp1(const void *p1, const void * p2) 
{ 
    char *v1, *v2; 

    v1 = (char *) p1; 
    v2 = (char *) p2; 
    return strcmp(v1,v2); 
} 

// Second version of scmp 
int scmp2(const void *p1, const void * p2) 
{ 
    char *v1, *v2; 

    v1 = *(char **) p1; 
    v2 = *(char **) p2; 
    return strcmp(v1,v2); 
} 

void test1() 
{ 
    char* strings[] = { "abc", "xyz", "def" }; 
    qsort(strings, 3, sizeof(char*), scmp1); 
    for(int i = 0; i < 3; ++i) 
    { 
     printf("%s\n", strings[i]); 
    } 
    printf("\n"); 
} 

void test2() 
{ 
    char* strings[] = { "abc", "xyz", "def" }; 
    qsort(strings, 3, sizeof(char*), scmp2); 
    for(int i = 0; i < 3; ++i) 
    { 
     printf("%s\n", strings[i]); 
    } 
    printf("\n"); 
} 

void test3() 
{ 
    char** strings = malloc(3*sizeof(char*)); 
    strings[0] = "abc"; 
    strings[1] = "xyz"; 
    strings[2] = "def"; 

    qsort(strings, 3, sizeof(char*), scmp1); 
    for(int i = 0; i < 3; ++i) 
    { 
     printf("%s\n", strings[i]); 
    } 

    free(strings); 
    printf("\n"); 
} 

void test4() 
{ 
    char** strings = malloc(3*sizeof(char*)); 
    strings[0] = "abc"; 
    strings[1] = "xyz"; 
    strings[2] = "def"; 

    qsort(strings, 3, sizeof(char*), scmp2); 

    for(int i = 0; i < 3; ++i) 
    { 
     printf("%s\n", strings[i]); 
    } 

    free(strings); 
    printf("\n"); 
} 

int main() 
{ 
    // Does not work. 
    test1(); 

    // Should work always. 
    test2(); 

    // Does not work. 
    test3(); 

    // Should work always. 
    test4(); 
} 

Выход (с помощью GCC 4.8.4):

abc 
xyz 
def 

abc 
def 
xyz 

abc 
xyz 
def 

abc 
def 
xyz 
+1

Приятно видеть некоторые очень подробные ответы. +1 – Mirakurun

+0

Теперь имеет смысл - спасибо! – user1123530

Смежные вопросы