欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C語(yǔ)言指針入門學(xué)習(xí)面面觀

 更新時(shí)間:2016年02月07日 15:54:59   投稿:goldensun  
這篇文章主要介紹了C語(yǔ)言指針的一些基礎(chǔ)知識(shí),指針可以說(shuō)是C語(yǔ)言入門學(xué)習(xí)中的頭等大事,文中從數(shù)組和函數(shù)等多方面剖析C中指針的作用,需要的朋友可以參考下

這似乎是一個(gè)很凝重的話題,但是它真的很有趣。

1. 指針是指向某一類型的東西,任何一個(gè)整體,只要能稱為整體就能擁有它自己的獨(dú)一無(wú)二的指針類型,所以指針的類型其實(shí)是近似無(wú)窮無(wú)盡的

2. 函數(shù)名在表達(dá)式中總是以函數(shù)指針的身份呈現(xiàn),除了取地址運(yùn)算符以及sizeof

3. C語(yǔ)言最晦澀難明的就是它復(fù)雜的聲明: void (*signal(int sig, void (*func)(int)))(int),試試著把它改寫成容易理解的形式

4. 對(duì)于指針,盡最大的限度使用const保護(hù)它,無(wú)論是傳遞給函數(shù),還是自己使用

先來(lái)看看一個(gè)特殊的指針,姑且稱它為指針,因?yàn)樗蕾囉诃h(huán)境: NULL,是一個(gè)神奇的東西。先附上定義,在編譯器中會(huì)有兩種NULL(每種環(huán)境都有唯一確定的NULL):

#define NULL 0
#define NULL ((void*)0)

有什么區(qū)別嗎?看起來(lái)沒(méi)什么區(qū)別都是0,只不過(guò)一個(gè)是常量,一個(gè)是地址為0的指針。

當(dāng)它們都作為指針的值時(shí)并不會(huì)報(bào)錯(cuò)或者警告,即編譯器或者說(shuō)C標(biāo)準(zhǔn)認(rèn)為這是合法的:

int* temp_int_1 = 0; //無(wú)警告
int* temp_int_2 = (void*)0; //無(wú)警告
int* temp_int_3 = 10; //出現(xiàn)警告

為什么?為什么0可以賦值給指針,但是10卻不行?他們都是常量。

因?yàn)镃語(yǔ)言規(guī)定當(dāng)處理上下文的編譯器發(fā)現(xiàn)常量0出現(xiàn)在指針賦值的語(yǔ)句中,它就作為指針使用,似乎很扯淡,可是卻是如此。

回到最開(kāi)始,對(duì)于NULL的兩種情況,會(huì)有什么區(qū)別?拿字符串來(lái)說(shuō),實(shí)際上我是將字符數(shù)組看作是C風(fēng)格字符串。

在C語(yǔ)言中,字符數(shù)組是用來(lái)存儲(chǔ)一連串有意義的字符,默認(rèn)在這些字符的結(jié)尾添加'\0',好這里又出現(xiàn)了一個(gè)0值。

對(duì)于某些人,在使用字符數(shù)組的時(shí)候總是分不清楚NULL與'\0'的區(qū)別而誤用,在字符數(shù)組的末尾使用NULL是絕對(duì)錯(cuò)誤的!雖然它們的本質(zhì)都是常量0,但由于位置不同所以含義也不同。

開(kāi)胃菜已過(guò)

對(duì)于一個(gè)函數(shù),我們進(jìn)行參數(shù)傳遞,參數(shù)有兩種形式: 形參與實(shí)參

int function(int value)
{
    /*...*/
}
//...
function(11);

其中,value是形參,11是實(shí)參,我們知道場(chǎng)面上,C語(yǔ)言擁有兩種傳遞方式:按值傳遞和按址傳遞,但是你是否有認(rèn)真研究過(guò)?這里給出一個(gè)實(shí)質(zhì),其實(shí)C語(yǔ)言只有按值傳遞,所謂按址傳遞只不過(guò)是按值傳遞的一種假象。至于原因稍微一想便能明白。

