const char ** name VS char * name []

时间:2012-11-15 00:31:39

标签: c arrays pointers char

我知道这个主题已经讨论了好几次了,我想我基本上知道了数组和指针之间的区别,但我对如何将数组完全存储在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

一切都很顺利。

6 个答案:

答案 0 :(得分:4)

定义类似

的变量时
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 |
          +---+     +---+---+

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

答案 1 :(得分:2)

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

花括号初始化程序没有。

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

答案 2 :(得分:1)

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

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

通过这种修改,您的程序可以工作并生成所需的输出(link to ideone)。

答案 3 :(得分:1)

您的第一个示例声明了指向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

这有意义吗?

答案 4 :(得分:1)

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

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

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

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

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

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

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

答案 5 :(得分:1)

您必须查看声明变量时会发生什么,以及存储变量数据的内存。

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

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;