2014-09-27 43 views
0

我想了解C編程中的指針和字符數組。 我有一個名爲球員結構定義爲這樣:C解引用指針中的分段錯誤

typedef struct player{ 
    char* name; 
    float ppm; 
} player; 

而且在主要我有以下幾點:

int main() 
{ 
    player* head = (player*) malloc(sizeof(player)); 
    char* meName = &(head->name); 
    (*head).name = "potato"; 
    (*head).name = "Paul"; 
    (*head).ppg =7.6; 
    //printf("player is named %s\n", *meName); //First print 
    printf("player is named %s\n", meName); //second print 
    printf("player is named %s\n", (*head).name); //third print 
    return 0; 
} 

爲什麼我的第一次印刷導致段錯誤,爲什麼做我的其他兩個印記輸出如下:

player is named [email protected] 
player is named Paul 

如果我沒有記錯meName應指向,然後由使用另一個指針改變的存儲地址到相同的內存地址。事實上,如果我將其打印在第二張照片中,則會顯示該地址。當我清楚地將變量設置爲指向剛剛被修改的內存空間時,爲什麼取消引用會導致segFault?爲什麼它不會更改爲Paul?爲什麼它會首先拋出段錯誤?

此外,我會很感激解釋箭頭 - >與*用於取消引用指針。

+0

[不投出malloc(和朋友)](http://stackoverflow.com/q/605845)的結果。另外,'(* head).name'等價於更好的'head-> name'。這個表達式中的括號是混亂的:'&(head-> name)'。無論如何,'meName'具有'char *'類型,而不是'char **'。我的建議:用完整的警告編譯你的代碼,並全部留意。 – Deduplicator 2014-09-27 15:53:48

回答

1

首先關閉所有你用printfs使用%S碼。這意味着第二個參數將被解釋爲指向表示字符的字節(即地址)的指針。 Printf將打印該字節的值(作爲字符),然後打印存儲器中下一個字節的值,依此類推,直到遇到一個值爲0的字節。

我會將您的打印語句一次。

printf("player is named %s\n", *meName); 

*meName指在位置meName的字符。這個字符將是一個介於-128和+127之間的整數。 (實際上它是107--見下文)現在你使用這個數字107,就好像它是一個以空字符結尾的字符串(的第一個字符)的地址一樣。機會是這個號碼不是有效地址,所以當printf試圖讀取該地址(107)處的字符時,它不在你的進程的內存範圍內,因此你會得到分段錯誤。 (通常是分段錯誤意味着你已經嘗試過在你的進程不允許訪問的地址讀取或寫入內存。)

printf("player is named %s\n", meName); 

這是更好,因爲meName至少是一個指針。那麼它指向什麼。初始化

meName = &(head->name); 

意味着meName是(即指向)的字段head->name地址。請注意,這實際上並不是一個字符,它是一個指向字符的指針。那麼會發生什麼printf打印head->name的第一個字節就好像它是一個字符。它恰巧打印爲k,因此第一個字節是107(k的ascii號碼)。然後它會打印指針的第二個字節,依此類推,直到遇到一個字節爲00000000的字節爲止。爲什麼編譯器接受初始化並不清楚,因爲左側的類型是「指向字符的指針」,而在右邊的類型是「指向字符的指針的指針」。它應該產生至少一個警告錯誤。

三printf的

printf("player is named %s\n", (*head).name); 

這看起來在地址head和發現的結構。該結構的name字段的值是五字節序列的第一個字節的地址'P''a''u''l''l' 0. printf在該指針值後面找到'P'(它將打印),然後它增加指針並遵循它再次找到'a',依此類推,直到找到0字節,並停止。

請注意,因爲printf有點特殊,它的參數不是由編譯器檢查類型。這就是爲什麼即使第二個參數的類型不是char*,因爲%s指令指示它應該是第一個printf。

+0

我忘了解決'head-> name'與'(* head).name'的用法。他們的意思是*完全一樣的東西*。大多數人更喜歡'head-> name',因爲它稍微容易閱讀,並且輸入起來稍微容易一些。 – 2014-09-27 16:40:01

+0

這很有道理。所以當我想要聲明一個新的「字符串」時,等號左邊的項應該是_pointer_,而右邊的項應該是實際的字符集?所以如果我說char數組[] =「保羅」那麼數組現在是指向字符'P'的內存地址的指針。是? – farid99 2014-09-27 16:44:42

+0

不完全。在大多數情況下,像「Paul」這樣的字符串就代表了一個字符數組。但是在C中,一個字符數組很容易轉換爲一個char指針。在某種意義上,你的賦值'(* head).name =「Paul」對於'(* head).name =&(「Paul」)[0]'來說是簡短的,這意味着第一項那5個字符數組被分配給指針'head-> name'。但是有一個例外;字符串文字在用於初始化數組時具有完全不同的含義:當您聲明'char array [] =「Paul」;'時,這是'char array [5] = {'P','a','的u」, 'L',0};'。 – 2014-09-27 17:56:53

3

這是不正確的,你應該得到一個警告:

char* meName = &(head->name); 

meName是一個指針的指針char,不是簡單的一個指針char。這裏是你如何解決這個問題:

當然
char** meName = &(head->name); 

印刷應該是這樣的 - 即你已經註釋掉的方式:

printf("player is named %s\n", *meName); 
+0

所以,當我做_ **'head-> name' ** _時,這是返回一個指向字符串「name」的指針,當我通過使用&符號來獲取地址時,我得到一個指向指針的指針。這是怎麼回事?在那種情況下,爲什麼不_ **'char * meName = head-> name;'** _ compile要麼?在這種情況下,_ **'meName'** _是一個指向char的指針,並且_ **'head-> name' ** _也是一個指向_ **'char' ** _的指針,如果我我沒有錯。 – farid99 2014-09-27 16:07:30

+0

我並不是故意要用雙星號。評論的格式不在我身上。 – farid99 2014-09-27 16:13:32

+0

@ mufasa56' - >'運算符引用'head',給你一個指向'char'的指針。 「接收地址」&'操作符使其成爲指向char的指針。 – dasblinkenlight 2014-09-27 16:18:36

0
int main() 
{ 
    player* head = (player*) malloc(sizeof(player)); 
    char* meName = &(head->name); 
    (*head).name = "potato"; 
    (*head).name = "Paul"; 
    (*head).ppg =7.6; 
    //printf("player is named %s\n", *meName); //First print 
    printf("player is named %s\n", meName); //second print 
    printf("player is named %s\n", (*head).name); //third print 
    return 0; 
} 

此代碼有嚴重的錯誤類型(其中有些會被編譯器,如果你使用適當的警告級別被抓),並且不使用的最佳實踐(和也因錯別字不會編譯)。對比它

int main(void) 
{ 
    player* head = malloc(sizeof *head); 
    char** pname = &head->name; 

    head->name = "potato"; 
    head->name = "Paul"; 
    head->ppm = 7.6; 

    printf("address of player name is %p\n", pname); 
    printf("player is named %s\n", *pname); 
    printf("ditto: %s\n", head->name); 

    return 0; 
}