對(duì)于形參和實(shí)參而言兩個(gè)關(guān)系緊密,可以這么理解總是實(shí)參將自己的一份拷貝傳遞給形參,這樣形參便能安全的使用實(shí)參的值,但也帶給我們一些麻煩,最經(jīng)典的交換兩數(shù)

void swap_v1(int* val_1, int* val_2)
{
  int temp = *val_1;
  *val_1 = *val_2;
  *val_2 = *val_1;
}

這就是所謂的按址傳遞,實(shí)際上只是將外部指針(實(shí)參)的值做一個(gè)拷貝,傳遞給形參val_1與val_2,實(shí)際上我們使用:

#define SWAP_V2(a, b) (a += b, b = a - b, a -= b)
#define SWAP_V3(x, y) {x ^= y; y ^= x; x ^= y}

試一試是不是很神奇,而且省去了函數(shù)調(diào)用的時(shí)間,空間開(kāi)銷。上述兩種寫法的原理實(shí)質(zhì)是一樣的。

但是,動(dòng)動(dòng)腦筋想一想,這種寫法真的沒(méi)有瑕疵嗎?如果輸入的兩個(gè)參數(shù)本就指向同一塊內(nèi)存,會(huì)發(fā)生什么?

...
int test_1 = 10, test_2 = 100;
SWAP_V2(test_1, test_2);          
printf("Now the test_1 is %d, test_2 is %d\n", test_1, test_2);
.../*恢復(fù)原值*/
SWAP_V2(test_1, test_1);
printf("Now the test_1 is %d\n", test_1);  

會(huì)輸出什么?:

$: Now the test_1 is 100, test_2 is 10
$: Now the test_1 is 0

對(duì),輸出了0,為什么?稍微動(dòng)動(dòng)腦筋就能相通,那么對(duì)于后面的SWAP_V3亦是如此,所以在斟酌之下,解決方案應(yīng)該盡可能短小精悍:

static inline void swap_final(int* val_1, int* val_2)
{
  if(val_1 == val_2)
    return;
  *val_1 ^= *val_2;
  *val_2 ^= *val_1;
  *val_1 ^= *val_2;
}
#define SWAP(x, y) \
do{         \
  if(&x == &y)  \
    break;   \
  x ^= y;   \
  y ^= x;   \
  x ^= y;   \
}while(0)

這便是目前能找到最好的交換函數(shù),我們?cè)诖嘶A(chǔ)上可以考慮的更深遠(yuǎn)一些,如何讓這個(gè)交換函數(shù)更加通用?即適用范圍更大?暫不考慮浮點(diǎn)類型。 提示:可用void*

與上面的情況類似,偶爾的不經(jīng)意就會(huì)造成嚴(yán)重的后果:

int combine_1(int* dest, int* add)
{
  *dest += *add;
  *dest += *add;
  return *dest;
}
int combine_2(int* dest, int* add)
{
  *dest = 2* (*add);//在不確定優(yōu)先級(jí)時(shí)用括號(hào)是一個(gè)明智的選擇
  return *dest;
}

上述兩個(gè)函數(shù)的功能一樣嗎?恩看起來(lái)是一樣的

int test_3 = 10, test_4 = 100;

combine_1(&test_3, &test_4);
printf("After combine_1, test_3 = %d\n",test_3);
.../*恢復(fù)原值*/
combine_2(&test_3, &test_4);
printf("After combine_2, test_3 = %d\n",test_3);

輸出

$: After combine_1, test_3 = 210

$: After combine_2, test_3 = 210

如果傳入兩個(gè)同一對(duì)象呢?

... /*恢復(fù)test_3原值*/
combine_1(&test_3, &test_3);
printf("After second times combine_1, test_3 = %d\n",test_3);
...
combine_2(&test_3, &test_3);
printf("After second times combine_2, test_3 = %d\n",test_3);

輸出

$: After second times combine_1, test_3 = 30

$: After second times combine_2, test_3 = 20

