【问题标题】:char ** then dereference to char *char ** 然后取消对 char * 的引用
【发布时间】:2012-09-09 08:30:51
【问题描述】:

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

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 如果它是一个常量,我还能投射它们吗?(虽然我知道我可以)

【问题讨论】:

  • 你从哪里得到这个代码?它看起来非常骇人听闻......
  • 我想这一定是一个qsort比较函数来排序字符串...
  • 这确实是一个包装器,用于 qsort/bsearch 类型的函数或只是为了保留一些接口或其他。在将指针数组传递给 qsort/bsearch 时,您一定要编写这样的包装器,如果您传递正常的 strcmp(),代码将崩溃并烧毁。
  • 是的。很容易犯错误,因为strcmp 听起来像是一个完美的排序比较函数。
  • 所以我可以直接返回 strcmp((char star)a, (char star)b) 吗?

标签: c pointers casting


【解决方案1】:

“为什么他们必须先将其转换为(字符指针指针)? 为什么不直接将它们转换为(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 *)          address a points 
  a                 to
  mem 1000          mem 10000         mem 20000
  to mem 1007
  _____________     ______            ________________
  pointer var  -->  "char"            unreachable char
  10000             ?                 'a'
  _____________     ______            ________________

"如果我有(a pointer) to (a pointer to pointer) 是什么意思

char *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);
}

【讨论】:

  • 当我将两个字符串传递给这个函数 StrCmp("apple", "Orange")。我遇到分段错误,我的代码是否正确?哦..我似乎明白一点。但我仍然对这些东西感到困惑
  • 没有。您需要像 Lundin 在他的示例中所做的那样将字符串放入数组中。
  • 所以.. 我们最好做的是,投到它应该是第一个。然后投射到我们想要的样子
  • @TimothyLeung 转换为它是什么,然后“取消引用”以获取它指向(或引用)的内容。一个演员,然后一个取消引用。不是一个演员,然后演员。
【解决方案2】:

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

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

Good reading on the topic.

【讨论】:

    【解决方案3】:

    这实际上是将 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。

    祝你好运!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-27
      • 2012-04-16
      • 1970-01-01
      • 1970-01-01
      • 2016-01-20
      • 1970-01-01
      相关资源
      最近更新 更多