2016-02-12 232 views
3

在這裏有關於SO指針的無數問題,以及互聯網上無數的資源,但我仍然無法理解這一點。指針的類型?

This answer報價A Tutorial on Pointers and Arrays in C: Chapter 3 - Pointers and Strings

int puts(const char *s); 

目前,忽略常量。傳遞給puts()的參數是一個指針,它是一個指針的值(因爲C中的所有參數都是按值傳遞的),而指針的值就是它指向的地址,或者簡單地說就是一個地址。因此,當我們按照我們所見寫puts(strA);時,我們傳遞的地址是strA[0]

我根本不明白這一點。

  • 爲什麼puts()需要指針到字符串常量? puts()不修改並返回它的參數,只是將它寫入stdout,然後該字符串被丟棄。

  • 忽略爲什麼,它是如何puts()的原型,它明確地需要一個指針到字符串常量,接受一個字符串文字,而不是指向一個?也就是說,爲什麼puts("hello world");工作時puts()的原型會顯示puts()需要更像char hello[] = "hello world"; puts(&hello);

  • 如果你給,例如printf()一個指針爲一個字符串常量,這顯然是它想要什​​麼,GCC會抱怨,你的程序將段錯誤的,因爲:

    error: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘char (*)[6]’ 
    

但給printf()一個字符串常量,不是一個指向字符串的指針,工作正常。

This Programmers.SE question's answers對我很有意義。

回答這個問題的答案,指針只是代表內存中某個位置的數字。內存地址的數字是無符號整數,而C是用本地C和彙編寫的,所以指針只是體系結構定義的uint s。

但這種情況並非如此,因爲編譯器是在其誤差約intint *int **如何同樣很清楚。它們是最終指向記憶中的某個途徑。

爲什麼需要一個指針功能接受的東西是不一個指針,拒絕指針?


我知道「字符串常量」實際上是一個字符數組,但我試圖在這裏簡化。

+1

我已閱讀你的問題。這對我來說有點困惑。你能簡單地告訴我你所期望的嗎? –

+0

@JafferWilson在那裏,我希望我已經說得更清楚 – cat

+2

您可能想要了解一個數組傳遞給函數(C語言)時會發生什麼。 – alk

回答

2

爲什麼puts()需要一個指向字符串常量? puts()不會修改並返回其參數,只是將其寫入標準輸出,然後該字符串將被丟棄。

puts接收指向在字符串的第一個字符;它會「走」下來,直到它看到一個0結束符。一個天真的實施將是這個樣子:

void puts(const char *ptr) 
{ 
    while (*ptr)  // loop until we see a 0-valued byte 
    putchar(*ptr++); // write the current character, advance the pointer 
         // to point to the next character in the string. 
    putchar('\n'); 
} 

忽略的原因,它是如何puts()的原型,它明確地需要一個指向字符串常量,接受一個字符串文字,而不是一個指針一?也就是說,爲什麼puts("hello world");工作時puts()的原型會顯示puts()需要更像char hello[] = "hello world"; puts(&hello);

除了當它是sizeof或一元&操作者的操作數,或是一個字符串被用來在聲明初始化另一個陣列,類型的表達「的T N元件陣列」,將被轉換(「衰減」)爲「指向T的指針」類型的表達式,並且表達式的值將是該數組的第一個元素的地址。

字符串文字以char(在C++中爲const char)的數組存儲;因此,字符串文字"hello world"是「char」的「12元素數組」類型的表達式。當您調用puts("hello world");時,字符串文字不是sizeof或一元&運算符的操作數,因此表達式的類型被轉換爲「指向char」的指針,並且表達式的值是第一個字符的地址串。

如果你給,例如printf()一個指向字符串常量,這顯然是它想要什​​麼,GCC會抱怨,你的程序將段錯誤的,因爲:

error: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘char (*)[6]’

請記住,上面的數據表達式轉換爲指針類型,除了,它是sizeof或一元運算符&的操作數,或用於在聲明中初始化另一個數組。假設聲明

char hello[] = "hello world"; 

上面一樣,表達式"hello world"有類型的char 12個元素的陣列;但是,因爲它正用於在聲明中初始化另一個char數組,它不會轉換爲指針表達式;相反,字符串文字的內容被複制到hello陣列。

同樣,如果調用printf如下:

printf("%s", &hello); 

則表達式hello轉換爲指針char;相反,表達式&hello的類型是「指向12元素陣列的char」或char (*)[12]。由於%s轉換說明需要一個char *,你應該只傳遞數組表達

printf("%s", hello); 

,並用字符串文字,只使用文字:

printf("%s", "hello world"); 

去關閉這個問題的答案,指針只是代表記憶中某個位置的數字。內存地址的數字是無符號整數,而C是用本地C和彙編寫的,所以指針只是體系結構定義的uint s。

但事實並非如此,因爲編譯器在關於intint *int **如何不一致的錯誤中非常清楚。它們是最終指向記憶中的某個途徑。

C是(或多或少)強類型語言;類型問題。即使一個intint *,和int **可以佔用的空間的相同量的存儲器,語義它們是非常不同的東西和(通常)不能互換。指向int的指針是與指向float的指針不同的類型,該指針是從指向數組char等的指針的不同類型,這對於諸如指針算術之類的事情而言是重要的;當你寫

T *p = some_address(); 
p++; 

