水滴石穿C語(yǔ)言之C語(yǔ)言的底層操作_第1頁(yè)
水滴石穿C語(yǔ)言之C語(yǔ)言的底層操作_第2頁(yè)
水滴石穿C語(yǔ)言之C語(yǔ)言的底層操作_第3頁(yè)
水滴石穿C語(yǔ)言之C語(yǔ)言的底層操作_第4頁(yè)
水滴石穿C語(yǔ)言之C語(yǔ)言的底層操作_第5頁(yè)
已閱讀5頁(yè),還剩111頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

1、水滴石穿C語(yǔ)言之C語(yǔ)言的底層操作概述 HYPERLINK /key/1574/1574.html t _blank C語(yǔ)言的內(nèi)存模型基本上對(duì)應(yīng)了現(xiàn)在von Neumann(馮諾伊曼)計(jì)算機(jī)的實(shí)際存儲(chǔ)模型,很好的達(dá)到了對(duì)機(jī)器的 HYPERLINK /key/4883/14883.html t _blank 映射,這是C/C+適合做底層開(kāi)發(fā)的主要原因,另外,C語(yǔ)言適合做底層開(kāi)發(fā)還有另外一個(gè)原因,那就是C語(yǔ)言對(duì)底層操作做了很多的的支持,提供了很多比較底層的功能。下面結(jié)合問(wèn)題分別進(jìn)行闡述。問(wèn)題:移位操作在運(yùn)用移位操作符時(shí),有兩個(gè)問(wèn)題必須要清楚:(1)、在右移操作中,騰空位是填 0 還是符號(hào)位;(2)、

2、什么數(shù)可以作移位的位數(shù)。答案與分析:和移位的位數(shù) 左移: 變量名移位的位數(shù) 經(jīng)過(guò)移位后, 一端的位被擠掉,而另一端空出的位以0 填補(bǔ),在C語(yǔ)言中的移位不是循環(huán)移動(dòng)的。(1) 第一個(gè)問(wèn)題的答案很簡(jiǎn)單,但要根據(jù)不同的情況而定。如果被移位的是無(wú)符號(hào)數(shù),則填 0 。如果是有符號(hào)數(shù),那么可能填 0 或符號(hào)位。如果你想解決右移操作中騰空位的填充問(wèn)題,就把變量聲明為無(wú)符號(hào)型,這樣騰空位會(huì)被置 0。(2) 第二個(gè)問(wèn)題的答案也很簡(jiǎn)單:如果移動(dòng) n 位,那么移位的位數(shù)要不小于 0 ,并且一定要小于 n 。這樣就不會(huì)在一次操作中把所有數(shù)據(jù)都移走。比如,如果整型數(shù)據(jù)占 32 位,n 是一整型數(shù)據(jù),則 n 31 和 n

3、 0 都合法,而 n 32 和 n 1 不可能為 0 。 問(wèn)題:位段結(jié)構(gòu)struct RPR_ATD_TLV_HEADERULONG res1:6;ULONG HYPERLINK /key/4018/19018.html t _blank type:10;ULONG res1:6;ULONG length:10; ;位段結(jié)構(gòu)是一種特殊的結(jié)構(gòu), 在需按位訪問(wèn)一個(gè)字節(jié)或字的多個(gè)位時(shí), 位結(jié)構(gòu)比按位運(yùn)算符更加方便。 位結(jié)構(gòu)定義的一般形式為: struct位結(jié)構(gòu)名 數(shù)據(jù)類型 變量名: 整型常數(shù); 數(shù)據(jù)類型 變量名: 整型常數(shù); 位結(jié)構(gòu)變量;其中: 整型常數(shù)必須是非負(fù)的整數(shù), 范圍是015, 表示二進(jìn)制

4、位的個(gè)數(shù), 即表示有多少位。變量名是選擇項(xiàng), 可以不命名, 這樣規(guī)定是為了排列需要。 例如: 下面定義了一個(gè)位結(jié)構(gòu)。struct unsigned incon: 8; /*incon占用低字節(jié)的07共8位*/ unsigned txcolor: 4;/*txcolor占用高字節(jié)的03位共4位*/ unsigned bgcolor: 3;/*bgcolor占用高字節(jié)的46位共3位*/ unsigned blink: 1; /*blink占用高字節(jié)的第7位*/ ch;位結(jié)構(gòu)成員的訪問(wèn)與結(jié)構(gòu)成員的訪問(wèn)相同。 例如: 訪問(wèn)上例位結(jié)構(gòu)中的bgcolor成員可寫(xiě)成: ch.bgcolor位結(jié)構(gòu)成員可以與其

5、它結(jié)構(gòu)成員一起使用。 按位訪問(wèn)與設(shè)置,方便&節(jié)省例如:struct info char name8; int age; struct addr address; float pay; unsigned state: 1; unsigned pay: 1; workers;上例的結(jié)構(gòu)定義了關(guān)于一個(gè)工從的信息。其中有兩個(gè)位結(jié)構(gòu)成員, 每個(gè)位結(jié)構(gòu)成員只有一位, 因此只占一個(gè)字節(jié)但保存了兩個(gè)信息, 該字節(jié)中第一位表示工人的狀態(tài), 第二位表示工資是否已發(fā)放。由此可見(jiàn)使用位結(jié)構(gòu)可以節(jié)省存貯空間。 注意不要超過(guò)值限制問(wèn)題:字節(jié)對(duì)齊讓偶們先來(lái)看下面這個(gè)結(jié)構(gòu)體:struct stu1 int a; char b

6、; 來(lái)看看sizeof(stu)的結(jié)果為多少? 怎么是8啊?你先別急,再來(lái)看下一個(gè)例子:struct stu2 char b; int a; 這個(gè)sizeof(stu2)是多少?怎么還是8啊?現(xiàn)在創(chuàng)建一個(gè)結(jié)構(gòu)體變量stu2 s2 a , 0 x12345678h; stu1 s1 0 x12345678, a 運(yùn)行DEGUG,怎么樣發(fā)現(xiàn)了什么?在第一個(gè)結(jié)構(gòu)體中char b的后面內(nèi)存有三個(gè)字節(jié)是添了數(shù)據(jù)的.也就是這樣 78 56 34 12 61 cc cc cc 而在第二個(gè)結(jié)構(gòu)體中CHAR B的后面內(nèi)存中也添加了數(shù)據(jù).61 cc cc cc 78 56 34 12這又是怎么回事呢?需要字節(jié)對(duì)齊

7、當(dāng)然有設(shè)計(jì)者的考慮了,原來(lái)這樣有助于加快計(jì)算機(jī)的存取速度,否則就得多花指令周期了。所以,編譯器通常都會(huì)對(duì)結(jié)構(gòu)體進(jìn)行處理,讓寬度為2的基本數(shù)據(jù)類型(short等)都位于能被2整除的地址上,讓寬度為4的基本數(shù)據(jù)類型(int等)都位于能被4整除的地址上。正是因?yàn)槿绱藘蓚€(gè)數(shù)中間就可能需要加入填充字節(jié),所以結(jié)構(gòu)體占的內(nèi)存空間就增長(zhǎng)了。其實(shí)字節(jié)對(duì)齊的細(xì)節(jié)和具體編譯器實(shí)現(xiàn)相關(guān),但一般而言,滿足三個(gè)準(zhǔn)則:結(jié)構(gòu)體變量的首地址能夠被其最寬基本類型成員的大小所整除;2) 結(jié)構(gòu)體每個(gè)成員相對(duì)于結(jié)構(gòu)體首地址的偏移量都是成員大小的整數(shù)倍,如有需要編譯器會(huì)在成員之間加上填充字節(jié);例如上面第二個(gè)結(jié)構(gòu)體變量的地址空間。 3)