知道真相總是令人吃驚,指針也是那么令人又愛(ài)又恨。

C99 標(biāo)準(zhǔn)之后出現(xiàn)了一個(gè)新的關(guān)鍵字, restrict,被用于修飾指針,它并沒(méi)有太多的顯式作用,甚至加與不加,在 你自己 看來(lái),效果毫無(wú)區(qū)別。但是反觀標(biāo)準(zhǔn)庫(kù)的代碼中,許多地方都使用了該關(guān)鍵字,這是為何

  • 首先這個(gè)關(guān)鍵字是寫給編譯器看的
  • 其次這個(gè)關(guān)鍵字的作用在于輔助編譯器更好的優(yōu)化該程序
  • 最后,如果不熟悉,絕對(duì)不要亂用這個(gè)關(guān)鍵字。

關(guān)于數(shù)組的那些事

數(shù)組和指針一樣嗎?

不一樣

要時(shí)刻記住,數(shù)組與指針是不同的東西。但是為什么下面代碼是正確的?

int arr[10] = {10, 9, 8, 7};
int* parr = arr;

我們還是那句話,結(jié)合上下文,編譯器推出 arr處于賦值操作符的右側(cè),默默的將他轉(zhuǎn)換為對(duì)應(yīng)類型的指針,而我們?cè)谑褂胊rr時(shí)也總是將其當(dāng)成是指向該數(shù)組內(nèi)存塊首位的指針。

//int function2(const int test_arr[10]
//int function2(const int test_arr[]) 考慮這三種寫法是否一樣
int function2(const int* test_arr)
{
  return sizeof(test_arr);
}
...
int size_out = sizeof(arr);
int size_in = function2(arr);

printf("size_out = %d, size_in = %d\n", size_out, size_in);

輸出:

size_out = 40, size_in = 8

這就是為什么數(shù)組與指針不同的原因所在,在外部即定義數(shù)組的代碼塊中,編譯器通過(guò)上下文發(fā)覺(jué)此處arr是一個(gè)數(shù)組,而arr代表的是一個(gè)指向10個(gè)int類型的數(shù)組的指針,只所謂最開(kāi)始的代碼是正確的,只是因?yàn)檫@種用法比較多,就成了標(biāo)準(zhǔn)的一部分。就像世上本沒(méi)有路,走的多了就成了路。"正確"的該怎么寫

int (*p)[10] = &arr;

此時(shí)p的類型就是一個(gè)指向含有10個(gè)元素的數(shù)組的指針,此時(shí)(*p)[0]產(chǎn)生的效果是arr[0],也就是parr[0],但是(*p)呢?這里不記錄,結(jié)果是會(huì)溢出,為什么?

這就是數(shù)組與指針的區(qū)別與聯(lián)系,但是既然我們可以使用像parr這樣的指針,又為什么要寫成int (*p)[10]這樣丑陋不堪的模式呢?原因如下:

回到最開(kāi)始說(shuō)過(guò)的傳遞方式,按值傳遞在傳遞arr時(shí)只是純粹的將其值進(jìn)行傳遞,而丟失了上下文的它只是一個(gè)普通指針,只不過(guò)我們程序員知道它指向了一塊有意義的內(nèi)存的起始位置,我想要將數(shù)組的信息一起傳遞,除了額外增加一個(gè)參數(shù)用來(lái)記錄數(shù)組的長(zhǎng)度以外,也可以使用這個(gè)方法,傳遞一個(gè)指向數(shù)組的指針 這樣我們就能只傳遞一個(gè)參數(shù)而保留所有信息。但這么做的也有限制:對(duì)于不同大小,或者不同存儲(chǔ)類型的數(shù)組而言,它們的類型也有所不同

 int arr_2[5];
 int (*p_2)[5] = &arr_2;
 float arr_3[5];
 float (*p_3)[5] = &arr_3;

如上所示,指向數(shù)組的指針必須明確指定數(shù)組的大小,數(shù)組存儲(chǔ)類型,這就讓指向數(shù)組的指針有了比較大的限制。

