0

为了使用库函数比较字符串,我学会了编写一个比较函数来比较两个字符串,但我不太清楚他们为什么这样做。

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

为什么他们必须先将其转换为 char ** ?为什么不直接将它们转换为 char * 呢?像

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

将(指针)转换为(指向指针的指针)的含义是什么?如果我有

char *x = "string";

如果我为 x 铸造

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

我对此很困惑,感谢澄清,还有一个问题是关于 const 如果它是 const,我还能转换它们吗?(虽然我知道我可以)

4

3 回答 3

4

该函数的调用者很可能正在使用指针数组,例如:

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

在这种情况下,数组的第一个元素是指向 char 的指针,它本身不是 char。因此,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);
}

很好的阅读主题

于 2012-09-17T06:44:05.453 回答
4

“为什么他们必须先将其转换为(char 指针指针)?为什么不直接将它们转换为(char 指针)?”


他们必须把它变成它的样子。假设传入的值指向内存位置 10,000,你有这样的东西:

  (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'
  _____________     _____________     _______________

如果将其转换为 (char *),则您是在告诉编译器 a(在本例中为 10,000)引用的地址是 char。这是不正确的。

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

“将(指针)转换为(指向指针的指针)是什么意思如果我有

字符 *x = "字符串"; 如果我为 x 铸造

(char**)x; // 这是什么?这个角色是's'吗?”


此示例中存在的内容类似于:

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 是一个 char** 那么它认为这是模式:

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

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

它错误地最终转到从 30000 开始的前 8 个字节解析为的任何“地址”并获得一个“字符”。但是由于 30000 是一个以空字符结尾的字符数组的开始,所以它充其量会进入内存的某个部分并获取一些随机字节,认为它是一个有效的字符。在最坏的情况下,它会得到一个对该程序无效的地址,当它试图访问它时会导致致命错误。


所以我能做吗

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

不,因为 a 和 b 不是 char 指针。要获得 char 指针,您无法避免:

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

使用 Linden 的示例,您可以这样调用:

#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);
}
于 2012-09-17T07:27:24.857 回答
1

这实际上是将 void 指针(即:)转换void*为指向 char 的双指针(即:)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。

祝你好运!

于 2012-11-23T21:55:10.467 回答