2016-05-30 47 views
8

Tôi đang gặp một số sự cố khi hiểu tại sao một đoạn mã hoạt động. Sau đây là một hàm so sánh để thực hiện stdlib của qsort:Sự cố với con trỏ và con trỏ đến con trỏ

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

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

Đương nhiên, điều này chỉ hoạt động đối với chuỗi. Câu hỏi của tôi là: tại sao mã bên dưới hoạt động?

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

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

Dường như với tôi rằng trong phiên bản thứ hai, tôi mạnh mẽ đúc những gì rõ ràng là một char*-char**. Vấn đề là biến sẽ vẫn chứa địa chỉ cho biến số char. Khi tôi áp dụng *, tôi hiểu rằng C sẽ xử lý lệnh này bằng cách tìm nạp nội dung của p1 và sau đó đọc 8 byte (trên vòm của tôi) theo địa chỉ được lưu trữ bên trong để cuối cùng nhận được giá trị loại char*. Điều này, theo ý kiến ​​của tôi, dẫn đến việc kết hợp 8 ký tự vào một địa chỉ bộ nhớ không hợp lệ.

Tuy nhiên, cả hai chức năng hoạt động tốt như nhau. Tôi làm sai ở đâu?

+1

Phiên bản thứ hai có vẻ đúng với tôi.Rất khó để biết phiên bản đầu tiên hoạt động như thế nào mà không thấy phần còn lại của mã của bạn. –

+1

làm thế nào để bạn gọi hàm? Nếu bạn gọi hàm giống như 'scmp (" hello "," hello ");' chỉ phiên bản đầu tiên sẽ hoạt động: http://ideone.com/P96Wmj – mch

+1

Nó có thể hoạt động trong một số trường hợp nhưng nó sẽ không làm việc cho mọi trường hợp. Nếu ví dụ p1 trỏ tới chuỗi "abcdefgh" và p2 thành chuỗi khác "abcdefgh". Bây giờ các chuỗi là như nhau vì vậy cả hai đều được hiểu là cùng một địa chỉ (chúng ta hãy gọi nó là p). Sau đó strcmp sẽ so sánh chuỗi tại p với chuỗi tại p và vì cả hai tham số đều trỏ đến cùng một địa chỉ, nội dung theo định nghĩa giống nhau. –

Trả lời

6

Giả sử bạn muốn sắp xếp một mảng của int s bằng cách sử dụng qsort.

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

Trước tiên, bạn tạo một hàm có thể so sánh hai int s.

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

Sau đó, bạn có thể sử dụng:

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

Khi numbers được chuyển cho qsort, nó được phân rã đến một int* và thông qua như void*. Khi chúng ta cần trích xuất số từ một số void* trong intCompare, chúng ta cần phải đưa số đó đến int* trước khi chúng ta dereference con trỏ và so sánh các giá trị.

Lấy tương tự với chuỗi, giả sử bạn muốn sắp xếp:

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

Các cuộc gọi đến qsort sẽ là:

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

Khi strings được chuyển cho qsort, nó được phân rã đến một char** và được chuyển thành void*. Các loại con trỏ cơ bản được chuyển đến scmp bởi qsort sẽ thuộc loại char**, không phải là char*. Do đó, việc sử dụng chính xác là:

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

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

Phiên bản đầu tiên hoạt động do trùng hợp ngẫu nhiên trong một số trường hợp. Đây là một chương trình ví dụ cho thấy vài trường hợp nó không hoạt động trong khi phiên bản thứ hai luôn hoạt động.

#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 (sử dụng gcc 4.8.4):

abc 
xyz 
def 

abc 
def 
xyz 

abc 
xyz 
def 

abc 
def 
xyz 
+1

Tốt để xem một số câu trả lời rất chi tiết. +1 – Mirakurun

+0

Làm cho cảm giác bây giờ - cảm ơn! – user1123530

Các vấn đề liên quan