這種用法在多維數(shù)組中使用的比較多,但總體來(lái)說(shuō)平常用的并不多,就我而言,更傾向于使用一維數(shù)組來(lái)表示多維數(shù)組,實(shí)際上誠(chéng)如前面所述,C語(yǔ)言是一個(gè)非常簡(jiǎn)潔的語(yǔ)言,它沒(méi)有太多的廢話,就本質(zhì)而言C語(yǔ)言并沒(méi)有多維數(shù)組,因?yàn)閮?nèi)存是一種線性存在,即便是多維數(shù)組也是實(shí)現(xiàn)成一維數(shù)組的形式。

就多維數(shù)組在這里解釋一下。所謂多維數(shù)組就是將若干個(gè)降一維的數(shù)組組合在一起,降一維的數(shù)組又由若干個(gè)更降一維的數(shù)組組合在一起,直到最低的一維數(shù)組,舉個(gè)例子:

int dou_arr[5][3];

就這個(gè)二維數(shù)組而言,將5個(gè)每個(gè)為3個(gè)int類型的數(shù)組組合在一起,要想指向這個(gè)數(shù)組該怎么做?

  int (*p)[3]    = &dou_arr[0];
  int (*dou_p)[5][3] = &dou_arr;
  int (*what_p)[3]  = dou_arr;

實(shí)際上多維數(shù)組只是將多個(gè)降一維的數(shù)組組合在一起,令索引時(shí)比較直觀而已。當(dāng)真正理解了內(nèi)存的使用,反而會(huì)覺(jué)得多維數(shù)組帶給自己更多限制 對(duì)于第三句的解釋,當(dāng)數(shù)組名出現(xiàn)在賦值號(hào)右側(cè)時(shí),它將是一個(gè)指針,類型則是 指向該數(shù)組元素的類型,而對(duì)于一個(gè)多維數(shù)組來(lái)說(shuō),其元素類型則是其降一維數(shù)組,即指向該降一維數(shù)組的指針類型。這個(gè)解釋有點(diǎn)繞,自己動(dòng)手寫一寫就好很多。

對(duì)于某種形式下的操作,我們總是自然的將相似的行為結(jié)合在一起考慮??紤]如下代碼:

int* arr_3[5] = {1, 2, 3, 4, 5};
int* p_4   = arr_3;

printf("%d == %d == %d ?\n", arr_3[2], *(p_4 + 2), *(arr_3 + 2));

輸出: 3 == 3 == 3 ? 實(shí)際上對(duì)于數(shù)組與指針而言, []操作在大多數(shù)情況下都能有相同的結(jié)果,對(duì)于指針而言*(p_4 + 2)相當(dāng)于p_4[2],也就是說(shuō)[]便是指針運(yùn)算的語(yǔ)法糖,有意思的是2[p_4]也相當(dāng)于p_4[2],"Iamastring"[2] == 'm',但這只是娛樂(lè)而已,實(shí)際中請(qǐng)不要這么做,除非是代碼混亂大賽或者某些特殊用途。 在此處,應(yīng)該聲明的是這幾種寫法的執(zhí)行效率完全一致,并不存在一個(gè)指針運(yùn)算便快于[]運(yùn)算,這些說(shuō)法都是上個(gè)世紀(jì)的說(shuō)法了,隨著時(shí)代的發(fā)展,我們應(yīng)該更加注重代碼整潔之道

在此處還有一種奇異又實(shí)用的技巧,在char數(shù)組中使用指針運(yùn)算進(jìn)行操作,提取不同類型的數(shù)據(jù),或者是在不同類型數(shù)組中,使用char*指針抽取其中內(nèi)容,才是顯示指針運(yùn)算的用途。但在使用不同類型指針操作內(nèi)存塊的時(shí)候需要注意,不要操作無(wú)意義的區(qū)域或者越界操作。

實(shí)際上,最簡(jiǎn)單的安全研究之一,便是利用溢出進(jìn)行攻擊。