8、結(jié)構(gòu)體的總大小為結(jié)構(gòu)體最寬基本類型成員大小的整數(shù)倍,如有需要編譯器會(huì)在最末一個(gè)成員之后加上填充字節(jié)。例如上面第一個(gè)結(jié)構(gòu)體變量。(哎呀!知道!真多嘴?。┈F(xiàn)在就可以解釋上面的問(wèn)題了,第一個(gè)結(jié)構(gòu)體變量中成員變量最寬為4(SIZEOF(INT) = 4),所以S1變量首地址必須能被整除。(不信你試試?。㏒1的大小也應(yīng)該為4的整數(shù)倍。但是現(xiàn)在s1中有 4 + 1 的空間,所以為了滿足第三個(gè)條件就在char b的后面在加上三個(gè)字節(jié)的空間以湊夠8個(gè)字節(jié)空間。第二個(gè)結(jié)構(gòu)體變量S2中 成員變量最大寬度為4,而且按照以前的理解int a 的地址和s2的地址相差5個(gè)字節(jié),但是為了滿足第而個(gè)條件(相差的距離-偏移地址

9、必須是4的整數(shù)倍)所以在char b的后面添加了三個(gè)字節(jié)的空間以保證int a的偏移地址是4的整數(shù)倍即為4。至于涉及到結(jié)構(gòu)體嵌套的問(wèn)題,你也可以用上述方法總結(jié)的,只不過(guò)你把被嵌套的結(jié)構(gòu)體在原地展開(kāi)就行了,不過(guò)在計(jì)算偏移地址的時(shí)候被嵌套的結(jié)構(gòu)體是不能原地展開(kāi)的必須當(dāng)作整體。嘿嘿!偶申明一點(diǎn),上述三條建議不是偶說(shuō)的,是做編譯器的工程師總結(jié)出來(lái)的,偶只是借用而已。我在使用VC編程的過(guò)程中,有一次調(diào)用DLL中定義的結(jié)構(gòu)時(shí),發(fā)覺(jué)結(jié)構(gòu)都亂掉了,完全不能讀取正確的值,后來(lái)發(fā)現(xiàn)這是因?yàn)镈LL和調(diào)用程序使用的字節(jié)對(duì)齊選項(xiàng)不同,那么我想問(wèn)一下,字節(jié)對(duì)齊究竟是怎么一回事?答案與分析:為了能使CPU對(duì)變量進(jìn)行高效快速

10、的訪問(wèn),變量的起始地址應(yīng)該具有某些特性,即所謂的“對(duì)齊”。例如對(duì)于4字節(jié)的int類型變量,其起始地址應(yīng)位于4字節(jié)邊界上,即起始地址能夠被4整除。關(guān)于字節(jié)對(duì)齊:1、 當(dāng)不同的結(jié)構(gòu)使用不同的字節(jié)對(duì)齊定義時(shí),可能導(dǎo)致它們之間交互變得很困難。2、 在跨CPU進(jìn)行通信時(shí),可以使用字節(jié)對(duì)齊來(lái)保證唯一性,諸如通訊協(xié)議、寫(xiě)驅(qū)動(dòng)程序時(shí)候寄存器的結(jié)構(gòu)等。三種對(duì)齊方式:1、 自然對(duì)齊方式( HYPERLINK /key/4870/19870.html t _blank Natural Alignment):與該數(shù)據(jù)類型的大小相等。2、 指定對(duì)齊方式 :#pragma pack(8) /指定Align為 8;#pra

11、gma pack() /恢復(fù)到原先值3、 實(shí)際對(duì)齊方式:Actual Align = min ( Order Align, Natual Align )對(duì)于復(fù)雜數(shù)據(jù)類型(比如結(jié)構(gòu)等):實(shí)際對(duì)齊方式是其成員最大的實(shí)際對(duì)齊方式:Actual Align = max( Actual align1,2,3,)編譯器的填充規(guī)律:1、 成員為成員Actual Align的整數(shù)倍,在前面加Padding。成員Actual Align = min( 結(jié)構(gòu)Actual Align,設(shè)定對(duì)齊方式)2、 結(jié)構(gòu)為結(jié)構(gòu)Actual Align的整數(shù)倍,在后面加Padding.例子分析:#pragma pack(8) /

12、指定Align為 8struct STest1char ch1; long lo1;char ch2; test1;#pragma pack()現(xiàn)在Align of STest1 = 4 , sizeof STest1 = 12 ( 4 * 3 )test1在內(nèi)存中的排列如下( FF 為 padding ):00 - - - 04 - - - 08 - - - 12 - - -01 FF FF FF 01 01 01 01 01 FF FF FF ch1 - lo1 - ch2#pragma pack(2) /指定Align為 2struct STest2char ch3;STest1 tes

13、t; test2;#pragma pack()現(xiàn)在 Align of STest1 = 2, Align of STest2 = 2 , sizeof STest2 = 14 ( 7 * 2 )test2在內(nèi)存中的排列如下:00 - - - 04 - - - 08 - - - 12 - - -02 FF 01 FF FF FF 01 01 01 01 01 FF FF FF ch3 ch1 - lo1 - ch2注意事項(xiàng):1、 這樣一來(lái),編譯器無(wú)法為特定平臺(tái)做優(yōu)化,如果效率非常重要,就盡量不要使用#pragma pack,如果必須使用,也最好僅在需要的地方進(jìn)行設(shè)置。2、 需要加pack的地方一

14、定要在定義結(jié)構(gòu)的頭文件中加,不要依賴命令行選項(xiàng),因?yàn)槿绻芏嗳耸褂迷擃^文件,并不是每個(gè)人都知道應(yīng)該pack。這特別表現(xiàn)在為別人開(kāi)發(fā)庫(kù)文件時(shí),如果一個(gè)庫(kù)函數(shù)使用了struct作為其參數(shù),當(dāng)調(diào)用者與庫(kù)文件開(kāi)發(fā)者使用不同的pack時(shí),就會(huì)造成錯(cuò)誤,而且該類錯(cuò)誤很不好查。3、 在VC及BC提供的頭文件中,除了能正好對(duì)齊在四字節(jié)上的結(jié)構(gòu)外,都加了pack,否則我們編的Windows程序哪一個(gè)也不會(huì)正常運(yùn)行。4、 在 #pragma pack(n) 后一定不要include其他頭文件,若包含的頭文件中改變了align值,將產(chǎn)生非預(yù)期結(jié)果。5、 不要多人同時(shí)定義一個(gè) HYPERLINK /key/837/8

