2012-11-15 189 views
2

我知道这个话题已经讨论过好几次了,我想我基本知道数组和指针的区别,但我对数组是如何完全存储在mem中感兴趣。const char ** name VS char * name []

例如:

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 

一切顺利的罚款。

+0

你是什么“这个不行”是什么意思?当你编译并运行第一个例子时会发生什么? –

+0

在我的系统上,这将导致seg错误 –

+3

hmm,甚至不能编译第一个样本 – billz

回答

2

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

大括号初始值设定项没有。

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

+0

我忘了C++ 11删除了那个。哈利路亚! – chris

1

第一个代码片段不起作用的原因是编译器将字符序列重新解释为指针的值,然后忽略其余的初始化器。为了使片段工作,你需要告诉你声明数组的编译器,而该数组中的元素是数组本身,就像这样:

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

有了这个修改的地方,你的程序工作并产生期望的输出(link to ideone)。

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 

这有道理吗?

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'

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 | 
      +---+  +---+---+ 

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

+0

很有意思;它是如何完成的。指针移动到下一个元素?这将意味着一个数组和一个指向数组的指针具有相同的行为 –

+0

@QuicknDirty:这就是如何定义一个指针的增量 - 它使它指向它先前指向的对象之后的下一个连续对象,如果有的话这样的对象。无法增加一个数组 - 对数组的唯一处理是对数组进行初始化,使用'&'取其地址,用sizeof'取其大小,并将其评估为指向其第一个元素的指针。你用数组做的其他事情都是通过这样一个指针发生的。 – caf

+0

你可以用相同的方式*访问一个指针和一个数组,但是一个数组不能通过其元素迭代*。要移动数组,您需要将其转换为指针。 –

1

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

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

char x = 42; 

你得到足够的字节来保存在栈上一个字符,这些字节的值设置为42

其次,什么

char x[] = "hello"; 

你在栈上的6个字节,并且它们被设置到字符H,E,L,L,O,和零值:当您声明一个数组发生。

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

const char* x = "hello"; 

为“你好”是什么地方存储在静态存储器,你会得到足够的字节来保存在栈上的指针,其值的字节被设置为该静态存储器的第一个字节的地址,该字节保存该字符串的值。

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

那么你的第一个例子试图做什么?它看起来像你想要一个指针数组的指针,但问题是这个指针数组在哪里去?这就像我上面的指针示例,应该在静态内存中分配内容。但是,恰好你不能在静态内存中使用大括号初始化来声明一个二维数组。所以,你可以做你想做的通过声明数组作为函数的变量外:

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

然后在函数内部:

const char** name = name_pointers;