Advance:對(duì)于一個(gè)函數(shù)中的某個(gè)數(shù)組的增長(zhǎng)方向,總是向著返回地址的,中間可能隔著許多其他自動(dòng)變量,我們只需要一直進(jìn)行溢出試驗(yàn),直到某一次,該函數(shù)無(wú)法正常返回了!那就證明我們找到了該函數(shù)的返回地址存儲(chǔ)地區(qū),這時(shí)候我們可以進(jìn)行一些操作,例如將我們想要的返回地址覆蓋掉原先的返回地址,這就是所謂的溢出攻擊中的一種。

內(nèi)存的使用的那些事兒

你一直以為你操作的是真實(shí)物理內(nèi)存,實(shí)際上并不是,你操作的只是操作系統(tǒng)為你分配的資格虛擬地址,但這并不意味著我們可以無(wú)限使用內(nèi)存,那內(nèi)存賣那么貴干嘛,實(shí)際上存儲(chǔ)數(shù)據(jù)的還是物理內(nèi)存,只不過(guò)在操作系統(tǒng)這個(gè)中介的介入情況下,不同程序窗口(可以是相同程序)可以共享使用同一塊內(nèi)存區(qū)域,一旦某個(gè)傻大個(gè)程序的使用讓物理內(nèi)存不足了,我們就會(huì)把某些沒(méi)用到的數(shù)據(jù)寫到你的硬盤上去,之后再使用時(shí),從硬盤讀回。這個(gè)特性會(huì)導(dǎo)致什么呢?假設(shè)你在Windows上使用了多窗口,打開(kāi)了兩個(gè)相同的程序:

...
int stay_here;
char tran_to_int[100];
printf("Address: %p\n", &stay_here);

fgets(tran_to_int, sizeof(tran_to_int), stdin);
sscanf(tran_to_int, "%d", &stay_here);

for(;;)
{
  printf("%d\n", stay_here);
  getchar();
  ++stay_here;
}
...

對(duì)此程序(引用前橋和彌的例子),每敲擊一次回車,值加1。當(dāng)你同時(shí)打開(kāi)兩個(gè)該程序時(shí),你會(huì)發(fā)現(xiàn),兩個(gè)程序的stay_here都是在同一個(gè)地址,但對(duì)它進(jìn)行分別操作時(shí),產(chǎn)生的結(jié)果是獨(dú)立的!這在某一方面驗(yàn)證了虛擬地址的合理性。虛擬地址的意義就在于,即使一個(gè)程序出現(xiàn)了錯(cuò)誤,導(dǎo)致所在內(nèi)存完蛋了,也不會(huì)影響到其他進(jìn)程。對(duì)于程序中部的兩個(gè)讀取語(yǔ)句,是一種理解C語(yǔ)言輸入流本質(zhì)的好例子,建議查詢用法,這里稍微解釋一下:

通俗地說(shuō),fgets將輸入流中由調(diào)用起,stdin輸入的東西存入起始地址為tran_to_int的地方,并且最多讀取sizeof(tran_to_int)個(gè),并在后方sscanf函數(shù)中將剛才讀入的數(shù)據(jù)按照%d的格式存入stay_here,這就是C語(yǔ)言一直在強(qiáng)調(diào)的流概念的意義所在,這兩個(gè)語(yǔ)句組合看起來(lái)也就是讀取一個(gè)數(shù)據(jù)這么簡(jiǎn)單,但是我們要知道一個(gè)問(wèn)題,一個(gè)關(guān)于scanf的問(wèn)題

 scanf("%d", &stay_here);

這個(gè)語(yǔ)句將會(huì)讀取鍵盤輸入,直到回車之前的所有數(shù)據(jù),什么意思?就是回車會(huì)留在輸入流中,被下一個(gè)輸入讀取或者丟棄。這就有可能會(huì)影響我們的程序,產(chǎn)生意料之外的結(jié)果。而使用上當(dāng)兩句組合則不會(huì)。