15、37.html t _blank 數(shù)據(jù)結(jié)構(gòu)。這樣可以保證一致的pack值。問(wèn)題:按位運(yùn)算符 C語(yǔ)言和其它高級(jí)語(yǔ)言不同的是它完全支持按位運(yùn)算符。這與匯編語(yǔ)言的位操作有些相似。 C中按位運(yùn)算符列出如下: 操作符 作用 & 位邏輯與 | 位邏輯或 位邏輯異或 - 位邏輯反 右移 左移 注意:1、按位運(yùn)算是對(duì)字節(jié)或字中的實(shí)際位進(jìn)行檢測(cè)、設(shè)置或移位, 它只適用于字符型和整數(shù)型變量以及它們的變體, 對(duì)其它數(shù)據(jù)類型不適用。 2、關(guān)系運(yùn)算和邏輯運(yùn)算表達(dá)式的結(jié)果只能是1或0。 而按位運(yùn)算的結(jié)果可以取0或1以外的值。 要注意區(qū)別按位運(yùn)算符和邏輯運(yùn)算符的不同, 例如, 若x=7, 則x&8 的值為真(兩個(gè)非零值相與

16、仍為非零), 而x&8的值為0。 3、 | 與 |,&與&,與! 的關(guān)系&、| 和 操作符把它們的操作數(shù)當(dāng)作一個(gè)為序列,按位單獨(dú)進(jìn)行操作。比如:10 & 12 = 8,這是因?yàn)?操作符把 10 和 12 當(dāng)作二進(jìn)制描述 1010 和 1100 ,所以只有當(dāng)兩個(gè)操作數(shù)的相同位同時(shí)為 1 時(shí),產(chǎn)生的結(jié)果中相應(yīng)位才為 1 。同理,10 | 12 = 14 ( 1110 ),通過(guò)補(bǔ)碼運(yùn)算,10 = -11 ( 11.110101 )。 &、| 和!操作符把它們的操作數(shù)當(dāng)作真或假,并且用 0 代表假,任何非 0 值被認(rèn)為是真。它們返回 1 代表真,0 代表假,對(duì)于&和|操作符,如果左側(cè)的操作數(shù)的值就可以

17、決定表達(dá)式的值,它們根本就不去計(jì)算右側(cè)的操作數(shù)。所以,!10 是 0 ,因?yàn)?10 非 0 ;10 & 12 是 1 ,因?yàn)?10 和 12 均非 0 ;10 | 12也是 1 ,因?yàn)?10 非 0 。并且,在最后一個(gè)表達(dá)式中,12 根本就沒(méi)被計(jì)算,在表達(dá)式 10 | f( ) 中也是如此。水滴石穿C語(yǔ)言之extern聲明辨析1 基本解釋 HYPERLINK /key/263/495263.html t _blank extern可以置于變量或者 HYPERLINK /key/1456/6456.html t _blank 函數(shù)前,以標(biāo)示變量或者函數(shù)的定義在別的文件中,提示編譯器遇到此變量和函

18、數(shù)時(shí)在其他模塊中尋找其定義。另外,extern也可用來(lái)進(jìn)行鏈接指定。2 問(wèn)題:extern 變量在一個(gè)源文件里定義了一個(gè)數(shù)組:char a6;在另外一個(gè)文件里用下列語(yǔ)句進(jìn)行了聲明:extern char *a;請(qǐng)問(wèn),這樣可以嗎? 答案與分析:1)、不可以,程序運(yùn)行時(shí)會(huì)告訴你非法訪問(wèn)。原因在于,指向類型T的指針并不等價(jià)于類型T的數(shù)組。extern char *a聲明的是一個(gè)指針變量而不是字符數(shù)組,因此與實(shí)際的定義不同,從而造成運(yùn)行時(shí)非法訪問(wèn)。應(yīng)該將聲明改為extern char a 。2)、例子分析如下,如果a = abcd,則外部變量a=0 x61626364 (abcd的ASCII碼值),*

19、a顯然沒(méi)有意義,如下圖:顯然a指向的空間(0 x61626364)沒(méi)有意義,易出現(xiàn)非法內(nèi)存訪問(wèn)。3)、這提示我們,在使用extern時(shí)候要嚴(yán)格對(duì)應(yīng)聲明時(shí)的格式,在實(shí)際編程中,這樣的錯(cuò)誤屢見(jiàn)不鮮。4)、extern用在變量聲明中常常有這樣一個(gè)作用,你在*.c文件中聲明了一個(gè)全局的變量,這個(gè)全局的變量如果要被引用,就放在*.h中并用extern來(lái)聲明。3 問(wèn)題:extern 函數(shù)1常常見(jiàn)extern放在函數(shù)的前面成為函數(shù)聲明的一部分,那么, HYPERLINK /key/1574/1574.html t _blank C語(yǔ)言的關(guān)鍵字extern在函數(shù)的聲明中起什么作用?答案與分析:如果函數(shù)的聲明中

20、帶有關(guān)鍵字extern,僅僅是暗示這個(gè)函數(shù)可能在別的源文件里定義,沒(méi)有其它作用。即下述兩個(gè)函數(shù)聲明沒(méi)有明顯的區(qū)別:extern int f(); 和int f();當(dāng)然,這樣的用處還是有的,就是在程序中取代include “*.h”來(lái)聲明函數(shù),在一些復(fù)雜的項(xiàng)目中,我比較習(xí)慣在所有的函數(shù)聲明前添加extern修飾。4 問(wèn)題:extern 函數(shù)2當(dāng)函數(shù)提供方單方面修改函數(shù)原型時(shí),如果使用方不知情繼續(xù)沿用原來(lái)的extern申明,這樣編譯時(shí)編譯器不會(huì)報(bào)錯(cuò)。但是在運(yùn)行過(guò)程中,因?yàn)樯倭嘶蛘叨嗔溯斎雲(yún)?shù),往往會(huì)照成系統(tǒng)錯(cuò)誤,這種情況應(yīng)該 HYPERLINK /key/4203/549203.html t _

21、blank 如何解決?答案與分析:目前業(yè)界針對(duì)這種情況的處理沒(méi)有一個(gè)很完美的方案,通常的做法是提供方在自己的xxx_pub.h中提供對(duì)外部接口的聲明,然后調(diào)用方include該頭文件,從而省去extern這一步。以避免這種錯(cuò)誤。寶劍有雙鋒,對(duì)extern的應(yīng)用,不同的場(chǎng)合應(yīng)該選擇不同的做法。5 問(wèn)題:extern “C”在C+環(huán)境下使用C函數(shù)的時(shí)候,常常會(huì)出現(xiàn)編譯器無(wú)法找到obj模塊中的C函數(shù)定義,從而導(dǎo)致鏈接失敗的情況,應(yīng)該如何解決這種情況呢?答案與分析: HYPERLINK /key/4186/504186.html t _blank C+語(yǔ)言在編譯的時(shí)候?yàn)榱私鉀Q函數(shù)的多態(tài)問(wèn)題,會(huì)將函數(shù)名

22、和參數(shù)聯(lián)合起來(lái)生成一個(gè)中間的函數(shù)名稱,而C語(yǔ)言則不會(huì),因此會(huì)造成鏈接時(shí)找不到對(duì)應(yīng)函數(shù)的情況,此時(shí)C函數(shù)就需要用extern “C”進(jìn)行鏈接指定,這告訴編譯器,請(qǐng)保持我的名稱,不要給我生成用于鏈接的中間函數(shù)名。下面是一個(gè)標(biāo)準(zhǔn)的寫(xiě)法:/在.h文件的頭上#ifdef _cplusplus#if _cplusplusextern C#endif#endif /* _cplusplus */ /.h文件結(jié)束的地方#ifdef _cplusplus#if _cplusplus#endif#endif /* _cplusplus */水滴石穿C語(yǔ)言之static辨析1、概述static 聲明的變量在C語(yǔ)言中

