我有一個程序是這樣的: int main() { int *p; int i; int*fun(void); p=fun(); for(i=0;i<3;i++) { printf("%d\n",*p); p++; } return 0; }; int* fun(void) { static int str[]={1,2,3,4,5}; int*q=str; return q; } 我想問一下,除了將str定義爲靜態區以及用malloc這樣的方法外,還有什麼好的方法,同時也想問一下如果我改成 int main() { char *p; char*fun(void); p=fun(); printf("%s\n",p); return 0; }; char * fun(void) { char *str="hello"; return str; } 爲什麼能輸出準確值,函數返回指針有哪些方法。
問題補充:
使用字符數組時,就能返回正確值 int main() { char *p; char *fun(void); p=fun(); printf("%c",p[2]); return 0; }; char* fun(void) { char str[]={'a','b','c'}; return str; } 但若將str[]改成str[]="hello"卻不行。
您的後一個例子中,雖然str是在動態變量區,而該動態變量是局部的,函數結束時不保留的。 但是,字符串"hello"不是變量,而是一個常量,編譯程序在處理這種常量時,通常把它放在了常量區中。而常量區則是始終存在的。 後一個例子中函數fun的返回值就是一個指向這種常量區的指針。 函數返回指針,要使主程序可以使用這個指針來訪問有意義的數據,關鍵就是要保證在使用這個指針值的時候,該指針所指向的地方的數據仍然有意義。 除了您所說的:用靜態區以及用malloc這樣的方法外,還有就是第二個例子裏的:用常量區。再另外,還可以用全局變量,也就是說返回的指針是指向全局變量的。 還有,如果指針是指向函數的指針,那麼這個指針就是指向程序代碼區的。這也是一種應用的情況。 另外,如果明白了它的原理,程序員還可以發明出一些其他靈活的使用方法,當然,那都屬於“怪”方法,一般不提倡的。 【總結】 常規程序中,函數返回的指針通常應該是: (1)指向靜態(static)變量; (2)指向專門申請分配的(如用malloc)空間; (3)指向常量區(如指向字符串"hello"); (4)指向全局變量; (5)指向程序代碼區(如指向函數的指針)。 除這5項以外,其它怪技巧不提倡。
- 追問
-
這裏我還是沒太明白,比如: int main() { int a,b; int max; int fun (int a,int b); scanf("%d%d",&a,&b); max=fun (a,b); printf("\n%d\n",max); return 0; }; int fun(int a,int b) { int max; if(a>b) max=a; else max=b; return max; } 在這個程序,爲什麼在fun()函數中就能返回max,按照動態存儲原理,在fun結束時,就max就被釋放了,不能再返回值了。但事實卻不是這樣。
- 回答
-
這個例子中,返回的不是變量max的地址,返回的是它的值。 return後面的東西,看做一個表達式,返回的是這個表達式的值。 例如,入口如果a是3,b是5,則此時(執行return語句時)max裏面存的是5。而return語句的功能就是把max裏面的5取出來,放到“返回值寄存器”中。 主程序是從“返回值寄存器”得到這個5的(此時max變量已經不存在了)。 你前面的第二個例子中,同樣,指針變量str在函數結束後已經不存在了。但是在return語句中,把指針變量str裏面的值(等於字符串"hello"存放處的地址)送到“返回值寄存器”中了。 動態變量str不存在了,但常量區中的字符串"hello"還存在。主程序根據返回的地址就可以找到該字符串。
- 追問
-
返回的是值,這個我明白了,但還是有點不明白的是: int main() { char *p; char *fun(void); p=fun(); printf("%x\n",p); printf("%s\n",p); return 0; } char* fun(void) { // char str[]={'a','b','c','d','e','f','\0'}; char str[]="hello"; printf("%x\n",str); return str; } 返回的也是str的地址,從結果來看p和str的值相等,但爲什麼準確,我的qq是121814322,謝謝加我,希望繼續請教。
- 回答
-
char str[]="hello"; 和 char *str="hello"; 不一樣。 char str[]="hello"; 是在動態變量區中開闢了可以容納6個字符的數組,數組名叫str。同時將字符串"hello"(原存放於常數空間)拷貝到這個數組空間中去作爲數組的初始化值。 此時若執行return str; 其中的str是數組名。C語言規定,表達式中如果是數組名,則該表達式的值就等於這個數組的地址。所以返回的是這個數組的地址,請注意:並不是字符串常量"hello"的地址!而函數結束時,雖然常數空間並不破壞,但這個數組空間是破壞了的,而你返回的卻不是常數空間裏的地址而正是已經破壞了的數組的地址。 而char *str="hello"; 是在動態變量區中開闢了一個可以存放一個指針值的變量,名叫str。同時將原存放於常數空間的字符串"hello"的地址賦給這個指針變量作爲初始值。 此時若執行return str; 其中的str是指針變量名。C語言規定,表達式中如果是變量名,則該表達式的值就等於這個變量的值。所以返回的是變量str的值,而變量str的值就等於字符串常量"hello"的地址。而函數結束時,變量str破壞了的,但常數空間中的字符串並不破壞。主程序根據返回的地址就可以找到該字符串。 【再補充】 您最後的【問題補充】中的新例子,用了char str[]={'a','b','c'};據我所知,應該是不行的。 您之所以說試驗了能行,有兩種可能的原因: (1)有可能函數返回後雖然數組str的空間已經被釋放了,但暫時該空間還沒有被新的內容完全覆蓋沖掉,所以您用p[2]訪問的時候那個位置裏原來的字符還在。 (2)有可能具體某個編譯程序對這類數組做了特殊處理(例如強行把它做成了靜態的)。按標準,具體的編譯程序應該保證符合標準的程序能正確運行,但“不符合標準”的程序並不一定要“不能運行”。如果某個編譯程序放寬標準使得某些不合標準的程序也出了正確的結果,這個編譯程序不算不標準。 如果是第一種原因,那麼程序上下文改一改,就可能不成立了;如果是第二個原因,那麼換一個C版本,就可能不成立了。