函數(shù)與函數(shù)指針的那些事

事實(shí)上,函數(shù)名出現(xiàn)在賦值符號(hào)右邊就代表著函數(shù)的地址

int function(int argc){ /*...*/
}
...
int (*p_fun)(int) = function;
int (*p_fuc)(int) = &function;//和上一句意義一致

上述代碼即聲明并初始化了函數(shù)指針,p_fun的類型是指向一個(gè)返回值是int類型,參數(shù)是int類型的函數(shù)的指針

p_fun(11);
(*p_fun)(11);
function(11);

上述三個(gè)代碼的意義也相同,同樣我們也能使用函數(shù)指針數(shù)組這個(gè)概念

int (*p_func_arr[])(int) = {func1, func2,};

其中func1,func2都是返回值為int參數(shù)為int的函數(shù),接著我們能像數(shù)組索引一樣使用這個(gè)函數(shù)了。

Tips: 我們總是忽略函數(shù)聲明,這并不是什么好事。

在C語(yǔ)言中,因?yàn)榫幾g器并不會(huì)對(duì)有沒(méi)有函數(shù)聲明過(guò)分深究,甚至還會(huì)放縱,當(dāng)然這并不包含內(nèi)聯(lián)函數(shù)(inline),因?yàn)樗旧砭椭辉诒疚募捎谩?br /> 比如,當(dāng)我們?cè)谀硞€(gè)地方調(diào)用了一個(gè)函數(shù),但是并沒(méi)有聲明它:

 CallWithoutDeclare(100); //參數(shù)100為 int 型

那么,C編譯器就會(huì)推測(cè),這個(gè)使用了int型參數(shù)的函數(shù),一定是有一個(gè)int型的參數(shù)列表,一旦函數(shù)定義中的參數(shù)列表與之不符合,將會(huì)導(dǎo)致參數(shù)信息傳遞錯(cuò)誤(編譯器永遠(yuǎn)堅(jiān)信自己是對(duì)的!),我們知道C語(yǔ)言是強(qiáng)類型語(yǔ)言,一旦類型不正確,會(huì)導(dǎo)致許多意想不到的結(jié)果(往往是Bug)發(fā)生。

對(duì)函數(shù)指針的調(diào)用同樣如此
C語(yǔ)言中malloc的那些事兒

我們常常見(jiàn)到這種寫法:

int* pointer = (int*)malloc(sizeof(int));

這有什么奇怪的嗎?看下面這個(gè)例子:

int* pointer_2 = malloc(sizeof(int));

哪個(gè)寫法是正確的??jī)蓚€(gè)都正確,這是為什么呢,這又要追求到遠(yuǎn)古C語(yǔ)言時(shí)期,在那個(gè)時(shí)候, void* 這個(gè)類型還沒(méi)有出現(xiàn)的時(shí)候,malloc 返回的是 char* 的類型,于是那時(shí)的程序員在調(diào)用這個(gè)函數(shù)時(shí)總要加上強(qiáng)制類型轉(zhuǎn)換,才能正確使用這個(gè)函數(shù),但是在標(biāo)準(zhǔn)C出現(xiàn)之后,這個(gè)問(wèn)題不再擁有,由于任何類型的指針都能與 void* 互相轉(zhuǎn)換,并且C標(biāo)準(zhǔn)中并不贊同在不必要的地方使用強(qiáng)制類型轉(zhuǎn)換,故而C語(yǔ)言中比較正統(tǒng)的寫法是第二種。

題外話: C++中的指針轉(zhuǎn)換需要使用強(qiáng)制類型轉(zhuǎn)換,而不能像第二種例子,但是C++中有一種更好的內(nèi)存分配方法,所以這個(gè)問(wèn)題也不再是問(wèn)題。

Tips:

