2012-12-05 173 views
2

我有下面的代碼,它只是打印出一個人的名字的介紹。C指針參考

#include <stdio.h> 
#include <stdlib.h> 

typedef struct { 
    char* firstname; 
    char* lastname; 
}Person; 

void intro(void *person){ 
    printf("The person you are looking for is %s %s\n", ((Person *)person)->firstname, ((Person *)person)->lastname); 
} 

int main() 
{ 
    Person *a = NULL; 
    a = (Person *)malloc(sizeof(Person)); 

    char *first = NULL, *last = NULL; 
    first = (char *)malloc(sizeof(char)*20); 
    strncpy(first,"Bob", 20); 
    last = (char *)malloc(sizeof(char)*20); 
    strncpy(last,"Newmonson", 20) 
    a->firstname = first; 
    a->lastname = last; 

    intro(a); 

    return 0; 
} 

產生輸出

The person you are looking for is Bob Newmonson

但是改變intro(a)intro(&a)產生

The person you are looking for is �@ Newmonson

當我打開GDB的第一次嘗試,並在第10行打破我找到地址person=0x601010。名字和姓氏都存儲在我預期的地方,0x04006b90x4006bd,因爲它們是在堆棧中早些時候聲明的。

什麼讓我是當我運行GDB與intro(&a)的變化。 person的地址現在是0x7fffffffffdd38,第一個名字指向0x601010,姓氏指向0x4006db

任何人都可以幫助解釋是怎麼回事,爲什麼我仍然可以在第二次測試中訪問姓氏的正確地址。

編輯:

至於大家似乎一直在問關於它的void *是這個代碼,我沒有包括的螺紋部分。

+4

FYI:'第一=(的char *)malloc的(的sizeof(char)的* 20);第一個=「鮑勃」;「沒有做你認爲正在做的事情。 –

+1

另外,爲什麼當聲明'intro()'是否只有一個''* *'的時候,有一個希望與它一起工作的'void *'? –

+0

@MichaelBurr我想用'void *'來進行練習,但碰到了這個問題。 – Blackninja543

回答

4

這是因爲a已經指向Person結構;因此intro(&a)會將指針傳遞給該指針,但intro()將其參數視爲指向Person的指針。

此外,如果intro()打算在人員上工作,則應聲明Person *參數,而不是void *

+0

'void *'位是巧合的,因爲它最初與這個問題無關。 – Blackninja543

+0

@ Blackninja543:實際上,'void *'是重要的 - 如果函數被更好地聲明,編譯器在使用'intro(&a)'時會警告你指針類型不匹配。應該避免使用'void *' - 它需要的是一個大紅旗,因爲某些事情是錯誤的。 –

3

指針變量a內部保存的地址與a佔用的實際內存位置的地址不同。

0x601010這樣的地址是一個「低」內存地址,並且通常會在堆上的某個地方。像0x7fffffffffdd38的地址是一個很「高」的地址,通常會在堆棧中。所以&a是給你堆棧上的變量a的實際地址,它是傳遞一個值的函數,而不是價值0x601010存儲指針變量a內,並表示被分配的內存緩衝區的首地址從返回malloc

0

首先:

/* allocate space for 20 characters and make 'first' point to that space. */ 
first = (char *)malloc(sizeof(char)*20); 
/* now make 'first' point to the string Bob. Leak the memory that was allocated before. */ 
first = "Bob"; 
/* Rinse, lather, repeat. */ 
last = (char *)malloc(sizeof(char)*20); 
last = "Newmonson"; 

我們解釋爲什麼使用intro(&a)給你意想不到的結果:

intro預期的指針,它假設點Person型的結構。您創建一個指向Person的指針,然後爲其分配空間。

調用intro(a)使指針Person到作爲指針以void引導頁,然後將其視爲一個指針Person和一切正常傳遞。

調用intro(&a)但需要的a地址並通過在再次,intro試圖把它當作一個指向Person但不會工作,因爲你已經通過一個指針的指針一個Person。你正在將西紅柿傳遞給認爲它變成橘子的功能。兩種都是水果,你會得到果汁,雖然你可能不會很高興,當你的早餐與美味的番茄汁,而不是美味的橙汁服務。

如果你問爲什麼調用intro(&a)會導致名字被破壞,但要打印的姓氏,答案是因爲運氣好:帶姓的字符串恰好在內存中的正確位置。

0

試試這個:

#include <stdio.h> 
#include <stdlib.h> 

typedef struct { 
    char* firstname; 
    char* lastname; 
}Person; 

void intro(Person *person){ 
    printf("The person you are looking for is %s %s\n", (person)->firstname, (person)->lastname); 
} 

int main() 
{ 
    Person *a = NULL; 
    char *first = NULL, *last = NULL; 
    a = (Person *)malloc(sizeof(Person)); 


    first = (char *)malloc(sizeof(char)*20); 
    first = "Bob"; 
    last = (char *)malloc(sizeof(char)*20); 
    last = "Newmonson"; 
    a->firstname = first; 
    a->lastname = last; 

    intro(a); 

    return 0; 
} 

希望它可以幫助...

+0

你爲什麼要將'Person *'投射到'Person *'? –

+0

我知道它不必要,但我相信它的好習慣,平靜編譯器:P – omi

+0

再來一次?我不知道這意味着什麼。 –