2012-08-31 47 views
2

我有這樣的代碼:無法理解爲什麼這種行爲


    char *name[] = { "a1", "b2", "c3", "d4" }; 
    printf("%s\n", *name); //the critical line 

相關的critical line

在這種形式下,輸出很簡單:a1。 如果我更換critical line

printf("%s\n", ++*name);

然後輸出爲1。我認爲直到現在一切都很好。

考慮在帳戶name是一個指向字符的第一個字符串,分別"a1",我更換critical line有:

printf("%s\n", ++name);

,希望我會得到"b2"結果作爲輸出。但我得到這個錯誤:

../src/test.c:32: error: lvalue required as increment operand

問題:我不明白爲什麼++*name是合法的 - name是一個指向字符的第一串 - 並++name不是。在我看來,++name應該將name移動到下一個字符串。任何人都可以解釋我的內褲缺乏嗎?

回答

3

當寫++name,所述陣列name被轉換爲指針到所述陣列的第一個元素。此轉換的結果不是左值,並且不能用++或其他方式進行修改。你可以改爲寫name+1,這會打印正確的東西。當name是一個數組時,無法修改它以引用除該數組[*]以外的任何其他數據。

也考慮:

char **p = name; // OK, `name' converts to pointer 
++p;    // OK, `p' is an lvalue 
++(p+1);   // not OK, `p+1' is not an lvalue 
++((char**)p);  // not OK, `(char**)p' is not an lvalue 

++*name;   // OK, `*name' is an lvalue 

粗略地講,一個「左值」是指物體的表達,而「不是左值」是具有值的表達式。對象和值之間的區別在於對象是存儲值的地方(以及一次一個值)。值永遠不能被修改,對象有時可以。

只要你有一個左值的子表達式,但是它的當前值是需要的,那麼這個對象就會被讀取/加載/你想要調用它的任何東西。在C++中,這被稱爲「右值轉換的左值」,除了「評估子表達式」之外,我不記得它是否在C中調用任何東西。

[*]你可以用另一個變量name來隱藏它的內部範圍,它指的是別的東西。但這仍然沒有修改外部name,只是暫時隱藏它。

+0

請參閱我對@ Kerrek的回覆的評論。 – artaxerxe

2

name是數組,所以,當使用它作爲sizeof&運營商的操作數,它是作爲一個指針陣列客體的初始構件評價除了和是不是左值

因此,您不能直接修改name,如++(請記住後綴增量運算符需要一個可修改的左值作爲操作數)。否則,您可以使用臨時指針(在以下示例中爲p)。

#include <stdio.h> 

const char *name[] = { "a1", "b2", "c3", "d4" }; 
const char **p = name; 
printf("%s\n", *p); /* Output: a1 */ 
*++p; /* or *p++ */ 
printf("%s\n", *p); /* Output: b2 */ 
+0

是的,我完全同意。我只是想強調,'我'需要是一個可修改的左值才能執行此操作。注意:我已將其刪除。 – md5

1

首先,請確保您完全滿意以下事實:數組不是指針。

二,什麼是name?數組衰減成爲指向第一個元素的指針。衰變後,表達式name的類型爲char **(指向數組char*的第一個元素,然而,衰減的表達式是的右值。您無法修改它!當然,因爲修改指針是沒有意義的一個指針到固定陣列。

因此,不能直接遞增指針,它是衰減name,不超過可以說++5++foo()(其中foo返回由值基本類型) [結果哎呀,那是一個讓步於C++的讓步]

什麼你可以說的是:

char ** np = name; 
++np; 
printf("%s", *np); 

這與印刷name[1]同樣的效果,但現在你也有具有指向第二個數組元素的變量。

+0

「腐爛的表情是一個右值」 - 或用C標準術語,它是「表達的結果」。愚蠢的C標準術語。同樣,在C中,函數調用'foo()'不能返回一個左值。 –

+0

即使是我的代碼,我執行你的代碼後,我會得到二進制字符串,如下所示:$ ' * @ 而不是'「b2」'。什麼解釋? – artaxerxe

+0

@artaxerxe:我無法重現:http://ideone.com/S63K7。也許你運行的代碼還有其他問題。 –

1

雖然名稱指向第一個元素的地址,但名稱不是type char *,而是char *[4]。因此,sizof(name) == sizeof(char *)*4

增加一個指針總是意味着添加它指向的數據的大小。所以,增加後,它指向整個陣列後面。就像如果你有

int i, a; 
    int *p = &i; 
    p++; 

P中就指向背後我。它會指向一個,如果編譯器決定把我放在後面。

另請注意,您的數組只包含4個指針,而不是4個字符串。像上面那樣,這些字符串實際上就是編譯器的選擇。所以,第一個字符串的結尾不一定要在第二個字符串的開頭。特別是如果您稍後將其他值(字符串地址)分配給名稱[1]。因此,您可以將名稱轉換爲char **,但不應將其轉換爲char *。增加前者會指向第二個元素(第二個char *指針)。