C語(yǔ)言的三個(gè)函數(shù)malloc, calloc, realloc都是擁有很大風(fēng)險(xiǎn)的函數(shù),在使用的時(shí)候務(wù)必記得對(duì)他們的結(jié)果進(jìn)行校驗(yàn),最好的辦法還是對(duì)他們進(jìn)行再包裝,可以選擇宏包裝,也可以選擇函數(shù)包裝。
realloc函數(shù)是最為人詬病的一個(gè)函數(shù),因?yàn)樗穆毮苓^(guò)于寬廣,既能分配空間,也能釋放空間,雖然看起來(lái)是一個(gè)好函數(shù),但是有可能在不經(jīng)意間會(huì)幫我們做一些意料之外的事情,例如多次釋放空間。正確的做法就是,應(yīng)該使用再包裝閹割它的功能,使他只能進(jìn)行擴(kuò)展或者縮小堆內(nèi)存塊大小。
指針與結(jié)構(gòu)體

typedef struct tag{
    int value;
    long vari_store[1];
}vari_struct;

乍一看,似乎是一個(gè)很中規(guī)中矩的結(jié)構(gòu)體

...
vari_struct vari_1;
vari_struct* vari_p_1 = &vari_1;
vari_struct* vari_p_2 = malloc(sizeof(vari_struct))(

似乎都是這么用的,但總有那么一些人想出了一些奇怪的用法

int     what_spa_want = 10;
vari_struct* vari_p_3 = malloc(sizeof(vari_struct) + sizeof(long)*what_spa_want);

這么做是什么意思呢?這叫做可變長(zhǎng)結(jié)構(gòu)體,即便我們超出了結(jié)構(gòu)體范圍,只要在分配空間內(nèi),就不算越界。what_spa_want解釋為你需要多大的空間,即在一個(gè)結(jié)構(gòu)體大小之外還需要多少的空間,空間用來(lái)存儲(chǔ)long類型,由于分配的內(nèi)存是連續(xù)的,故可以直接使用數(shù)組vari_store直接索引。 而且由于C語(yǔ)言中,編譯器并不對(duì)數(shù)組做越界檢查,故對(duì)于一個(gè)有N個(gè)數(shù)的數(shù)組arr,表達(dá)式&arr[N]是被標(biāo)準(zhǔn)允許的行為,但是要記住arr[N]卻是非法的。 這種用法并非是娛樂(lè),而是成為了標(biāo)準(zhǔn)(C99)的一部分,運(yùn)用到了實(shí)際中

對(duì)于內(nèi)存的理解

在內(nèi)存分配的過(guò)程中,我們使用 malloc 進(jìn)行分配,用 free 進(jìn)行釋放,但這是我們理解中的分配與釋放嗎? 在調(diào)用 malloc 時(shí),該函數(shù)或使用 brk() 或使用 nmap() 向操作系統(tǒng)申請(qǐng)一片內(nèi)存,在使用時(shí)分配給需要的地方,與之對(duì)應(yīng)的是 free,與我們硬盤刪除東西一樣,實(shí)際上:

int* value = malloc(sizeof(int)*5);
...
free(value);
printf("%d\n", value[0]);

代碼中,為什么在 free 之后,我又繼續(xù)使用這個(gè)內(nèi)存呢?因?yàn)?free 只是將該內(nèi)存標(biāo)記上釋放的標(biāo)記,示意分配內(nèi)存的函數(shù),我可以使用,但并沒(méi)有破壞當(dāng)前內(nèi)存中的內(nèi)容,直到有操作對(duì)它進(jìn)行寫入。 這便引申出幾個(gè)問(wèn)題:

Bug更加難以發(fā)現(xiàn),讓我們假設(shè),如果我們有兩個(gè)指針p1,p2指向同一個(gè)內(nèi)存,如果我們對(duì)其中某一個(gè)指針使用了 free(p1); 操作,卻忘記了還有另一個(gè)指針指向它,那這就會(huì)導(dǎo)致很嚴(yán)重的安全隱患,而且這個(gè)隱患十分難以發(fā)現(xiàn),原因在于這個(gè)Bug并不會(huì)在當(dāng)時(shí)顯露出來(lái),而是有可能在未來(lái)的某個(gè)時(shí)刻,不經(jīng)意的讓你的程序崩潰。
有可能會(huì)讓某些問(wèn)題更加簡(jiǎn)化,例如釋放一個(gè)條條相連的鏈表域。
總的來(lái)說(shuō),還是那句話C語(yǔ)言是一把雙刃劍。