表達p++前進p指向T類型的下一個對象。如果sizeof (T)爲1,則p++前進一個字節;如果sizeof (T)爲4,則p++前進4個字節(假設我們大多數人都在使用字節尋址體系結構)。


1.是否。不能保證指向不同類型的指針具有相同的大小或表示形式,也不能保證它們只是無符號整數;在分段架構上,它們可能具有更復雜的表示。

1

int ** r = 90; r是一個雙指針,並且你正在爲指針分配90。當你解除引用時,它會嘗試解引用地址0x90。

2

這裏有幾個問題,但希望我能說明如何指針指針工作。

原因puts需要一個指針,是C真的沒有內置類型的字符串。一個字符串是一個接一個的一堆char。因此,puts需要一個指向第一個字符的指針。

字符串文字「優雅地降低」到指針。這是一個花哨的編譯器,這意味着字符串文字實際上是一串字符,並且由指向第一個字符的指針表示。

你需要一個指向指針類型,例如,如果你想從一個函數「返回」一個數組,像這樣:

bool magic_super_function(int frob, int niz, char** imageptr /* pointer to pointer */) 
{ 
    char* img = malloc(frob * niz * IMAGE_DEPTH); 
    if (NULL == ptr) { 
     return false; 
    } 

    *imageptr = img; 

    return true; 
} 

有時一個例子(甚至做作)可以說明一個點。你會打電話 這個函數像這樣:

char* img; /* pointer to char */ 

if (true == magic_super_function(12, 8, &img /* pointer to pointer (to char)*/ )) { 
    /* Here img is valid and allocated */ 
    /* Do something with img */ 
} else { 
    /* img has no valid value here. Do not use it. */ 
    /* Something failed */ 
} 
+0

'puts'不這樣做。 'puts'返回一個int值來表示它所寫的字符數。 – cat

+0

我問了兩個特定的函數,'printf'和'puts',它們的類型都是'int',這是他們寫的字符串的長度。我不明白他們爲什麼需要一個指針。 – cat

+1

@cat,'puts'做了什麼,或者沒有做什麼,我認爲這一點並不重要? :-)上面的例子是爲了說明如何使用指向指針的指針。 –

3

puts()並不需要一個指向字符串的指針,它需要一個指針(*)的字符(char)。恰巧在C中,指向一個字符(char *)的指針可以被同化爲一個字符串(一個字符數組),前提是該字符串的末尾是空字符\0

+0

這是否意味着'char s []'和'char s'不是不同的類型,只是恰好是指向'char's的指針集合? – cat

+2

不,'char s'是一個char,而不是一個字符數組。 – mouviciel

1

爲什麼puts()需要一個指向字符串常量的指針?

puts()以這樣的方式定義,以便它可以利用實際參數而不是複製它並重新使用它。因爲它提高了性能。而且它需要一個const指針,所以它不能改變指針指向的內容。它是請致電

它是如何puts()的原型,其中明確採用指向 字符串常量,接受一個字符串文字,而不是指向一個?

當您傳遞字符串文字時,首先將字符串文字存儲在只讀存儲器中,然後實際傳遞指向該存儲器的指針。所以你可以撥打電話puts()與任何文字如puts("abcd"),puts("xyz")。它會工作。

error: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘char (*)[6]’

這裏您的實際傳遞指針的6 char小號不是char *陣列。所以編譯器會抱怨這個錯誤。

2

intint*不同,因爲它將如何在代碼中使用。您可以期望訪問int*指向的內存位置並找到一個整數值。這就是所謂的「強類型」,語言這樣做是爲了對使用變量的方式有嚴格的規定。所以即使intint*可能都是相同的大小,int不能用作指針。類似地,int**是指向指針的指針,因此必須解除兩次引用才能找到它所引用的實際整數值。

puts(const char*)的示例中,該函數的定義告訴您該函數需要一個存儲位置(指針),該位置指向一個以NULL結尾的值爲char的值。在執行操作時,puts將取消您提供的位置,並打印在該處找到的字符。 const部分告訴您它不會更改值,以便可以安全地將const陣列char發送給它。當您發送像puts("hello")這樣的文字字符串時,爲方便起見,編譯器會將其轉換爲「hello」指針,因此仍會發送指針(不是字符串的副本)。

關於您對printf的問題,請注意char*char*[6]是不同的。第一個表示一個指向空終止字符串的指針,其中第二個指針指向一組正好六個值的集合,這些值可能不以空值終止。編譯器會抱怨,因爲如果puts(&hello)試圖將輸入參數視爲以空字符結尾的字符串,它不會在數組的長度後停止,並且會訪問它不應該存在的內存。

4

表達式"hello world"的類型爲char[12]

在大多數情況下,使用陣列被轉換爲一個指向它的第一個元素:在"hello world"情況下,它被轉換成一個指針到'h'char*類型。當使用puts("Hello world")時,數組轉換爲char*

請注意,從特定大小的數組轉換,丟失大小信息。

char array[42]; 
printf("size of array is %d\n", (int)sizeof array); 
printf("size of pointer is %d\n", (int)sizeof &array[0]); 
+0

啊,所以*有*轉換。這更有意義,謝謝。 – cat

+0

是的,存在隱式轉換。你不需要做任何事情,它總是執行。最顯着的例外是當數組被用作'sizeof'操作數的操作數時。其他的例外情況是,當一個數組被用作'&(地址)運算符的操作數,以及當用作字符串文字來初始化一個數組對象時。 – pmg