2012-11-15 111 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;