23、有兩方面的特征:1)、變量會(huì)被放在程序的全局存儲(chǔ)區(qū)中,這樣可以在下一次調(diào)用的時(shí)候還可以保持原來(lái)的賦值。這一點(diǎn)是它與堆棧變量和堆變量的區(qū)別。2)、變量用static告知編譯器,自己僅僅在變量的作用范圍內(nèi)可見(jiàn)。這一點(diǎn)是它與全局變量的區(qū)別。2、問(wèn)題:Static的理解關(guān)于static變量,請(qǐng)選擇下面所有說(shuō)法正確的內(nèi)容:A、若全局變量?jī)H在單個(gè)C文件中訪問(wèn),則可以將這個(gè)變量 HYPERLINK /key/4425/49425.html t _blank 修改為靜態(tài)全局變量,以降低 HYPERLINK /key/4819/54819.html t _blank 模塊間的耦合度;B、若全局變量?jī)H由單個(gè)函數(shù)訪

24、問(wèn),則可以將這個(gè)變量改為該函數(shù)的靜態(tài)局部變量,以降低模塊間的耦合度;C、設(shè)計(jì)和使用訪問(wèn)動(dòng)態(tài)全局變量、靜態(tài)全局變量、靜態(tài)局部變量的函數(shù)時(shí),需要考慮重入問(wèn)題;D、靜態(tài)全局變量過(guò)大,可那會(huì)導(dǎo)致堆棧溢出。 答案與分析:對(duì)于A,B:根據(jù)本篇概述部分的說(shuō)明b),我們知道,A,B都是正確的。對(duì)于C:根據(jù)本篇概述部分的說(shuō)明a),我們知道,C是正確的(所謂的函數(shù)重入問(wèn)題,下面會(huì)詳細(xì)闡述)。對(duì)于D:靜態(tài)變量放在程序的全局?jǐn)?shù)據(jù)區(qū),而不是在堆棧中分配,所以不可能導(dǎo)致堆棧溢出,D是錯(cuò)誤的。因此,答案是A、B、C。3、問(wèn)題:不可重入函數(shù)曾經(jīng)設(shè)計(jì)過(guò)如下一個(gè)函數(shù),在代碼檢視的時(shí)候被提醒有bug,因?yàn)檫@個(gè)函數(shù)是不可重入的,為什

25、么?unsigned int sum_int( unsigned int base )unsigned int index;static unsigned int sum = 0; / 注意,是static類型的。 for (index = 1; index 1:int *(*a5)(int, char*);2:void (*b10) (void (*)();3. doube(*)() (*pa)9;答案與分析:對(duì)復(fù)雜變量建立一個(gè)類型別名的方法很簡(jiǎn)單,你只要在傳統(tǒng)的變量聲明表達(dá)式里用類型名替代變量名,然后把關(guān)鍵字typedef加在該語(yǔ)句的開(kāi)頭就行了。 (注:如果你對(duì)有些變量的聲明語(yǔ)法感到難以理

26、解,請(qǐng)參閱本系列第十篇的相關(guān)內(nèi)容)。1:int *(*a5)(int, char*);/pFun是我們建的一個(gè)類型別名typedef int *(*pFun)(int, char*); /使用定義的新類型來(lái)聲明對(duì)象,等價(jià)于int* (*a5)(int, char*);pFun a5; 2:void (*b10) (void (*)();/首先為上面表達(dá)式藍(lán)色部分聲明一個(gè)新類型typedef void (*pFunParam)();/整體聲明一個(gè)新類型typedef void (*pFun)(pFunParam);/使用定義的新類型來(lái)聲明對(duì)象,等價(jià)于void (*b10) (void (*)()

27、;pFun b10;3. doube(*)() (*pa)9; /首先為上面表達(dá)式藍(lán)色部分聲明一個(gè)新類型typedef double(*pFun)();/整體聲明一個(gè)新類型typedef pFun (*pFunParam)9;/使用定義的新類型來(lái)聲明對(duì)象,等價(jià)于doube(*)() (*pa)9;pFunParam pa;水滴石穿C語(yǔ)言之編譯器引出的問(wèn)題本節(jié)主要探討C編譯器下面兩方面的特點(diǎn)所引發(fā)的一系列常見(jiàn)的編程問(wèn)題。 對(duì)C文件進(jìn)行分別編譯:C程序通常由幾個(gè) HYPERLINK /key/173/15173.html t _blank 小程序(.c文件)組成,編譯器將這幾個(gè)小程序分別編譯,然后

28、通過(guò)鏈接程序?qū)⑺鼈兘M合在一起形成一個(gè)目標(biāo)代碼。由于編譯器每次只能編譯一個(gè)文件,因此它不能立即檢查需要幾個(gè)源文件配合才能發(fā)現(xiàn)的錯(cuò)誤。 對(duì) HYPERLINK /key/1456/6456.html t _blank 函數(shù)的參數(shù)和返回值建立臨時(shí)變量C編譯器會(huì)對(duì)函數(shù)的參數(shù)建立臨時(shí)參數(shù),也可能會(huì)對(duì)函數(shù)的返回值隱含傳遞一個(gè)指針。因?yàn)檫@些臨時(shí)變量的隱含性存在,使得在某些情況下,特別是有指針存在的時(shí)候,會(huì)引發(fā)一系列的問(wèn)題。 C文件中所包含的頭文件會(huì)和 HYPERLINK /key/1574/1574.html t _blank C語(yǔ)言一同編譯C語(yǔ)言中被包含的頭文件是和.c文件一起編譯的,頭文件中的問(wèn)題會(huì)反映

29、到.c文件的編譯中。 問(wèn)題:C文件的分別編譯我有一個(gè)數(shù)組a定義在f1.c中,但是我想在f2.c中計(jì)算它的元素個(gè)數(shù),用sizeof可以達(dá)到這個(gè)目的嗎? 答案與分析:答案是否定的,你沒(méi)有辦法達(dá)到目的,本質(zhì)原因是sizeof操作符只是在“編譯時(shí)(compile time)”起作用,而C語(yǔ)言的編譯單位是每次單個(gè).c文件進(jìn)行編譯(其它語(yǔ)言也都如此)。因此,sizeof可以確定同一個(gè)源文件中某個(gè)數(shù)組的大小,但是對(duì)于定義在另一個(gè)源文件中的數(shù)組它無(wú)能為力了,因?yàn)槟且呀?jīng)是“運(yùn)行時(shí)(run time)”才能確定的事情了。一件事情要想做,總會(huì)有辦法的,下面提供有三種可選的辦法來(lái)解決這個(gè)問(wèn)題:1)、定義一個(gè)全局變量,

