2013-06-22 217 views
3

的陣列我很困惑,以下通道如何與它後面的代碼相匹配:argv的指針的指針

由於argv的是一個指針,指向指針的陣列,我們可以操縱 指針而不是索引數組。下一個變體是基於 遞增argv,它是一個指向字符指針,而ARGC 倒計時:

#include <stdio.h> 
/* echo command-line arguments; 2nd version */ 
main(int argc, char *argv[]) 
{ 
    while (--argc > 0) 
     printf("%s%s", *++argv, (argc > 1) ? " " : ""); 
    printf("\n"); 
    return 0; 
} 

是不是char *argv[]只是一個指針數組?不會將指向數組的指針寫爲char *(*argv[])或類似的東西嗎?

作爲一個方面說明,一般情況下,我發現混合數組和指針的聲明相當混亂嗎?

回答

7

術語「指向數組的指針」或「指向數組」通常在C術語中處理得相當鬆散。他們至少可以有兩種不同的東西。

在最嚴格和迂腐的術語的意義上,「指針數組」具有與「指針數組」類型進行聲明,如在

int a[10]; 
int (*p)[10] = &a; 

在上面的例子p被聲明爲一個指向數組10 int s的指針,它實際上被初始化爲指向這樣一個數組。

但是,這個術語也常被使用的是它的不太正式的含義。在這個例子中

int a[10]; 
int *p = &a; 

p被聲明爲單純的指針int。它被初始化爲指向數組a的第一個元素。在這種情況下,你經常可以聽到和看到人們說p也「」指向「數組」,儘管這種情況在語義上與前一種情況不同。在這種情況下,「指向數組」指的是「通過指針算術提供對數組元素的訪問」,如p[5]*(p + 3)

這正是「... argv是指向指向數組的指針......」的意思。argv的在main參數列表聲明等效於char **argv,這意味着argv實際上是一個指向char *指針。但是因爲它物理地指向某個char *指針數組的第一個元素(由調用代碼維護),所以半非正式地說argv指向一個指針數組是正確的。

這就是您引用的文本的意思。

0

Since argv is a pointer to an array of pointers

這是錯誤的。 argv是一個指針數組。

+0

來這裏張貼,所以+1你。另外,將它寫爲'char * argv []'可能更有意義,因此讀取指針數組更容易。 – austin

+1

不,這也是錯誤的,'argv'是指向指針的指針。考慮'sizeof()',將'argv'變量賦值給別的東西等等...... – 2013-06-22 20:12:32

+0

@ H2CO3 C井把'argv'指向一個指針數組。但嚴格來說是的,正式的'main'參數被調整爲指向指針的指針。 – ouah

0

由於argv的是一個指針,指向指針數組,

否,即使不靠近。

是不是char *argv[]只是一個指針數組?

不,它是指向指針的指針。

2

在C函數聲明接受數組的地方,他們嚴格地接受指針。該語言不區分void fn(int *foo) {}void fn(int foo[])。它甚至不關心你是否有void fn(int foo[100]),然後傳遞一個int [10]的數組。

int main(int argc, char *argv[]) 

相同

int main(int argc, char **argv) 

因此,argv點的char指針的數組的第一個元素,但它本身不是一個數組類型,它不(正式)指向整個陣列。但是我們知道數組在那裏,我們可以通過索引來獲取其他元素。

在更復雜的情況下,如接受多維數組,它只是第一個回退到指針(並且可以保持未定義狀態)的[]。其他的仍然是被指向的類型的一部分,並且它們對指針算術有影響。

+0

@DanielFischer,哎呀!將修復,謝謝。 – sh1

0

「的指針數組的第一元素」是一種常見的構建體。每個字符串函數都使用它,包括輸入和輸出字符串的stdio函數。主要用於argv。

「指向數組的指針」是一種罕見的構造。我無法在C標準庫或POSIX中找到它的任何用處。 grepping我已經在本地安裝的所有頭文件(對於'([^)]*\*[^)]) *\[')我找到了指向數組的指針的兩個合法實例,一個在libjpeg中,一個在gtk中。 (兩者都是struct成員,而不是函數參數,但那不在功能上)

所以,如果我們堅持官方語言,我們有一個罕見的事情,用一個短名稱和一個類似的,但更常見的事情與一個長名稱。這與人類語言自然而然地想要工作的方式相反,所以存在緊張局勢,通過使用短名稱「不正確」,除了最正式的情況之外,所有情況都會得到解決。

我們不只是說「指針,指針」的原因是,有另一種常見的使用指針作爲函數的參數,其中參數指向一個單一的對象,這不是一個數組中的一員。例如,在

long strtol(const char *nptr, char **endptr, int base); 

endptr是完全一樣的類型argvmain,都是指針到指針,但他們以不同的方式使用。 argv指向char * s中的第一個char *;在內部主要你需要使用索引argv[0],argv[optind]等,或者通過增加++argv來增加數組。

endptr指向一個單一char *。在strtol之內,增加endptr或參考endptr[n]對於非0的任何值n都沒有用處。

這種語義差異表達爲「argv是指向數組的指針」的非正式用法。在形式語言中可能與「指向數組的指針」意味的混淆被忽略了,因爲使用簡潔語言的自然本能比堅持正式定義的願望更強烈,因爲它保留了不使用最明顯的簡單短語對於幾乎不會發生的情況。

2

數組指針等價的東西只成立函數參數,因此,儘管void fn(const char* argv[])void fn(const char** argv)是等價的,但它不是真實的,當涉及到的變量,你可能想傳遞給函數。

考慮

void fn(const char** argv) 
{ 
    ... 
} 

int main(int argc, const char* argv[]) 
{ 
    fn(argv); // acceptable. 

    const char* meats[] = { "Chicken", "Cow", "Pizza" }; 

    // "meats" is an array of const char* pointers, just like argv, so 
    fn(meats); // acceptable. 

    const char** meatPtr = meats; 
    fn(meatPtr); // because the previous call actually cast to this,. 

    // an array of character arrays. 
    const char vegetables[][10] = { "Avocado", "Pork", "Pepperoni" }; 
    fn(vegetables); // does not compile. 

    return 0; 
} 

「蔬菜」不是一個指針的指針,它直接指向在3×10個連續字符序列的第一個字符。在上述替換FN(蔬菜),以獲得

int main(int argc, const char* argv[]) 
{ 
    // an array of character arrays. 
    const char vegetables[][10] = { "Avocado", "Pork", "Pepperoni" }; 
    printf("*vegetables = %c\n", *(const char*)vegetables); 

    return 0; 
} 

,輸出是「A」:蔬菜本身直接指向 - 無需間接 - 的字符,而不是中間指針。

蔬菜的分配基本上是這樣一個快捷方式:

const char* __vegetablesPtr = "Avocado\0\0\0Pork\0\0\0\0\0\0Pepperoni\0"; 
vegetables = __vegetablesPtr; 

const char* roni = vegetables[2]; 

轉化爲

const char* roni = (&vegetables[0]) + (sizeof(*vegetables[0]) * /*dimension=*/10 * /*index=*/2);