|
值傳遞, 指針傳遞?
這幾天在學習C過程中,在使用指針作為函數參數傳遞的時候出現了問題,根本不知道從何得解:源代碼如下:
createNode(BinNode *tree,char *p)
{
tree = (BinNode *) malloc(sizeof(BinNode));
tree->data = *p;
}
該代碼段的意圖是通過一個函數創建一個二叉樹的節點,然而在,調用該函數后,試圖訪問該節點結構體的成員時候,卻發生了內存訪問錯誤,到底問題出在哪兒呢?
一直不明白指針作為函數參數傳值的機制,翻開林銳的《高質量C/C++編程指南》,找到了答案。
[如果函數的參數是一個指針,不要指望用該指針去申請動態內存]
原來問題出在C編譯器原理上:編譯器總是要為函數的每個參數制作臨時副本,指針參數tree的副本是 _tree,編譯器使 _tree = tree。如果函數體內的程序修改了_tree的內容,就導致參數tree的內容作相應的修改。這就是指針可以用作輸出參數的原因。
即上面的函數代碼經過編譯后成為:
createNode(BinNode *tree,char *p)
{
BinNode *_tree;
_tree = tree;
_tree = (BinNode *) malloc(sizeof(BinNode));
_tree->data = *p;
}
如果沒有
_tree = (BinNode *) malloc(sizeof(BinNode));
這個語句,在函數體內修改了_tree的內容,將會導致參數tree的內容作相應的修改,因為它們指向相同的內存地址。而
_tree = (BinNode *) malloc(sizeof(BinNode));
這個句,系統重新分配內存給_tree指針,_tree指針指向了系統分配的新地址,函數體內修改的只是_tree的內容,對原tree所指的地址的內容沒有任何影響。因此,函數的參數是一個指針時,不要在函數體內部改變指針所指的地址,那樣毫無作用,需要修改的只能是指針所指向的內容。即應當把指針當作常量。
如果非要使用函數指針來申請內存空間,那么需要使用指向指針的指針
createNode(BinNode **tree,char *p)
{
*tree = (BinNode *) malloc(sizeof(BinNode));
}
上面的是林銳的說法,目前來說不知道怎么去理解,不過可以有另外的方案,通過函數返回值傳遞動態內存:
BinNode *createNode()
{
BinNode *tree;
tree = (BinNode *) malloc(sizeof(BinNode));
return tree;
}
這個倒還說得過去,因為函數返回的是一個地址的值,該地址就是申請的內存塊首地址。但是,這個容易和另外的一個忠告相混繞
[不要用return語句返回指向“棧內存”的指針,因為該內存在函數結束時自動消亡]
這里區分一下靜態內存,棧內存和動態分配的內存(堆內存)的區別:
(1) 從靜態存儲區域分配。內存在程序編譯的時候就已經分配好,這塊內存在程序的整個運行期間都存在。例如全局變量,static變量。
(2) 在棧上創建。在執行函數時,函數內局部變量的存儲單元都可以在棧上創建,函數執行結束時這些存儲單元自動被釋放。棧內存分配運算內置于處理器的指令集中,效率很高,但是分配的內存容量有限。
(3) 從堆上分配,亦稱動態內存分配。程序在運行的時候用malloc或new申請任意多少的內存,程序員自己負責在何時用free或delete釋放內存。動態內存的生存期由我們決定,使用非常靈活,但問題也最多。
因此,試圖返回一個棧上分配的內存將會引發未知錯誤
char *GetString(void)
{
char p[] = "hello world";
return p; // 編譯器將提出警告
}
p是在棧上分配的內存,函數結束后將會自動釋放,p指向的內存區域內容不是"hello world",而是未知的內容。
如果是返回靜態存儲的內存呢:
char *GetString(void)
{
char *p = "hello world";
return p;
}
這里“hello world”是常量字符串,位于靜態存儲區,它在程序生命期內恒定不變。無論什么時候調用GetString,它返回的始終是同一個“只讀”的內存塊。
[參考:林銳《高質量C/C++編程指南》]
1、結構體內存分配按照最嚴格的數據類型分配
例:
struct student
{
int num;
char c;
};
struct student stu1,stu2,stus[20],*ps;
內存分配的時候按照int型分配(地址按照能被4整除),成員的排列次序不同,內存分配不同。。。
另外編譯器影響結構體的內存分配。。最有效的方式(sizeof(student))計算字節數。。
struct student
{
int num;
char name[20];
}stu1,stu2,stus[20],*ps;
struct
{
int num;
char name[20];
}stu1;//沒有結構體名稱,所以不能在其他地方定義變量。。
無語。。
2、結構體可以嵌套,但是結構體不能嵌套自身。。。
Linux定義: Linux is not unix!!!
struct student li,zhang={"zhang",1,2,3};
li=zhang;//結構體可以直接相等。。當然兩個不同的結構體變量不能直接賦值。。。
li={"li",1,2,3};//錯。。
if(stu1==stu2);//錯。。
struct student
{
int age;
char *name;
}*ps;
ps=(struct student *)malloc(sizeof(struct student));
(*ps).age=30;
(*ps).name=(char *)malloc(20);
strcpy((*ps).name,"jince");
free((*ps).name);//釋放順序。。。
free(ps);
3、海賊王更新。。。
4、typedef int intage;
typedef double real;
#define int intage;
#define char* string;
string s1,s2;//這時候存在問題。。。 char* s1,s2;。。。
typedef char* string;
string s1,s2;//OK
typedef int bool;
struct Rec
{
...
};
typedef struct Rec Rec;
Rec jince;
指針變量統一占4個字節。。。
指針數組。。。解決鏈表問題??
前一個節點記錄后一個節點的地址。。。。
typedef struct Link
{
int a;
char c;
Link *next;
}Link;
5、#ifndef LIST_H //預編譯命令。。。對于已經定義的LIST_H不進行編譯。。