30、讓它記住數(shù)組的大小,在另外一個(gè).c文件中我們通過(guò)訪問(wèn)這個(gè)全局變量來(lái)得到數(shù)組的大小信息(好像有點(diǎn)小題大做得不償失_)。2)、在某個(gè).h文件中用宏定義數(shù)組的大小,例如#define ARRAY_SIZE 50,然后在兩個(gè)源文件中都包含這個(gè).h文件,通過(guò)直接訪問(wèn)ARRAY_SIZE來(lái)得到定義在不同.c文件中的數(shù)組的大小。3)、設(shè)置數(shù)組的最后一個(gè)元素為特殊值,例如0,-1,NULL等,然后我們通過(guò)遍歷數(shù)組來(lái)尋找這個(gè)特殊的結(jié)尾元素,從而判斷數(shù)組的長(zhǎng)度(這個(gè)辦法效率低,也是笨笨的)。 問(wèn)題:函數(shù)返回值隱含傳遞指針下面的代碼可以正常工作,但是在程序結(jié)束時(shí)會(huì)有一個(gè)致命錯(cuò)誤產(chǎn)生。究竟是什么原因呢?struct

31、listchar *item;struct list *next;main (argc, argv).答案與分析:原因很簡(jiǎn)單,稍微注意一點(diǎn)不難發(fā)現(xiàn),在定義結(jié)構(gòu)list的右花括弧后面加一個(gè)分號(hào)就可以解決這個(gè)問(wèn)題:struct list char *item;struct list *next;;/缺了這個(gè)分號(hào)可不行!好了,問(wèn)題是解決了,但,你知道這個(gè)錯(cuò)誤究竟導(dǎo)致了什么致命問(wèn)題嗎?問(wèn)題不是表面上那么簡(jiǎn)單的,OK,讓我們來(lái)看看事情背后的真相。首先看一看下面這段代碼:VOID Func ( struct HYPERLINK /key/3948/3948.html t _blank my_struct

32、stX).struct my_struct stY = .;Func (stY);當(dāng)調(diào)用函數(shù)Func的時(shí)候,是把結(jié)構(gòu)變量stY的值拷貝一份到調(diào)用棧中,從而作為參數(shù)傳遞給函數(shù)FUNC的,這個(gè)叫做C語(yǔ)言的參數(shù)值傳遞。我相信這個(gè)你一定很清楚,那么,你應(yīng)該知道:如果函數(shù)的返回值是結(jié)構(gòu)變量的話,函數(shù)應(yīng)該如何將值返回給調(diào)用者呢?且看下面這段代碼:struct my_structFunc (VOID).struct my_struct stY = Func();此時(shí)函數(shù)Func的返回值是一個(gè)結(jié)構(gòu)類型的值,這個(gè)返回值被放在內(nèi)存中一個(gè)陰暗恐怖的地方,同時(shí)安排了一個(gè)指針指向這個(gè)地方(暫時(shí)稱為“神秘指針”),而這個(gè)

33、指針會(huì)由C語(yǔ)言的編譯器作為一個(gè)隱藏參數(shù)傳遞給函數(shù)Func。當(dāng)函數(shù)Func返回時(shí),編譯器生成的代碼將這個(gè)由隱藏指針指向的內(nèi)存區(qū)的值拷貝到返回結(jié)構(gòu)stY中,從而完成將結(jié)構(gòu)變量值返回給調(diào)用者。你明白了上述所講的東東,那么今天問(wèn)題的真正原因也就呼之欲出了:因?yàn)閟truct list .的定義后面沒(méi)有加分號(hào),導(dǎo)致主函數(shù)main (argc, argv)被編譯器理解為是一個(gè)返回值為結(jié)構(gòu)變量的函數(shù),從而期望得到除了argc和argv以外的第三個(gè)參數(shù),也就是我們上面提到的那個(gè)隱含傳入的“神秘指針”??墒?,大家知道,這里函數(shù)是main函數(shù),main函數(shù)的參數(shù)是由程序中的啟動(dòng)代碼(startup code)提供的

34、。而啟動(dòng)代碼當(dāng)然認(rèn)為main()天生就應(yīng)該只得到兩個(gè)參數(shù),要“神秘指針”,當(dāng)然沒(méi)有,如此一來(lái), main()在返回時(shí)自作主張地去調(diào)用棧中訪問(wèn)它的那個(gè)并不存在的第三個(gè)參數(shù)(即神秘指針),這樣導(dǎo)致非法訪問(wèn),產(chǎn)生致命問(wèn)題。這才是這個(gè)問(wèn)題的真正根源。建議: 1)、盡量將結(jié)構(gòu)變量的指針而不是結(jié)構(gòu)本身作為函數(shù)參數(shù),否則 HYPERLINK /key/696/450696.html t _blank 函數(shù)調(diào)用時(shí)內(nèi)存拷貝的開(kāi)銷可不小,尤其是對(duì)那些調(diào)用頻繁、結(jié)構(gòu)體大的情況。 2)、結(jié)構(gòu)定義的后面一定要加分號(hào),經(jīng)過(guò)上面我的大段講述,我相信你不會(huì)犯相同的錯(cuò)誤問(wèn)題:編譯器會(huì)給函數(shù)的參數(shù)隱含制造臨時(shí)副本 請(qǐng)問(wèn)運(yùn)行下面的

35、Test函數(shù)會(huì)有什么樣的結(jié)果?void GetMemory2(char *p, int num)*p = (char *)malloc(num);void Test(void)char *str = NULL;GetMemory(&str, 100);strcpy(str, hello);printf(str);答案與分析:這是林銳的C/C+高質(zhì)量編程指南上面的例子,拿來(lái)用一下。這樣調(diào)用會(huì)產(chǎn)生如下兩個(gè)后果:1)、能夠輸出hello2)、內(nèi)存泄漏 另一個(gè)相關(guān)問(wèn)題:請(qǐng)問(wèn)運(yùn)行Test函數(shù)會(huì)有什么樣的結(jié)果?void GetMemory(char *p)p = (char *)malloc(100);v

