C 指針傳遞變數為什麼無法修改變數值?
題目要求就是給以下函數糾錯:
答案是這樣的: 傳入中GetMemory(char *p )函數的形參為字元串指針,在函數內部修改形參並不能真正的改變傳入形參的值,執行完後的str仍然為NULL;可是我的理解是: 當指針作為函數的參數進行傳遞的時候,本質上還是進行的「值傳遞」,也就是複製了一個新的指向該地址的指針變數, 那麼我在函數中在堆上給該地址分配一塊內存, 難道函數外就沒有了嗎?
void GetMemory( char*p )
{
p = (char*) malloc( 100 );
}
void Test( void )
{
char*str = NULL;
GetMemory( str );
strcpy( str, "hello world" );
printf( str );
}
T_T 求指導, 我的理解有什麼問題..
我以前學習的時候也陷入到過這樣的誤區,所以我想分享一下我後面理解與解決的方式,希望題主也能理解到。我覺得有關指針的問題,其實最好的理解方式莫過於繪圖。
從Test函數執行開始,str指向的是NULL
然後經過GetMemory函數以後,在這裡發生了一次賦值,即 char *p = str;所以,p指向了(str指向的NULL),於是就變為了如此
然而經過了GetMemory的malloc以後,p獲得了一塊新內存的地址,於是p的指向就變為了這樣
那麼,從這裡可以看出來str與p就分道揚鑣了,而str也還是指向NULL。那麼,如何修改呢?一個是C++的引用,另外一個就是二重指針,即把GetMemory變為 GetMemory(char ** p) 來保存str的地址。
那麼,針對這兩種情況,樓主是否可以嘗試這樣的繪圖,然後來解釋為什麼這樣就可以做到了呢?(我當初也是在理解二重指針的時候遇到了這樣的問題,然後通過繪圖理解了為什麼可以,所以題主也可以試試)題主,你可以這樣簡化一下,會更容易看懂(既然是C++,我就用C++了,你的題目是C):
int * p = nullptr; // 空指針,類似C里的NULL
int * q = p; // 此時p和q都是nullptr 這裡的q你可以把它視作GetMemory里的參數q = new int; // q不再是nullptr,而p仍然是也就是說,你一開始有倆毫不相干的空指針,然後你給其中一個分配了空間,另一個當然一直沒變過。這代碼就是純粹的垃圾,硬要糾錯的話,糾完你也可以準備下家面試了。。。。
很多人硬要問我糾錯,那就更新一下。
====================== 硬是要糾錯的話,至少有如下幾點 1,java 名字 sb 2,指針參數檢查 3,錯誤處理和返回和檢查 4,strcpy 函數是不安全的,現在已經不應該用了 5,free 6,格式化輸出應用字面量字元串我就不說沒 main 函數這些了。。。
你說,我面試的時候如果這麼說,對方會不會覺得特沒面子,會不會覺得你這人怎麼這麼事兒呀,是不是可以直接準備下一家面試了呢 :hhh&>&>&>&>&>&>&>那就只說對方想聽的標準答案吧,見下圖- str 是一個變數,它的地址是 1,地址 1 中存的數字是 0 (NULL)
- p 是一個變數,它的地址是 3,地址 3 中存的數字是 0 (這一步是把地址 1 中的數字拷貝到地址 3)
- malloc 返回一個數字,這個數字是新申請的內存的首地址,通過 = 將這個數字寫入了變數 p 也就是地址 3 的內存中
所有變數都是一個地址,它的值不過是地址中的那個數字
變數賦值就是複製數字就說這麼多吧,反正只是掏糞的面試和掏糞的工作,畢竟這是 java 出身的碼畜打著 cpp 旗號寫的 c 語言代碼你想要修改參數,就要傳參數的指針同理,你想要修改指針參數,就要傳指針參數的指針另外這tm是C
C++裡面我們一般不用指針,想修改參數我們一般用引用
C函數都是值傳遞,所謂的地址傳遞是靠傳遞指針的值來實現的。
void GetMemory( char**p )
{
*p = (char*) malloc( sizeof(char) * 100 );
}
void Test( void )
{
char*str = NULL;
GetMemory( str );
strcpy( str, "hello world" );
printf( str );
}
我在函數中在堆上給該地址分配一塊內存, 難道函數外就沒有了嗎?
Getmemory函數執行完畢後你就沒有辦法再訪問那塊內存,這就是內存泄漏。
函數調用可以當成內聯了一段代碼,只是最後有個返回值:
void GetMemory( char*p )
{
p = (char*) malloc( 100 );
}
void Test( void )
{
char*str = NULL;
GetMemory( str );
strcpy( str, "hello world" );
printf( str );
}
相當於:
void Test( void )
{
char*str = NULL;
{//引入一層scope
char* p=str;//初始化參數
p = (char*) malloc( 100 );
}
strcpy( str, "hello world" );
printf( str );
}
正好這裡沒有返回值就不去單獨考慮了——寫成這樣你能看懂了吧?
ps:還有就是printf(str)很危險,誰知道str裡面有沒有百分號void GetMemory( char ** p )
{
*p = (char*) malloc( 100 );
}
void Test( void )
{
char*str = NULL;
GetMemory( str );
strcpy( str, "hello world" );
printf( str );
}
應該傳入二級指針,當前修改的值仍是str指向的值,而在子函數中對他的操作是無效的
因為GetMemory裡面的p並不是傳引用,而是傳值。看Windows API的常式就會發現,傳入一個指針變數時從來不會去試圖改變它的值,因為這個改變被調用者是不知道的。傳進指針後你去對他malloc,執行完了這塊內存就是野內存了,這是更大的錯誤建議再抽象一層,用一個結構體把指針裝裡面(Windows API下都是這樣做)。
void GetMemory( char*p )
{
p = (char*) malloc( 100 );
}
·
你從這個函數的形式也能看出來,它是不能為外部的那個傳入參數的src復值的。
因為函數體中寫的是 p=。。。 (在非引用的情況下,是外部參數的「值拷貝」)。換句話說,你賦值的對象,是函數體的 stackframe 裡面的參數部分。而不是 *p=。。。(這是典型為函數外部對象賦值的形式),也等價於 p[0] = ...;
當然如果你寫成引用就可以了:
void GetMemory((char*) p){ p=...;}等價於void getMemory(char **pp)
{*pp=(char*)mallo(...);}在教材上會使用實際參數,形式參數這樣的術語,但是我覺得不是很容易讓初學者分清楚這兩個術語具體指代什麼。。。
當然這裡還有一個給字元串賦值的函數,非常容易和上面的這種情況混淆,比如說:void foo(char* p){strcpy(p, "hello");}char s[16] = "123456";foo(s);這個函數可以修改字元串(的值),不能修改的,是傳入的那個指向該字元串的指針的指向。運行起來大致是這樣的:
char* str = NULL:
此時變數str的值為0。
調用GetMemory(str):
把變數str的值複製到內層棧上的變數p,於是p的值是0。
p = (char*) malloc(xxx);
把malloc分配的首地址,比如0x123456,賦給p。此時p的值為0x123456。
並沒有變數str什麼事。
這裡的關鍵在於,你需要修改變數str。
當然,實際上還有一堆問題,比如:- 分配了固定尺寸的空間,卻使用了strcpy,可以溢出。
- 把字元串直接塞到printf的第一個參數。如果字元串含有任何printf認為是格式的內容,都會崩掉。
const char* resource = "fuck";
/* 駝峰是異端 */
void get_memory(char** ptr, size_t size)
{
*ptr = (char*) malloc(sizeof(**ptr) * size);
}
void Test()
{
char* str = NULL;
get_memory(str, strlen(resource) + 1);
strcpy(str, resource);
printf("%s
", str);
}
你該這麼理解,傳入了指針,或者說是數組的首地址。
如果是數組的首地址,你就能修改這個數組的內容但如果把它看作是指針,指針本質上可以理解為一個整型,那麼傳入一個整型就是值傳遞,拷貝了一次這個指針但沒法修改原來的它。如果想要修改,很簡單,傳指針的地址,int**想在函數內改變一個外部變數的值,你要傳它的指針進去。So, 你要改變一個指針變數的值,你得傳這個指針變數的指針進去,而不是傳這個指針變數進去……
首先指針也是變數,也有內存空間存儲它的值,當然它的值又是另外一個內存的地址。
參數的值傳遞時,是這樣的
然後你給指針p賦值,只會改變存儲p的那塊內存的值,就像這樣
所以str指向的地址還是NULL…
用引用或者多重指針就好啦,指針也是一個數據類型,所以也有它對應的指針類型。首先這是一道c的面試題
void xxx(char** p){*p=malloc}c++的話
void xxx(char* p){p=new}另外malloc要free new要delete所謂傳遞指針,終究也是copy指針再傳遞,所以到了GetMemory,p已經不再是那個p。
前面有人已經回答地很好了,只要記住,1.c只有傳值調用;2.指針也是一個變數,也有內存空間用於存儲它的值,就不難理解了
要傳遞指針的值,你需要指針的指針
指針和普通變數一個樣啊, 只不過指針存的是變數的地址而已, 所以可以有指針的指針的指針 int*** p; 除了這個, 指針與其他變數沒有兩樣.
我發現很多人糾結指針, 都是沒明白這一點.不過題主糾結的不是這個, 題主對於指針的值傳遞理解是正確的, 但是, 對於重新分配內存的理解不對, 重新分配內存的過程是在堆上分配新的內存, 然後把新的內存的地址賦值給指針, 不是在該指針的地址重新分配內存.推薦閱讀:
※不學C語言,直接學C++會有問題嗎?
※怎麼理解 C 語言是面向過程的語言,C++ 是面向對象的語言。?
※gcc環境下不能使用gets怎麼辦?
※怎麼理解C語言的複雜聲明?
※C語言中的指針為什麼要區別出指向不同數據類型的指針?
TAG:C編程語言 |