2012-09-17 4 views
0

Чтобы сравнить строку, используя библиотечную функцию, я научился писать функцию сравнения для сравнения двух строк, но им не совсем понятно, почему они это делают.char ** then dereference to char *

int StrCmp(const void *a, const void *b){ 
    char *s1 = *(char**) a; 
    char *s2 = *(char**) b; 
    return strcmp(s1,s2); 
} 

Зачем им сначала бросать его на шар **? Почему бы просто не перевести их на char * напрямую? как

return strcmp((char*)a, (char*)b); 

Что смысл литья (указатель), чтобы (указатель на указатель) Если у меня есть

char *x = "string"; 

если я кастинг х

(char**)x; // what is this? Is this character 's'? 

Im довольно путают с этим, спасибо за разъяснение , и еще один вопрос о const , если это const, могу ли я стиль л бросить их (altought я знаю, что могу)

+2

Где у вас этот код? Он выглядит непристойно взломанным ... – Mysticial

+0

Я думаю, что это должна быть функция сравнения qsort для сортировки строк ... – nneonneo

+1

Это действительно оболочка для функций qsort/bsearch или для сохранения какого-либо интерфейса. Вы должны обязательно писать такие обертки, передавая массив указателей на qsort/bsearch, если вы передадите нормальный strcmp(), код сработает и сгорит. – Lundin

ответ

4

«Почему они должны отсылать его (указатель указателя на указатель) в первую очередь? Почему бы просто не просто направить их (указатель на указатель)?»


Им нужно бросить его, что это такое. Если предположить, что значение приходя в точках на ячейку памяти 10000, то есть что-то вроде этого:

(char **) 

    a     address a 
        "points to" 
        *a    **a 
    mem 1000   mem 10000   mem 20000 
    to mem 1007  to mem 10007 
    _____________  _____________  _______________ 
    pointer var --> char pointer -> char 
    10000    20000    'a' 
    _____________  _____________  _______________ 

Если вы приводите его к (символ *) вы рассказываете компилятор , что адрес, на который ссылается (в этом случае 10 000) является символом. Это неверно.

(char *)   address a points 
    a     to 
    mem 1000   mem 10000   mem 20000 
    to mem 1007 
    _____________  ______   ________________ 
    pointer var --> "char"   unreachable char 
    10000    ?     'a' 
    _____________  ______   ________________ 

«Что смысл литья (указатель), чтобы (указатель на указатель) Если я

обугленного * х =„строка“; если я кастинг х

(char **) x; // что это? Этот персонаж '?"


То, что существует в этом примере, что-то вроде:

x   *x or x[0] x[1]  x[2]  x[3]  x[4]  x[5]  x[6] 
mem 15000 mem 30000 mem 30001 mem 30002 mem 30003 mem 30004 mem 30005 mem 30006 
to 15007 
char *  char  char  char  char  char  char  char 
_________ _________ _________ _________ _________ _________ _________ _________ 

30000 --> 's'  't'   'r'  'i'  'n'  'g'  '\0' 
________  _________ _________ _________ _________ _________ _________ _________ 

Если компилятора, что х является символом **, то он думает, что это является картиной:

x    *x or x[0]   **x 
       char * 
mem 15000  mem 30000   "char" 
to 15007  to 30007 
__________  _________   _________ 

30000  --> "string\0?" 
       converted to 
       pointer value -> ?? 
__________  _________   _________ 

Это неверно заканчивается тем, что касается «адреса» первых 8 байтов, начиная с 30000, доходит до «персонажа». Но так как 30000 - это начало o f символьный массив с нулевым символом, в лучшем случае он переходит в некоторую часть памяти и получает некоторый случайный байт, считая его действительным символом. В худшем случае он получит адрес, который недействителен для этой программы, вызывая фатальную ошибку при попытке получить к ней доступ.


так может я просто сделать

return strcmp((char *)a, (char *)b); 

Нет, потому что б не символьные указатели. Для того, чтобы получить полукокс указатели вы не можете избежать:

return strcmp(*(char**)a, *(char**)b); 

Используя пример Линдены вы могли бы назвать так:

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

int StrCmp(const void *a, const void *b){ 
    char *s1 = *(char**) a; 
    char *s2 = *(char**) b; 
    printf("s1:%s\n",s1); 
    printf("s2:%s\n",s2); 
    return strcmp(s1,s2); 
} 

const char* arr_of_ptr[] = 
{ 
    "hello", 
    "world" 
}; 

const char **p_arr_of_ptr = arr_of_ptr; 

int main(void) 
{ 
    const char *cstring1 = "LaDonna"; 
    const char *cstring2 = "McPherson"; 
    const char **pcstring1 = &cstring1; 
    const char **pcstring2 = &cstring2; 
    StrCmp(&arr_of_ptr[0],&arr_of_ptr[1]); 
    StrCmp(pcstring1,pcstring2); 
    StrCmp(p_arr_of_ptr,p_arr_of_ptr + 1); 
} 
+0

, когда я передаю две строки этой функции StrCmp («apple», «Orange»). У меня возникла ошибка сегментации, правильно ли мой код? ох .. мне кажется, понимаю немного больше. Но им все еще путают эти вещи. –

+0

Нет. Вам нужно поместить строки в массив так, как это сделал Лундин в своем примере. – Scooter

+0

Итак, что нам лучше делать, бросать то, что оно должно быть первым. а затем бросить на то, что мы хотим, чтобы это было –

4

Скорее всего вызывающий этой функции используется массив указателей, таких как:?

const char* arr_of_ptr[] = 
{ 
    "hello", 
    "world" 
}; 

В этом случае, первый элемент массива указатель на символ, это не символ в себе. Поэтому функция StrCmp действует как переводчик между массивом указателей на обычные строки C.

Однако они также отбрасывают ключевое слово const, что является плохой практикой. Функция должна быть записана следующим образом:

int StrCmp(const void *a, const void *b){ 
    const char *s1 = *(const char**) a; 
    const char *s2 = *(const char**) b; 
    return strcmp(s1,s2); 
} 

Good reading on the topic.

1

Это эффективно литьем указателя пустот (то есть: void*) удваивать указатель на символ (то есть: char**).

Помните, что оператор направления/удаления ссылки (оператор унарного *) и оператор литья типа (например: (char**)) имеют одинаковый приоритет, но также ассоциативность слева направо.

Итак, рассмотрим следующее:

void myFunction(void* a) { 
    char* s1; // Pointer to a character, or the beginning of a character array or string 
      // Note that the effective difference between a character array and a string is that 
      // a string is a character array that is terminated with the '\0' character 
    s1 = *(char**) a; 
      /* The above statement can be decomposed as follows: 
       * ( *((char**) a)) 
       * 1) Cast the pointer-to-void-type, (ie: void*)'a', 
       * to a pointer-to-pointer-to-char (ie: char**) 
       * 2) Deference the value, so it becomes a pointer-to-char 
       * 3) Assign this char* value to s1 
       */ 
} 

Это очень распространено в C, где вы должны сделать некоторые ловкий магию указателя, потому что язык не поддерживает шаблоны, как его преемник C++. Имейте в виду, что если вы можете работать с расширенной логикой указателя, вам, вероятно, не понадобится использовать C++ вместо C anyways.

Удачи вам!