36、oid Test(void) char *str = NULL;GetMemory(str);strcpy(str, hello world);printf(str);答案與分析:后果嚴(yán)重,運(yùn)行的結(jié)果是程序崩潰,通過(guò)運(yùn)行調(diào)試我們可以看到,經(jīng)過(guò)GetMemory后,Test函數(shù)中的 str仍舊是NULL??上攵徽{(diào)用strcpy(str, hello world);程序必然崩潰了事。原因分析: C編譯器總是會(huì)為函數(shù)的每個(gè)參數(shù)制作臨時(shí)副本,指針參數(shù)p的副本是 _p,編譯器使 _p = p。如果函數(shù)體內(nèi)的程序修改了_p的內(nèi)容,就導(dǎo)致參數(shù)p的內(nèi)容作相應(yīng)的修改。這就是指針可以用作輸出參數(shù)的原因。在本

37、例中,_p申請(qǐng)了新的內(nèi)存,只是把_p所指的內(nèi)存地址改變了,但是p絲毫未變。所以函數(shù)GetMemory并不能輸出任何東西,如果想要輸出動(dòng)態(tài)內(nèi)存,請(qǐng)使用指向指針的指針,或者,使用指向引用的指針。問(wèn)題:頭文件和包含它的.c文件一同編譯問(wèn) 下面的代碼非常短小,看起來(lái)毫無(wú)問(wèn)題,但編譯器會(huì)報(bào)告一個(gè)錯(cuò)誤,請(qǐng)問(wèn)問(wèn)題可能出現(xiàn)在什么地方?#include someheader.hint myint = 0;答案與分析:不用盯著int myint = 0;看,這一句賦值應(yīng)該是C語(yǔ)言中最簡(jiǎn)單的語(yǔ)句,問(wèn)題肯定不會(huì)出在它身上,那么問(wèn)題只可能出現(xiàn)在someheader.h中,最常見(jiàn)的就是該頭文件的最后一行的聲明(函數(shù)也好,

38、變量也好)沒(méi)有用分號(hào);結(jié)尾,那么編譯器會(huì)將它和myint變量結(jié)合起來(lái)考慮,自然就會(huì)出錯(cuò)了。這個(gè)問(wèn)題主要是提醒你,在定位問(wèn)題時(shí)思路要拓寬一點(diǎn),可能要考慮一下所包含的頭文件是否有問(wèn)題。結(jié)論:被包含的頭文件是和.c文件一起編譯的,頭文件中的問(wèn)題會(huì)反映到.c文件編譯中去的,切記。水滴石穿C語(yǔ)言之可變參數(shù)問(wèn)題 HYPERLINK /key/4647/354647.html t _blank 概述C語(yǔ)言中有一種長(zhǎng)度不確定的參數(shù),形如:,它主要用在參數(shù)個(gè)數(shù)不確定的函數(shù)中,我們最容易想到的例子是printf函數(shù)。原型:int printf( const HYPERLINK /key/3784/213784.h

39、tml t _blank char *format , argument. );使用例:printf(Enjoy yourself everyday!n);printf(The value is %d!n, value);這種可變參數(shù)可以說(shuō)是C語(yǔ)言一個(gè)比較難理解的部分,這里會(huì)由幾個(gè)問(wèn)題引發(fā)一些對(duì)它的分析。注意:在C+中有 HYPERLINK /key/2073/202073.html t _blank 函數(shù)重載(overload)可以用來(lái)區(qū)別不同函數(shù)參數(shù)的調(diào)用,但它還是不能表示任意數(shù)量的函數(shù)參數(shù)。問(wèn)題:printf的實(shí)現(xiàn)請(qǐng)問(wèn),如何自己實(shí)現(xiàn)printf函數(shù),如何處理其中的可變參數(shù)問(wèn)題? 答案與分

40、析:在標(biāo)準(zhǔn)C語(yǔ)言中定義了一個(gè)頭文件專門(mén)用來(lái)對(duì)付可變參數(shù)列表,它包含了一組宏,和一個(gè)va_list的typedef聲明。一個(gè)典型實(shí)現(xiàn)如下:typedef char* va_list;#define va_start(list) list = (char*)&va_alist#define va_end(list)#define va_arg(list, mode)(mode*) (list += sizeof(mode)-1 自己實(shí)現(xiàn)printf:#include int printf(char* format, )va_list ap;va_start(ap, format);int n =

41、vprintf(format, ap);va_end(ap);return n;問(wèn)題:運(yùn)行時(shí)才確定的參數(shù)有沒(méi)有辦法寫(xiě)一個(gè)函數(shù),這個(gè)函數(shù)參數(shù)的具體形式可以在運(yùn)行時(shí)才確定?答案與分析:目前沒(méi)有正規(guī)的解決辦法,不過(guò)獨(dú)門(mén)偏方倒是有一個(gè),因?yàn)橛幸粋€(gè)函數(shù)已經(jīng)給我們做出了這方面的榜樣,那就是main(),它的原型是:int main(int argc,char *argv);函數(shù)的參數(shù)是argc和argv。深入想一下,只能在運(yùn)行時(shí)確定參數(shù)形式,也就是說(shuō)你沒(méi)辦法從聲明中看到所接受的參數(shù),也即是參數(shù)根本就沒(méi)有固定的形式。常用的辦法是你可以通過(guò)定義一個(gè)void *類型的參數(shù),用它來(lái)指向?qū)嶋H的參數(shù)區(qū),然后在函數(shù)中根

42、據(jù)根據(jù)需要任意解釋它們的含義。這就是main函數(shù)中argv的含義,而argc,則用來(lái)表明實(shí)際的參數(shù)個(gè)數(shù),這為我們使用提供了進(jìn)一步的方便,當(dāng)然,這個(gè)參數(shù)不是必需的。雖然參數(shù)沒(méi)有固定形式,但我們必然要在函數(shù)中解析參數(shù)的意義,因此,理所當(dāng)然會(huì)有一個(gè)要求,就是調(diào)用者和被調(diào)者之間要對(duì)參數(shù)區(qū)內(nèi)容的格式,大小, HYPERLINK /key/1912/271912.html t _blank 有效性等所有方面達(dá)成一致,否則南轅北轍各說(shuō)各話就慘了。問(wèn)題:可變長(zhǎng)參數(shù)的傳遞有時(shí)候,需要編寫(xiě)一個(gè)函數(shù),將它的可變長(zhǎng)參數(shù)直接傳遞給另外的函數(shù),請(qǐng)問(wèn),這個(gè)要求能否實(shí)現(xiàn)?答案與分析:目前,你尚無(wú)辦法直接做到這一點(diǎn),但是我們可

43、以迂回前進(jìn),首先,我們定義被調(diào)用函數(shù)的參數(shù)為va_list類型,同時(shí)在調(diào)用函數(shù)中將可變長(zhǎng)參數(shù)列表轉(zhuǎn)換為va_list,這樣就可以進(jìn)行變長(zhǎng)參數(shù)的傳遞了??慈缦滤荆簐oid subfunc (char *fmt, va_list argp).arg = va_arg (fmt, argp); /* 從argp中逐一取出所要的參數(shù) */.void mainfunc (char *fmt, .)va_list argp;va_start (argp, fmt); /* 將可變長(zhǎng)參數(shù)轉(zhuǎn)換為va_list */subfunc (fmt, argp); /* 將va_list傳遞給子函數(shù) */va_end

44、 (argp);.問(wèn)題:可變長(zhǎng)參數(shù)中類型為函數(shù)指針我想使用va_arg來(lái)提取出可變長(zhǎng)參數(shù)中類型為函數(shù)指針的參數(shù),結(jié)果卻總是不正確,為什么?答案與分析:這個(gè)與va_arg的實(shí)現(xiàn)有關(guān)。一個(gè)簡(jiǎn)單的、演示版的va_arg實(shí)現(xiàn)如下:#define va_arg(argp, type) (*(type *)(argp) += sizeof(type) - sizeof(type)其中,argp的類型是char *。如果你想用va_arg從可變參數(shù)列表中提取出函數(shù)指針類型的參數(shù),例如int (*)(),則va_arg(argp, int (*)()被擴(kuò)展為:(*(int (*)() *)(argp) +=