相關(guān)文章

  • visual?studio?將編譯后的dll等文件自動(dòng)復(fù)制到指定目錄的方法

    visual?studio?將編譯后的dll等文件自動(dòng)復(fù)制到指定目錄的方法

    這篇文章主要介紹了visual?studio?將編譯后的dll等文件自動(dòng)復(fù)制到指定目錄,本文給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧
    2024-03-03
  • 淺談c++的編譯和運(yùn)行

    淺談c++的編譯和運(yùn)行

    下面小編就為大家?guī)?lái)一篇淺談c++的編譯和運(yùn)行。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-05-05
  • C++中的std::initializer_list使用解讀

    C++中的std::initializer_list使用解讀

    這篇文章主要介紹了C++中的std::initializer_list使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • C++序列操作函數(shù)學(xué)習(xí)最全指南

    C++序列操作函數(shù)學(xué)習(xí)最全指南

    這篇文章主要給大家介紹了關(guān)于C++序列操作函數(shù)學(xué)習(xí)的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用C++具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2022-02-02
  • 詳解Matlab中自帶的Java操作合集

    詳解Matlab中自帶的Java操作合集

    其實(shí)Matlab中也有一些自帶的Java操作,例如:獲取鼠標(biāo)在全屏位置、獲取當(dāng)前剪切板內(nèi)容、獲取鼠標(biāo)處像素顏色等,本文總結(jié)了七個(gè)這樣的操作,感興趣的可以了解一下
    2022-03-03
  • 基于Matlab實(shí)現(xiàn)嗅覺(jué)優(yōu)化算法的示例代碼

    基于Matlab實(shí)現(xiàn)嗅覺(jué)優(yōu)化算法的示例代碼

    嗅覺(jué)劑優(yōu)化是一種新穎的優(yōu)化算法,旨在模仿氣味分子源尾隨的藥劑的智能行為。本文將利用Matlab實(shí)現(xiàn)這一智能優(yōu)化算法,需要的可以參考一下
    2022-05-05
  • C語(yǔ)言深入探索遞歸的特點(diǎn)

    C語(yǔ)言深入探索遞歸的特點(diǎn)

    程序調(diào)???的編程技巧稱為遞歸 recursion)函數(shù)??調(diào)???就是遞歸,你也可以理解成是?種嵌套結(jié)構(gòu),但遞歸分為倆部分,第?是“遞”,進(jìn)?嵌套結(jié)構(gòu)。第?是”歸“,最終會(huì)?步?步返回。第?次接觸遞歸都會(huì)很懵,慢慢理解這個(gè)過(guò)程就明?了
    2022-06-06
  • C++中std::conditional的使用說(shuō)明

    C++中std::conditional的使用說(shuō)明

    這篇文章主要介紹了C++中std::conditional的使用說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • C++char類型和輸入輸出優(yōu)化

    C++char類型和輸入輸出優(yōu)化

    這篇文章主要介紹了C++char類型和輸入輸出優(yōu)化,char的全稱是character,也就是字符的意思。顧名思義,char類型是專門為了存儲(chǔ)字符而設(shè)計(jì)的。下面我們一起來(lái)看看文章的具體內(nèi)容吧
    2021-11-11
  • C++通用動(dòng)態(tài)抽象工廠的實(shí)現(xiàn)詳解

    C++通用動(dòng)態(tài)抽象工廠的實(shí)現(xiàn)詳解

    在面向?qū)ο蟮木幊讨?一般通過(guò)繼承和虛函數(shù)來(lái)提供抽象能力,下面這篇文章主要給大家介紹了關(guān)于C++通用動(dòng)態(tài)抽象工廠的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-07-07

最新評(píng)論