2016-05-30 12 views
8

Mam problem ze zrozumieniem, dlaczego fragment kodu działa. Poniżej przedstawiono funkcję porównania implementacji qsort dla stdlib:Problemy ze wskaźnikami i wskaźnikami do wskaźników

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

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

Oczywiście działa to tylko dla ciągów. Moje pytanie brzmi: dlaczego poniższy kod działa?

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

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

Wydaje mi się, że w drugiej wersji, jestem mocno odlewania, co wyraźnie jest char* do char**. Problem polega na tym, że zmienna nadal będzie zawierała adres zmiennej char. Kiedy aplikuję *, rozumiem, że C przetworzy to polecenie, pobierając zawartość p1, a następnie odczytując 8 bajtów (na moim łuku) po adresie przechowywanym w środku, aby ostatecznie uzyskać wartość typu char*. To powinno, moim zdaniem, doprowadzić do połączenia 8 znaków w nieprawidłowy adres pamięci.

Mimo to obie funkcje działają równie dobrze. Gdzie się mylę?

+1

Druga wersja wydaje mi się poprawne.Trudno powiedzieć, jak działa pierwsza wersja, nie widząc reszty kodu. –

+1

jak nazwać funkcję? Jeśli wywołasz funkcję podobną do 'scmp (" cześć "," cześć ");' działa tylko pierwsza wersja: http://ideone.com/P96Wmj – mch

+1

Może się wydawać, że działa w niektórych przypadkach, ale nie będzie działa we wszystkich przypadkach. Jeśli na przykład p1 wskazuje na napis "abcdefgh" i p2 na inny ciąg "abcdefgh". Teraz łańcuchy są równe, więc oba są interpretowane jako ten sam adres (nazwijmy to p). Następnie strcmp porówna ciąg w p do łańcucha w p, a ponieważ oba parametry wskazują na ten sam adres, zawartość jest z definicji taka sama. –

Odpowiedz

6

Załóżmy, że chcesz posortować tablicę z int s przy użyciu qsort.

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

Po pierwsze, należy utworzyć funkcję, która może porównać dwie int s.

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

Następnie można użyć:

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

Kiedy numbers jest przekazywana do qsort, jest zepsute do int* i przekazywane jako void*. Kiedy musimy wyodrębnić numer z void* w intCompare, musimy przesłać go do int*, zanim usuniemy wskaźnik i porównamy wartości.

Biorąc analogię do strun, powiedzmy chcesz uporządkować:

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

Wezwanie do qsort będą:

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

Kiedy strings jest przekazywana do qsort, jest zepsute do char** i przekazano jako void*. Podstawowe typy wskaźników przekazywanych do scmp przez qsort będą typu char**, a nie char*. W związku z tym jest poprawne użycie:

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

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

Pierwsza wersja działa w wyniku szczęścia przez przypadek w niektórych przypadkach. Oto przykładowy program, który pokazuje kilka przypadków, w których nie działa, podczas gdy druga wersja powinna zawsze działać.

#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(); 
} 

Output (używając gcc 4.8.4):

abc 
xyz 
def 

abc 
def 
xyz 

abc 
xyz 
def 

abc 
def 
xyz 
+1

Dobrze jest zobaczyć bardzo szczegółowe odpowiedzi. +1 – Mirakurun

+0

To ma sens - dzięki! – user1123530