45、sizeof (int (*)() -sizeof (int (*)()顯然,(int (*)() *)是無(wú)意義的。解決這個(gè)問(wèn)題的辦法是將函數(shù)指針用typedef定義成一個(gè)獨(dú)立的數(shù)據(jù)類型,例如:typedef int (*funcptr)();這時(shí)候再調(diào)用va_arg(argp, funcptr)將被擴(kuò)展為:(* (funcptr *)(argp) += sizeof (funcptr) - sizeof (funcptr)這樣就可以通過(guò)編譯檢查了。問(wèn)題:可變長(zhǎng)參數(shù)的獲取有這樣一個(gè)具有可變長(zhǎng)參數(shù)的函數(shù),其中有下列代碼用來(lái)獲取類型為float的實(shí)參:va_arg (argp, float);這樣

46、做可以嗎?答案與分析:不可以。在可變長(zhǎng)參數(shù)中,應(yīng)用的是加寬原則。也就是float類型被擴(kuò)展成 HYPERLINK /key/3008/213008.html t _blank double;char, short被擴(kuò)展成int。因此,如果你要去可變長(zhǎng)參數(shù)列表中原來(lái)為float類型的參數(shù),需要用va_arg(argp, double)。對(duì)char和short類型的則用va_arg(argp, int)。問(wèn)題:定義可變長(zhǎng)參數(shù)的一個(gè)限制為什么我的編譯器不允許我定義如下的函數(shù),也就是可變長(zhǎng)參數(shù),但是沒(méi)有任何的固定參數(shù)?int f (.).答案與分析:不可以。這是ANSI C 所要求的,你至少得定義一個(gè)

47、固定參數(shù)。這個(gè)參數(shù)將被傳遞給va_start(),然后用va_arg()和va_end()來(lái)確定所有實(shí)際調(diào)用時(shí)可變長(zhǎng)參數(shù)的類型和值。水滴石穿C語(yǔ)言之內(nèi)存使用問(wèn)題:內(nèi)存使用有人寫(xiě)了一個(gè)將整數(shù)轉(zhuǎn)換為字符串的函數(shù): HYPERLINK /key/3784/213784.html t _blank char *itoa (int n)char retbuf20;sprintf(retbuf, %d, n);return retbuf;如果我調(diào)用這個(gè)函數(shù):char *str5 = itoa(5),str5會(huì)是什么結(jié)果呢?答案分析:答案是不確定,可以確定的是肯定不是我們想要的 “5”。 retbuf定義在

48、函數(shù)體中,是一個(gè)局部變量,它的內(nèi)存空間位于棧(stack)中的某個(gè)位置,其作用范圍也僅限于在itoa()這個(gè)函數(shù)中。當(dāng)itoa()函數(shù)退出時(shí),retbuf在調(diào)用棧中的內(nèi)容將被收回,這時(shí),這塊內(nèi)存地址可能存放別的內(nèi)容。因此將retbuf這個(gè)局部變量返回給調(diào)用者是達(dá)不到預(yù)期的目的的。那么如何解決這個(gè)問(wèn)題呢,不用擔(dān)心,方法不但有,而且還不止一個(gè),下面就來(lái)闡述三種能解決這個(gè)問(wèn)題的辦法:1)、在itoa()函數(shù)內(nèi)部定義一個(gè)static char retbuf20,根據(jù) HYPERLINK /key/385/185385.html t _blank 靜態(tài)變量的特性,我們知道,這可以保證函數(shù)返回后retbu

49、f的空間不會(huì)被收回,原因是函數(shù)內(nèi)的靜態(tài)變量并不是放在棧中,而是放在程序中一個(gè)叫“.bss”段的地方,這個(gè)地方的內(nèi)容是不會(huì)因?yàn)楹瘮?shù)退出而被收回的。這種辦法確實(shí)能解決問(wèn)題,但是這種辦法同時(shí)也導(dǎo)致了itoa()函數(shù)變成了一個(gè)不可重入的函數(shù)(即不能保證相同的輸入肯定有相同的輸出),另外, retbuf 中的內(nèi)容會(huì)被下一次的調(diào)用結(jié)果所替代,這種辦法不值得推薦。2)、在itoa()函數(shù)內(nèi)部用malloc() 為retbuf申請(qǐng)內(nèi)存,并將結(jié)果存放其中,然后將retbuf返回給調(diào)用者。由于此時(shí)retbuf位于堆(heap)中,也不會(huì)隨著函數(shù)返回而 HYPERLINK /key/1028/186028.html

50、 t _blank 釋放,因此可以達(dá)到我們的目的。但是有這樣一種情況需要注意:itoa()函數(shù)的調(diào)用者在不需要retbuf的時(shí)候必須把它釋放,否則就造成內(nèi)存泄漏了,如果此函數(shù)和調(diào)用函數(shù)都是同一個(gè)人所寫(xiě),問(wèn)題不大,但如果不是,則比較容易會(huì)疏漏此釋放內(nèi)存的操作。3)、將函數(shù)定義為char *itoa(int n, char *retbuf),且retbuf的空間由調(diào)用者申請(qǐng)和釋放,itoa()只是將轉(zhuǎn)換結(jié)果存放到retbuf而已。這種辦法明顯比第一、二種方法要好,既避免了方法1對(duì)函數(shù)的影響,也避免了方法2對(duì)內(nèi)存分配釋放的影響,是目前一種比較通行的做法。擴(kuò)展分析:其實(shí)就這個(gè)問(wèn)題本身而言,我想大家都可

51、以立刻想到答案,關(guān)鍵在于對(duì)內(nèi)存這種敏感資源的正確和合理地利用,下面對(duì)內(nèi)存做一個(gè)簡(jiǎn)單的分析:1)、程序中有不同的內(nèi)存段,包括: HYPERLINK /key/879/215879.html t _blank .data - 已 HYPERLINK /key/1942/201942.html t _blank 初始化全局/靜態(tài)變量,在整個(gè)軟件執(zhí)行過(guò)程中有效;.bss - 未初始化全局/靜態(tài)變量,在整個(gè)軟件執(zhí)行過(guò)程中有效;.stack - 函數(shù)調(diào)用棧,其中的內(nèi)容在函數(shù)執(zhí)行期間有效,并由編譯器負(fù)責(zé)分配和收回;.heap - 堆,由程序顯式分配和收回,如果不收回就是內(nèi)存泄漏。2)、自己使用的內(nèi)存最好還是

52、自己申請(qǐng)和釋放。這可以說(shuō)是一個(gè)內(nèi)存分配和釋放的原則,比如說(shuō)上面解決辦法的第二種,由itoa()分配的內(nèi)存,最后由調(diào)用者釋放,就不是一個(gè)很好的辦法,還不如用第三種,由調(diào)用者自己申請(qǐng)和釋放。另外這個(gè)原則還有一層意思是說(shuō):如果你要使用一個(gè)指針,最好先確信它已經(jīng)指向合法內(nèi)存區(qū)了,如果沒(méi)有就得自己分配,要不就是非法指針訪問(wèn)。很多程序的致命錯(cuò)誤都是訪問(wèn)一個(gè)沒(méi)有指向合法內(nèi)存區(qū)的指針,這也包括空指針。問(wèn)題:內(nèi)存分配 & sizeof 我使用sizeof來(lái)計(jì)算一個(gè)指針變量,我希望得到這個(gè)指針變量所分配的內(nèi)存塊的大小,可以嗎?Char *p = NULL;int nMemSize = 0;p = malloc(1

53、024);nMemSize = sizeof(p);答案與分析: 答案是達(dá)不到你的要求,sizeof只能告訴你指針本身占用的內(nèi)存大小。指針?biāo)赶虻膬?nèi)存,如果是malloc分配的,sizeof 是沒(méi)有辦法知道的。換句話說(shuō),malloc分配的內(nèi)存是沒(méi)有辦法向內(nèi)存管理模塊進(jìn)行事后查詢的,當(dāng)然你可以自己編寫(xiě)代碼來(lái)維護(hù)。 問(wèn)題:棧內(nèi)存使用 下面程序運(yùn)行有什么問(wèn)題?char *GetString(void)char p = hello world;return p;/ 編譯器將提出警告void Test4(void)char *str = NULL;str = GetString();/ str 的內(nèi)容是

