【问题标题】:const char **name VS char *name[]const char **name VS char *name[]
【发布时间】:2012-11-15 00:31:39
【问题描述】:

我知道这个话题已经讨论过好几次了,我想我基本上知道数组和指针之间的区别,但我对数组如何准确地存储在 mem 中很感兴趣。

例如:

const char **name = {{'a',0},{'b',0},{'c',0},0};
printf("Char: %c\n", name[0][0]); // This does not work

但如果它是这样声明的:

const char *name[] = {"a","b","c"};
printf("Char: %c\n", name[0][0]); // Works well

一切正常。

【问题讨论】:

  • “这不起作用”是什么意思?编译并运行第一个示例时会发生什么?
  • 在我的系统上,这将导致段错误
  • 嗯,甚至无法编译第一个示例
  • 嗯,我得到了 gcc 版本 4.6.3,它可以工作(带有警告)
  • 也许你尝试用 g++ 编译它?

标签: c arrays pointers char


【解决方案1】:

当你定义一个像这样的变量时

char const*  str = "abc";
char const** name = &str;

看起来像这样:

+---+     +---+    +---+---+---+---+
| *-+---->| *-+--->| a | b | c | 0 |
+---+     +---+    +---+---+---+---+

当您使用表单定义变量时

char const* name[] = { "a", "b", "c" };

你有一个指针数组。这看起来像这样:

          +---+     +---+---+
          | *-+---->| a | 0 |
          +---+     +---+---+
          | *-+---->| b | 0 |
          +---+     +---+---+
          | *-+---->| c | 0 |
          +---+     +---+---+

可能令人困惑的是,当你将这个数组传递到某个地方时,它衰减变成了一个指针,你得到了这个:

+---+     +---+     +---+---+
| *-+---->| *-+---->| a | 0 |
+---+     +---+     +---+---+
          | *-+---->| b | 0 |
          +---+     +---+---+
          | *-+---->| c | 0 |
          +---+     +---+---+

也就是说,你得到一个指向数组第一个元素的指针。递增此指针移动到数组的下一个元素。

【讨论】:

  • 非常有趣;它是如何做到这一点的。指针移动到下一个元素?这意味着数组和指向数组的指针具有相同的行为
  • @QuicknDirty:这就是增加指针的定义方式——它使它指向它之前指向的对象之后的下一个连续对象,如果有任何这样的对象的话。不可能递增数组 - 唯一可以对数组执行的操作是初始化它,使用& 获取其地址,使用sizeof 获取其大小,并将其评估为指向其第一个元素的指针。你对数组所做的一切都是通过这样的指针发生的。
  • 您可以以相同的方式访问指针和数组,但数组不能迭代其元素。要在数组中移动,您需要将其转换为指针。
  • “一张千言万语的图片”。在这种情况下再真实不过了。该图有助于清楚地解释一切。谢谢!!!
【解决方案2】:

字符串文字隐式转换为char const*

花括号初始化器没有。

与您的示例无关,但值得了解:直到并包括 C++03,字符串文字也可以隐式转换为 char*(没有 const),以便与旧 C 兼容,但在 C+ 中很高兴+11 这个不安全的转换终于被移除了。

【讨论】:

  • 我忘记了 C++11 删除了它。哈利路亚!
【解决方案3】:

第一个 sn-p 不起作用的原因是编译器将字符序列重新解释为指针的值,然后忽略其余的初始化程序。为了使 sn-p 工作,您需要告诉编译器您正在声明一个数组,并且该数组的元素本身就是数组,如下所示:

const char *name[] = {(char[]){'a',0},(char[]){'b',0},(char[]){'c',0},0};

进行此修改后,您的程序可以运行并产生所需的输出 (link to ideone)。

【讨论】:

    【解决方案4】:

    您的第一个示例声明了一个指向 char 指针的指针。第二个声明了一个指向 char 的指针数组。不同之处在于第一个间接层多了一层。没有图纸,有点难以描述。

    以假的组装风格,

     char **name = {{'a',0},{'b',0},{'c',0},0};
    

    会翻译成这样的:

    t1:  .byte 'a', 0
      .align somewhere; possibly somewhere convenient
    t2:  .byte 'b', 0
      .align
    t3:  .byte 'c', 0
      .align
    t4:  .dword t1, t2, t3, 0
    name:  .dword t4
    

    第二个,

         char *name[] = {"a","b","c"};
    

    可能会为 t1、t2 和 t3 生成相同的代码,但会这样做

    name:  .dword t1, t2, t3
    

    这有意义吗?

    【讨论】:

      【解决方案5】:

      数组作为连续的对象序列存储在内存中,其中该对象的类型是数组的基本类型。因此,对于您的数组:

      const char *name[] = {"a","b","c"};
      

      数组的基本类型是const char *,数组的大小是3(因为你的初始化器有三个元素)。它在内存中看起来像这样:

      | const char * | const char * | const char * |
      

      请注意,数组的元素是指针——实际的字符串并不存储在数组中。这些字符串中的每一个都是一个字符串文字,它是一个char 的数组。在这种情况下,它们都是两个 chars 的数组,所以在内存的其他地方你有三个未命名的数组:

      | 'a' |  0  |
      | 'b' |  0  |
      | 'c' |  0  |
      

      初始化器将name 数组的三个元素设置为指向这三个未命名数组的初始元素。 name[0] 指向'a'name[1] 指向'b'name[2] 指向'c'

      【讨论】:

        【解决方案6】:

        你必须看看当你声明一个变量时会发生什么,以及存储变量数据的内存在哪里。

        首先,简单写是什么意思:

        char x = 42;
        

        您获得了足够的字节来在堆栈上保存一个字符,并且这些字节被设置为值 42。

        其次,当你声明一个数组时会发生什么:

        char x[] = "hello";
        

        您在堆栈上获得 6 个字节,它们被设置为字符 h、e、l、l、o 和零值。

        现在如果你声明一个字符指针会发生什么:

        const char* x = "hello";
        

        “hello”的字节存储在静态内存中的某个位置,并且您获得了足够的字节来保存堆栈上的指针,并且它的值设置为该静态内存的第一个字节的地址,该地址保存的值字符串。

        那么当你像第二个例子一样声明它时会发生什么?你得到三个单独的字符串存储在静态内存中,“a”、“b”和“c”。然后在堆栈上,你会得到一个由三个指针组成的数组,每个指针都设置为这三个字符串的内存位置。

        那么你的第一个例子是什么?看起来你想要一个指向指针数组的指针,但问题是这个指针数组会去哪里?这就像我上面的指针示例,应该在静态内存中分配一些东西。然而,碰巧你不能像这样使用大括号初始化在静态内存中声明一个二维数组。因此,您可以通过将数组声明为函数外部的变量来做您想做的事情:

        const char* name_pointers[] = {"a", "b", "c"};
        

        然后在函数内部:

        const char** name = name_pointers;
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-11-25
          • 1970-01-01
          • 2020-08-01
          • 2012-07-21
          • 1970-01-01
          • 1970-01-01
          • 2013-11-09
          • 1970-01-01
          相关资源
          最近更新 更多