电竞比分网-中国电竞赛事及体育赛事平台

分享

指針的一些問題

 看風景D人 2014-09-10

1、c++/c語言中不少地方,數(shù)組和指針可以相互替換使用,容易讓人產(chǎn)生一種錯覺,指針和數(shù)組是等價的。

數(shù)組要么在靜態(tài)存儲區(qū)域創(chuàng)建,如全局數(shù)組;要么在棧上創(chuàng)建如函數(shù)內(nèi)的數(shù)組。數(shù)組的名稱對應著(而不是指向)一塊內(nèi)存,它的地址和容量在其生命周期內(nèi)保持不變,數(shù)組的內(nèi)容可變。

指針可以指向任意類型的內(nèi)存塊,它的特征是可變的,所以常常用指針來操作動態(tài)內(nèi)存,指針比數(shù)組靈活,當時容易出錯。

char a[] = "hello";
a[0] = 'x';
cout<<a<<endl;
char *p = "world";  //這里的p指向的是常量字符串
p[0] = 'x';  //編譯器不能發(fā)現(xiàn)該錯誤
cout<<p;

如上邊一段代碼,a是容量為6的字符數(shù)組,a中的內(nèi)容是可以改變的,如a[0]='x'。指針p指向的是一個常量字符串“world”(位于靜態(tài)存儲區(qū)),常量字符串的內(nèi)容是不能夠被修改的。但是從語法的角度看,編譯器并不知道p[0]='x'有什么問題,但是該語句在企圖執(zhí)行時,就會出錯。

復制代碼
char a[] = "hello";
char *p = "world"; 
cout<<sizeof(a)<<endl;             //6
cout<<sizeof(p)<<endl;             //4
cout<<sizeof(char *)<<endl;     // 4
cout<<sizeof(void *)<<endl;      //4
cout<<sizeof(int *)<<endl;        //4  
cout<<sizeof(short *)<<endl;    //4
復制代碼

 

另外指針和數(shù)組的容量計算也是有區(qū)別的。以上一段代碼為例,sizeof(a)的值為6,但是sizeof(p)的值為4,這是因為sizeof(a)可以計算出數(shù)組的字節(jié)數(shù),但是sizeof(p)得到的是一個指針變量的字節(jié)數(shù),相當于sizeof(char *),而不是p所指向的內(nèi)存容量。c++、c語言是沒有辦法知道指針所指向的內(nèi)存容量,除非在申請內(nèi)存時記住。

 

void test(char p[100])
{
    cout<<sizeof(p)<<endl;//4
}

 

注意:當數(shù)組作為函數(shù)的參數(shù)進行傳遞時,該數(shù)組自動退化為同類型的指針。如上邊的代碼,sizeof(p)的大小為4。

2、指針參數(shù)傳遞內(nèi)存

復制代碼
void GetMemory(char *p)
{
    p = (char *)malloc(100);
}

int main()
{
      char *str = NULL;
      GetMemory(str);
      strcpy(str,"hello");
      printf("%s",str);   //運行出錯
      free(str);
}
復制代碼

這段代碼運行出錯,原因出自函數(shù)Getmemory中。編譯器總是要為函數(shù)的每個參數(shù)制作臨時副本,指針參數(shù)p的副本是_p,編譯器使_p=p。如果函數(shù)體內(nèi)的程序修改了_p的內(nèi)容,就導致了參數(shù)p的內(nèi)容作相應的修改。這就是指針可以用作輸出參數(shù)的原因。但是在本例中,_p申請了新的內(nèi)存,只是把_p所指向的內(nèi)存地址改變了,但是p絲毫未變。所以函數(shù)GetMemory并不能輸出任何東西,每次執(zhí)行一次GetMemory就會泄露一塊內(nèi)存。因為沒有執(zhí)行free釋放內(nèi)存。

void GetMemory2(char **p,int num)
{
    *p = (char *)malloc(num);
}

如果一定要使用指針參數(shù)去申請內(nèi)存,那么可以使用指向指針的指針,如上邊的代碼。當然也可以使用函數(shù)返回值來傳遞動態(tài)內(nèi)存,如:

char *GetMemory3(int num)
{
    char *p = (char *)malloc(num);
    return p;  
}

但是值得注意的是,我們這里使用返回值返回的是動態(tài)分配的堆內(nèi)存,不是棧內(nèi)存,如果不小心返回的是棧內(nèi)存,就會出錯,因為在函數(shù)結(jié)束時,棧內(nèi)存自動消亡了。

char *GetMemory4()
{
    char p[] ="hello world!"
    return p;   //編譯器會發(fā)出警告  
}

對上邊的程序稍作修改

char *GetMemory5()
{
    char *p ="hello world!"
    return p;   
}

這時候p指向的是字符串常量,位于靜態(tài)存儲區(qū),生命周期恒定不變,那么此時返回的是一個只讀的內(nèi)存塊。

 

3、結(jié)構(gòu)體的存儲分配

復制代碼
struct Align1
{
       int a;
       char b;
       char c;
};
struct Align2
{
       char b;
       int a;
       char c;
};
復制代碼

如上邊所示兩個結(jié)構(gòu)體的數(shù)據(jù)元素一樣,但是位置順序不同,那么他們占用的內(nèi)存大小不同。在32位機器中整型4個字節(jié),并且他的起始存儲位置必須能夠被4整除。所以以上兩個結(jié)構(gòu)體在內(nèi)存中分配如圖所示

編譯器按照成員列表的順序一個接著一個的給每個成員分配內(nèi)存。只有當成員之間滿足正確的對齊要求時,成員之間才會出現(xiàn)用于填充的額外內(nèi)存空間。有些時候,我們有充分的理由決定不對數(shù)據(jù)結(jié)構(gòu)成員進行重排,減少因邊界對齊帶來的空間損失。例如,我們可能想把相關(guān)的結(jié)構(gòu)成員存儲到一起,提高程序的可維護性和可讀性。但是,如果不存在這樣的理由,結(jié)構(gòu)成員應該根據(jù)他們的邊界進行重排,減少因為邊界對齊而造成的內(nèi)存損失。當程序創(chuàng)建幾百個甚至上千個結(jié)構(gòu)時,減少內(nèi)存浪費的要求就比程序的可讀性更為緊迫了。在這種情況下,在聲明中增加注釋可以彌補可讀性方面的損失。

運行結(jié)果:

 

 

 

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多