54、垃圾cout str = ulAllocedLines)/ * 當(dāng)前豎向空間已經(jīng)不夠了,通過(guò)realloc對(duì)其進(jìn)行擴(kuò)展。ulAllocedLines += 50; / 每次擴(kuò)展50行。 ppText = realloc (ppText, ulAllocedLines * (char *);if (NULL = ppText)return; / 內(nèi)存分配失敗,返回 ppTextulCurrLines+ = p; / 橫向“擴(kuò)展”,指向不定長(zhǎng)字符串 問(wèn)題:指針數(shù)組與數(shù)組指針與指向指針的指針指針和數(shù)組分別有如下的特征:指針:動(dòng)態(tài)分配,初始空間小數(shù)組:索引方便,初始空間大下面使用高維數(shù)組來(lái)說(shuō)明指針數(shù)組

55、、數(shù)組指針、指向指針的指針各自的適合場(chǎng)合。 多維靜態(tài)數(shù)組:各維均確定,適用于整體空間需求不大的場(chǎng)合,此結(jié)構(gòu)可方便索引,例a1040。 數(shù)組指針:低維確定,高維需要?jiǎng)討B(tài)生成的場(chǎng)合,例ax40。int (*pA)5. pA+. 指針數(shù)組:高維確定,低維需要?jiǎng)討B(tài)生成的場(chǎng)合,例a10y。int *pAi. pA0 = &b0 指向指針的指針:高、低維均需要?jiǎng)討B(tài)生成的場(chǎng)合,例axy。問(wèn)題:數(shù)組名相關(guān)問(wèn)題假設(shè)有一個(gè)整數(shù)數(shù)組a,a和&a的區(qū)別是什么?答案與分析:a = &a = &a0,數(shù)組名a不占用存儲(chǔ)空間。需要引用數(shù)組(非字符串)首地址的地方,我一般使用&a0,使用a容易和指針混淆,使用&a容易和非指針

56、變量混淆。區(qū)別在于二者的類型。對(duì)數(shù)組a的直接引用將產(chǎn)生一個(gè)指向數(shù)組第一個(gè)元素的指針,而&a的結(jié)果則產(chǎn)生一個(gè)指向全部數(shù)組的指針。例如:int a2 = 1, 2;int *p = 0;p = a; /* p指向a0所在的地方 */x = *p; /* x = a0 = 1*/p = &a; /* 編譯器會(huì)提示你錯(cuò)誤,*/*顯示整數(shù)指針與整數(shù)數(shù)組指針不一樣 */問(wèn)題:函數(shù)指針與指針函數(shù)請(qǐng)問(wèn):如下定義是什么意思:int *pF1();int (*pF2)();答案與分析:首先清楚它們的定義: 指針函數(shù),返回一個(gè)指針的函數(shù)。 函數(shù)指針,指向一個(gè)函數(shù)的指針??芍?pF1是一個(gè)指針函數(shù),它返回一個(gè)指向i

57、nt型數(shù)據(jù)的指針。 pF2是一個(gè)函數(shù)指針,它指向一個(gè)參數(shù)為空的函數(shù),這個(gè)函數(shù)返回一個(gè)整數(shù)。水滴石穿C語(yǔ)言之指針步進(jìn)辨析基本解釋通過(guò)上一篇的分析,我們已經(jīng)很清楚地知道:指針不是一個(gè)簡(jiǎn)單的類型,它是一個(gè)本身和所指向物相復(fù)合的類型。指針的算術(shù)運(yùn)算(如步進(jìn))與指針?biāo)赶蛭锏念愋兔芮邢嚓P(guān)。 問(wèn)題:指針步進(jìn) & HYPERLINK /key/3883/183883.html t _blank amp; 步進(jìn)單位下面的代碼中打印出的結(jié)果是幾?int arContext5 =0,1,2,3,4, i, *pAr;pAr = arContext;printf (%dn, *(pAr + 3 * HYPERLIN

58、K /key/2851/182851.html t _blank sizeof (int);答案與分析:這段代碼沒(méi)有正確答案,因?yàn)檫@段代碼是錯(cuò)的,printf將打出無(wú)法預(yù)測(cè)的內(nèi)存區(qū)的值,其中的原因如下:在 HYPERLINK /key/4077/169077.html t _blank C語(yǔ)言中,指針總是按照它所指向的對(duì)象的大小步進(jìn)。在上面的例子中,pAr是指向整數(shù)類型變量的指針,一個(gè)整數(shù)是4個(gè)字節(jié)(默認(rèn)CPU字長(zhǎng)是32位),pAr + 1就指向下一個(gè)整數(shù),也就是指針后移4個(gè)字節(jié),而不是說(shuō)將地址只移動(dòng)一個(gè)字節(jié)。因?yàn)镃語(yǔ)言編譯器知道每個(gè)指針的類型,因此對(duì)指針的運(yùn)算是會(huì)自動(dòng)把所指類型的Size考慮

59、進(jìn)去的。pAr + 3 * sizeof (int) = pAr + 3 * 4 = pAr + 12 ,因此pAr指向了數(shù)組的第 HYPERLINK /key/738/215738.html t _blank 13個(gè)整數(shù)元素。而數(shù)組本身才5個(gè)元素,pAr早已經(jīng)超出了界限,所指向的地方當(dāng)然就是無(wú)人可知道的東西了,具體指向什么東西,各種不同的編譯器互不相同??傊?,肯定不能打印出我們想要的值就是了。指針不是一個(gè)簡(jiǎn)單的類型,它是一個(gè)和指針?biāo)肝锏念愋拖鄰?fù)合的類型。因此,它的算術(shù)運(yùn)算與指針?biāo)肝锏念愋兔芮邢嚓P(guān), HYPERLINK /key/193/200193.html t _blank 在C+語(yǔ)言

60、中也是同樣。再比如下面的例子:int a8;int* p = a;int* q = p + 3;p+;指針的加減并不是指針本身的二進(jìn)制表示加減,要記住,指針是一個(gè)元素的地址,它每加一次,就指向下一個(gè)元素。所以:int* q = p + 3;q指向從p開(kāi)始的第三個(gè)整數(shù)。p+; p指向下一個(gè)整數(shù)。問(wèn)題:指針步進(jìn) & 步進(jìn)單位轉(zhuǎn)換我有一個(gè)char *類型的指針,恰好指向了一個(gè)int類型的值,我想讓這個(gè)指針跳過(guò)int指向下一個(gè)char,下面的代碼可以達(dá)到這個(gè)目的嗎?(int *)p)+;答案與分析:可以。首先我們要清楚語(yǔ)言中左值和右值的概念,C語(yǔ)言中左值是指可以放在“=”左側(cè),即可以被賦值,右值是可以

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論