《C專(zhuān)家編程》總結(jié)
《C專(zhuān)家編程》總結(jié)
開(kāi)始讀《C專(zhuān)家編程》之前,有一個(gè)很擔(dān)心的問(wèn)題:94年出的講語(yǔ)言的書(shū),在現(xiàn)在(201*)還有多少是適用的。因此,一邊讀,一邊用VS201*做實(shí)驗(yàn)。最后發(fā)現(xiàn)大部分內(nèi)容都還在用。讀完后,覺(jué)得最精彩的部分有二:一是講解如何理解聲明,二是深入地講解數(shù)組名與指針。下文是將看書(shū)過(guò)程中所做的筆記進(jìn)行的整理。
p.s:以下代碼均在VS201*測(cè)試過(guò)
1.使用無(wú)符號(hào)數(shù)時(shí)要特別注意(不推薦使用無(wú)符號(hào)數(shù))
當(dāng)無(wú)符號(hào)數(shù)與有符號(hào)數(shù)在同一條表達(dá)式中出現(xiàn)時(shí),有符號(hào)數(shù)會(huì)被轉(zhuǎn)換為無(wú)符號(hào)數(shù)。e.g:
intfeng=-1;
unsignedintyan=5;
boolresult=(fengvoid(*signal(intsig,void(*func)(int)))(int);//signal是一個(gè)函數(shù),該函數(shù)接收一個(gè)int,一個(gè)函數(shù)指針,并返回一個(gè)函數(shù)指針
5.左值與右值
左值通常表示存儲(chǔ)結(jié)果的地方(地址),其值在編譯時(shí)可知右值通常表示地址的內(nèi)容,其值通常要到運(yùn)行時(shí)才知道
6.指針與數(shù)組名不等同的情況(定義為數(shù)組,卻聲明為指針,或者反過(guò)來(lái))
前提知識(shí)(假設(shè)有定義:intarray[10],*ptr;):
a.使用數(shù)組名下標(biāo)訪(fǎng)問(wèn)(如:array[1]),會(huì)直接將數(shù)組名的地址加上偏移值作為變量的地址(即array[1]的地址)
b.使用指針下標(biāo)訪(fǎng)問(wèn)(如:ptr[1]),會(huì)先取指針指向的內(nèi)容,然后將這個(gè)內(nèi)容加上偏移值作為變量的地址(即ptr[1]的地址)
不等同的原因:
當(dāng)定義為數(shù)組,卻聲明為指針時(shí),相當(dāng)于用指針下標(biāo)訪(fǎng)問(wèn)的方法來(lái)解析一個(gè)數(shù)組名下標(biāo),即先取數(shù)組第0號(hào)元素的內(nèi)容,然后將這個(gè)內(nèi)容加上偏移值作為變量的地址,從而訪(fǎng)問(wèn)了不該訪(fǎng)問(wèn)的東西。反之亦然。7.指針與數(shù)組等同的情況
a.編譯器將表達(dá)式中的數(shù)組名當(dāng)作指向該數(shù)組第0號(hào)元素的指針,下標(biāo)當(dāng)作指針的偏移量,即array[i]會(huì)被當(dāng)作*(array+i)
b.編譯器將函數(shù)參數(shù)聲明中的數(shù)組名當(dāng)作指向該數(shù)組第0號(hào)元素的指針,即在函數(shù)內(nèi)部得到的是指針,而不是數(shù)組名
基于a情況,可知這條謠言是假的(至少在一維數(shù)組中一定不成立):用指針迭代數(shù)組比用下標(biāo)迭代數(shù)組更快
基于b情況,可解釋為什么在傳遞數(shù)組后,不能用以下方法計(jì)算數(shù)組長(zhǎng)度
intArrayLength(intarr[]){
returnsizeof(arr)/sizeof(arr[0]);//返回值必定是1,因?yàn)榇藭r(shí)的arr是一個(gè)指針,而不是數(shù)組名}
注意b情況的將數(shù)組改寫(xiě)為指針并不是遞歸定義的,e.g:
實(shí)參charzero[10][10]被改寫(xiě)為char(*zero)[10],這里將數(shù)組的數(shù)組改寫(xiě)為數(shù)組的指針
實(shí)參char*zero[10]被改寫(xiě)為char**zero,這里將指針數(shù)組改寫(xiě)為指針的指針
實(shí)參cahr(*zero)[10]不改變,因?yàn)榇藭r(shí)的zero是指針,而不是數(shù)組8.interposition
interposition指用戶(hù)定義的函數(shù)取代庫(kù)中聲明完全相同的函數(shù),注意這不是指重載,而是指下面這種:
voidzero();//userdefinedfunctionvoidzero();//libraryfunction
出現(xiàn)interposition時(shí),要特別注意以下情況:
voidzero();//userdefinedfunctionintmain(){
zero();//調(diào)用用戶(hù)定義的函數(shù)zero,而不是庫(kù)函數(shù)zero
FengYan();//假設(shè)這是另一個(gè)庫(kù)函數(shù),并且函數(shù)內(nèi)調(diào)用庫(kù)函數(shù)zero,此時(shí)由于interposition,變成調(diào)用用戶(hù)定義的zeroreturn0;}
備注:
出現(xiàn)interposition時(shí),在VS201*會(huì)出現(xiàn)warning:inconsistentdlllinkage9.堆棧段作用
a.存儲(chǔ)局部變量
b.函數(shù)調(diào)用時(shí),存儲(chǔ)有關(guān)的維護(hù)信息
c.用作暫時(shí)存儲(chǔ)區(qū)。e.g:計(jì)算一個(gè)很長(zhǎng)的表達(dá)式時(shí),會(huì)把部分結(jié)果先壓到堆棧中
擴(kuò)展閱讀:C語(yǔ)言要點(diǎn)總結(jié)
《C和指針》《C專(zhuān)家編程》《C陷阱與缺陷》《C語(yǔ)言編程要點(diǎn)》
《編程精粹--Microsoft編寫(xiě)優(yōu)質(zhì)無(wú)錯(cuò)C程序秘訣》
總結(jié)
說(shuō)明:總結(jié)的知識(shí)點(diǎn)主要源于上面的4本書(shū),《編程精粹--Microsoft編寫(xiě)優(yōu)質(zhì)無(wú)錯(cuò)C程序秘訣》這本書(shū)未做總結(jié),該書(shū)有清晰版的pdf格式的電子版。
--wuliming--201*-04-25
wuliming_sc@163.comwuliming_sc@qq.com
目錄
字符與字符串的區(qū)別(c缺陷與陷阱1.5節(jié))....................................................................................................................................4指針與數(shù)組1(c缺陷與陷阱3.1節(jié))................................................................................................................................................5指針與數(shù)組2(c和指針.P141.)......................................................................................................................................................6指針和數(shù)組的相同與不同(c專(zhuān)家編程.P199.).................................................................................................................................8用malloc為字符串分配存儲(chǔ)空間時(shí)的注意事項(xiàng)(c缺陷與陷阱3.2節(jié))........................................................................................10字符串常量(c和指針.P269.)........................................................................................................................................................12用字符串常量初始化指針和數(shù)組(c專(zhuān)家編程.P87.)....................................................................................................................13二維數(shù)組下標(biāo)操作的相關(guān)概念(c和指針.P156.)...........................................................................................................................14指向一維、二維數(shù)組的指針(c和指針.P158.)...............................................................................................................................17array_name和&array_name的異同........................................................................................................................................18數(shù)組作為函數(shù)的參數(shù)時(shí),不能通過(guò)sizeof運(yùn)算符得到該數(shù)組的大小............................................................................................18用strlen()求字符串的長(zhǎng)度(c和指針.P159.)...............................................................................................................................19‘char**’和‘constchar**’的兼容性問(wèn)題(c專(zhuān)家編程.P19.)..........................................................................................22空指針相關(guān)的問(wèn)題(c缺陷與陷阱3.5節(jié))......................................................................................................................................23NULL和NUL的區(qū)別..................................................................................................................................................................24未初始化的指針和NULL指針的區(qū)別(c和指針.P95.)..................................................................................................................25理解函數(shù)的聲明(c缺陷與陷阱2.1節(jié)).........................................................................................................................................27函數(shù)參數(shù)的傳值調(diào)用(c和指針.P122.).........................................................................................................................................29函數(shù)指針(c和指針.P260.)...........................................................................................................................................................31作為函數(shù)參數(shù)的多維數(shù)組(c和指針.P159.)..................................................................................................................................32強(qiáng)制類(lèi)型轉(zhuǎn)換相關(guān)概念(c專(zhuān)家編程.P187.)..................................................................................................................................34malloc()、calloc()、realloc()...................................................................................................................................................36常見(jiàn)的動(dòng)態(tài)內(nèi)存錯(cuò)誤(c和指針.P223.).........................................................................................................................................37在程序退出main()函數(shù)之后,還有可能執(zhí)行一部分代碼嗎?........................................................................................................38總線(xiàn)錯(cuò)誤和段錯(cuò)誤相關(guān)概念(c專(zhuān)家編程.P157.)...........................................................................................................................39怎樣判斷一個(gè)字符是數(shù)字、字母或其它類(lèi)別的符號(hào)?....................................................................................................................40怎樣將數(shù)字轉(zhuǎn)換為字符串?............................................................................................................................................................42怎樣將字符串轉(zhuǎn)換為數(shù)字?............................................................................................................................................................44字符串拷貝和內(nèi)存拷貝函數(shù)..........................................................................................................................................................47字符串和內(nèi)存數(shù)據(jù)比較函數(shù)..........................................................................................................................................................49連接字符串的函數(shù)........................................................................................................................................................................51查找字符/字符串的函數(shù)...............................................................................................................................................................52qsort()函數(shù).............................................................................................................................................................................58bsearch()函數(shù).............................................................................................................................................................................59lsearch(線(xiàn)性搜索)...................................................................................................................................................................60lfind(線(xiàn)性搜索).......................................................................................................................................................................60srand(設(shè)置隨機(jī)數(shù)種子)..........................................................................................................................................................61rand(產(chǎn)生隨機(jī)數(shù))...................................................................................................................................................................61什么是標(biāo)準(zhǔn)預(yù)定義宏?...................................................................................................................................................................62斷言assert(表達(dá)式)相關(guān)概念(c和指針.P342.)........................................................................................................................63連接運(yùn)算符“##”和字符串化運(yùn)算符"#"的作用..........................................................................................................................63注釋掉一段代碼的方法.................................................................................................................................................................66Typedef相關(guān)概念.......................................................................................................................................................................67=不同于==(c缺陷與陷阱1.1節(jié))........................................................................................................................................69詞法分析中的“貪心法”(c缺陷與陷阱1.3節(jié)).......................................................................................................................70運(yùn)算符的優(yōu)先級(jí)問(wèn)題(c缺陷與陷阱2.2節(jié))..............................................................................................................................72
變量的存儲(chǔ)類(lèi)型及初始化相關(guān)概念(c和指針.P43.)......................................................................................................................73左值和右值相關(guān)的概念(c和指針.P79.)........................................................................................................................................75變量的值和類(lèi)型相關(guān)的概念(c和指針.P92.).................................................................................................................................76怎樣刪去字符串尾部的空格..........................................................................................................................................................77怎樣刪去字符串頭部的空格..........................................................................................................................................................78怎樣打印字符串的一部分.............................................................................................................................................................79結(jié)構(gòu)的自引用(c和指針.P198.)....................................................................................................................................................80結(jié)構(gòu)的存儲(chǔ)分配(c和指針.P206.)................................................................................................................................................81邊界計(jì)算與不對(duì)稱(chēng)邊界(C缺陷與陷阱3.6節(jié))..............................................................................................................................83整數(shù)溢出(C缺陷與陷阱3.9節(jié))....................................................................................................................................................84返回整數(shù)的getchar函數(shù)(C缺陷與陷阱5.1節(jié)).........................................................................................................................85更新順序文件(C缺陷與陷阱5.2節(jié))............................................................................................................................................86隨機(jī)數(shù)的相關(guān)概念(c和指針.P328.).............................................................................................................................................87用遞歸和迭代兩種辦法解fibonacci............................................................................................................................................88
字符與字符串的區(qū)別(c缺陷與陷阱1.5節(jié))
#includeintmain(){
charch="abcdefghijklmnopqrstuvwxyz";charstr[]="abcdefghijklmnopqrstuvwxyz";printf("-----%c-----\\n%s\\n",ch,str);
return0;}
編譯該程序可以通過(guò),但是會(huì)產(chǎn)生警告;輸出結(jié)過(guò)為:-----z-----
Abcdefghijklmnopqrstuvwxyz//在Dev-C++4.9.9.2編譯環(huán)境中可以通過(guò),但是在VC.0中通不過(guò)
指針與數(shù)組1(c缺陷與陷阱3.1節(jié))
c語(yǔ)言中的數(shù)組值得注意的地方有以下兩點(diǎn):
1、c語(yǔ)言中只有一維數(shù)組,而且數(shù)組的大小必須在編譯期間就作為一個(gè)常數(shù)確定下來(lái)(C99標(biāo)準(zhǔn)允許變長(zhǎng)數(shù)組,GCC編譯器中實(shí)現(xiàn)了變長(zhǎng)數(shù)組)。然而,c語(yǔ)言中數(shù)組的元素可以是任何類(lèi)型的對(duì)象,當(dāng)然也可以是另外一個(gè)數(shù)組。這樣,要仿真出一個(gè)多維數(shù)組就不是一件難事。
2、對(duì)于一個(gè)數(shù)組,我們只能夠做兩件事:確定該數(shù)組的大小,以及獲得指向該數(shù)組下標(biāo)為0的元素的指針。其他有關(guān)數(shù)組的操作,哪怕它們乍看上去是以數(shù)組下標(biāo)進(jìn)行運(yùn)算的,實(shí)際上都是通過(guò)指針進(jìn)行的。換句話(huà)說(shuō),任何一個(gè)數(shù)組下標(biāo)運(yùn)算都等同于一個(gè)對(duì)應(yīng)的指針運(yùn)算,因此我們完全可以依據(jù)指針行為定義數(shù)組下標(biāo)的行為。
現(xiàn)在考慮下面的例子:inti;int*p;
intcalendar[12][31];
上面聲明的calendar是一個(gè)數(shù)組,該數(shù)組擁有12個(gè)數(shù)組類(lèi)型的元素,其中的每個(gè)元素都是一個(gè)擁有31個(gè)整型元素的數(shù)組。因此,sizeof(calendar)的值是:31×12×sizeof(int)。
考慮一下,calendar[4]的含義是什么?因?yàn)閏alender是一個(gè)有著12個(gè)數(shù)組類(lèi)型元素的數(shù)組,它的每個(gè)數(shù)組類(lèi)型元素又是一個(gè)有著31個(gè)整型元素的數(shù)組,所以calendar[4]是calendar數(shù)組的第5個(gè)元素,是calendar數(shù)組中12個(gè)有著31個(gè)整型元素的數(shù)組之一。因此,calendar[4]的行為也表現(xiàn)為一個(gè)有著31個(gè)整型元素的數(shù)組的行為。例如,sizeof(calendar[4])的結(jié)果是:31×sizeof(int)。
又如,p=calendar[4];這個(gè)語(yǔ)句使指針p指向了數(shù)組calendar[4]中下標(biāo)為0的元素。因?yàn)閏alendar[4]是一個(gè)數(shù)組,我們可以通過(guò)下標(biāo)的形式來(lái)指定這個(gè)數(shù)組中的元素:i=calendar[4][7],這個(gè)語(yǔ)句也可以寫(xiě)成下面這樣而表達(dá)的意思保持不變:i=*(calendar[4]+7),還可以進(jìn)一步寫(xiě)成:i=*(*(calendar+4)+7)。
下面我們?cè)倏矗簆=calendar;這個(gè)語(yǔ)句是非法的,因?yàn)閏alendar是一個(gè)二維數(shù)組,即“數(shù)組的數(shù)組”,在此處的上下文中使用calendar名稱(chēng)會(huì)將其轉(zhuǎn)換為一個(gè)指向數(shù)組的指針。而p是一個(gè)指向整型變量的指針,兩個(gè)指針的類(lèi)型不一樣,所以是非法的。顯然,我們需要一種聲明指向數(shù)組的指針的方法:intcalendar[12][31];int(*monthp)[31];monthp=calendar;
int(*monthp)[31]語(yǔ)句聲明的*monthp是一個(gè)擁有31個(gè)整型元素的數(shù)組,因此,monthp就是一個(gè)指向這樣的數(shù)組的指針。monthp指向數(shù)組calendar的第一個(gè)元素。
指針與數(shù)組2(c和指針.P141.)
1、數(shù)組的名的值是一個(gè)指針常量,不能試圖將一個(gè)地址賦值給數(shù)組名;
2、當(dāng)數(shù)組名作為sizeof操作符的操作數(shù)時(shí),sizeof(arrayname)返回的是整個(gè)數(shù)組的長(zhǎng)度,而不是指向
數(shù)組的指針的長(zhǎng)度;
3、當(dāng)數(shù)組名作為單目操作符&的操作數(shù),取一個(gè)數(shù)組名的地址所產(chǎn)生的是一個(gè)指向數(shù)組的指針,而不是一個(gè)指
向某個(gè)指針常量值的指針。
4、指針和數(shù)組并不總是相等的。為了說(shuō)明這個(gè)概念,請(qǐng)考慮下面這兩個(gè)聲明:
inta[5];int*b;
a和b能夠互換嗎?它們都具有指針值,它們都可以進(jìn)行間接訪(fǎng)問(wèn)和下標(biāo)操作。但是,它們還是有很大的區(qū)別的:聲明一個(gè)數(shù)組時(shí),編譯器將根據(jù)聲明所指定的元素?cái)?shù)量為數(shù)組保留內(nèi)存空間,然后再創(chuàng)建數(shù)組名,它的值是一個(gè)常量,指向這段空間的起始位置。聲明一個(gè)指針變量時(shí),編譯器只為指針本身保留內(nèi)存空間,它并不為任何整型值分配內(nèi)存空間。而且,指針變量并未被初始化為指向任何現(xiàn)有的內(nèi)存空間,如果它是一個(gè)自動(dòng)變量,它甚至根本不會(huì)被初始化。把這兩個(gè)聲明用圖的方法表示,可以發(fā)現(xiàn)它們之間存在顯著的不同:ab?因此,上述聲明后,表達(dá)式*a是完全合法的,但表達(dá)式*b卻是非法的。*b將訪(fǎng)問(wèn)內(nèi)存中某個(gè)不確定的位
置,或者導(dǎo)致程序終止。另一方面,表達(dá)式b++可以通過(guò)編譯,但是a++卻不能,因?yàn)閍的值是一個(gè)常量。
#include
intmain(){
//注意sizeof(num)的長(zhǎng)度應(yīng)該為10*4=40intnum[]={0,1,2,3,4,5,6,7,8,9};printf("sizeof(num)=%d\\n",sizeof(num));//注意sizeof(str)的長(zhǎng)度應(yīng)該為11,包括字符串后面的"\\0"charstr[]="0123456789";printf("sizeof(str)=%d\\n",sizeof(str));//注意sizeof(str1)的長(zhǎng)度應(yīng)該為10,不包括字符串后面的"\\0",但是,最好將字符串的最后一個(gè)字符設(shè)定為空charstr1[]={"0","1","2","3","4","5","6","7","8","9"};printf("sizeof(str1)=%d\\n",sizeof(str1));//&num的類(lèi)型為"int(*)[10]",表示的是一個(gè)指向長(zhǎng)度為10的整形數(shù)組的指針int(*ptoint)[10]=#printf("sizeof(ptoint)=%d,(*ptoint)[9]=%d\\n",sizeof(ptoint),(*ptoint)[9]);//&str的類(lèi)型為"char(*)[11]",表示的是一個(gè)指向長(zhǎng)度為11的字符數(shù)組的指針,注意str數(shù)組的長(zhǎng)度是11,而不是10char(*ptostr)[11]=&str;printf("sizeof(ptostr)=%d,(*ptostr)[9]=%c\\n",sizeof(ptostr),(*ptostr)[9]);//由于p指向的是數(shù)組num[5],所以對(duì)下標(biāo)取負(fù)值后,不會(huì)超出數(shù)組的正常取值范圍//該例子也說(shuō)明了為什么下標(biāo)檢查在c語(yǔ)言中是一項(xiàng)困難的任務(wù):下標(biāo)引用可以作用于任意的指針,而不僅僅是數(shù)組名//作用于指針的下標(biāo)引用的有效性即依賴(lài)于該指針當(dāng)時(shí)恰好指向什么內(nèi)容,也依賴(lài)于下標(biāo)的值int*p=num+5;printf("p[-1]=%d,p[0]=%d,p[1]=%d\\n",p[-1],p[0],p[1]);//下面的表達(dá)式中,"num[5]和5[num]"的值是一樣的,把它們轉(zhuǎn)換成對(duì)等的間接訪(fǎng)問(wèn)表達(dá)式,它們都等同于*(num+2)//"5[num]"這個(gè)古怪的表達(dá)式之所以可行,緣于C實(shí)現(xiàn)下標(biāo)的方法。對(duì)編譯器來(lái)說(shuō),這兩種形式并無(wú)差別//但是,決不應(yīng)該編寫(xiě)形如"5[num]"的表達(dá)式,因?yàn)樗鼤?huì)大大的影響程序的可讀性printf("num[5]=%d,5[num]=%d\\n",num[5],5[num]);getchar();
}
return0;
輸出結(jié)果為:
指針和數(shù)組的相同與不同(c專(zhuān)家編程.P199.)
在實(shí)際應(yīng)用中,數(shù)組和指針可以互換的情形要比兩者不可互換的情形更為常見(jiàn)。讓我們分別考慮“聲明”和“使用”這兩種情況。聲明本身還可以進(jìn)一步分為3種情況:外部數(shù)組的聲明;
數(shù)組的定義(定義是聲明的一種特殊情況,它分配內(nèi)存空間,并可能提供一個(gè)初始值);函數(shù)參數(shù)的聲明;
extern,如externchara[];不能改寫(xiě)為指針的形式
聲明定義,如chara[10];不能改寫(xiě)為指針的形式數(shù)組
函數(shù)的參數(shù),可以隨意選擇數(shù)組在表達(dá)式中使用的形式或者指針的形式
如c=a[i],可以隨意選擇數(shù)組形式或者是指針形式
也既是:作為函數(shù)參數(shù)時(shí)、在語(yǔ)句或表達(dá)式中使用數(shù)組時(shí),我們可以采用數(shù)組或者指針的任何一種形式,除此之外的其他情況下,指針和數(shù)組不要互換。下面就數(shù)組和指針相同的情況做詳細(xì)的說(shuō)明:規(guī)則1、表達(dá)式中的數(shù)組名被編譯器當(dāng)作一個(gè)指向該數(shù)組第一個(gè)元素的指針。
假如我們聲明:inta[10];int*p=a;
就可以通過(guò)一下任何一種方式來(lái)訪(fǎng)問(wèn)a[i]:
p[i]*(p+i)*(a+i)
事實(shí)上,可以采用的方法很多。對(duì)數(shù)組的引用如a[i]在編譯時(shí)總是被編譯器改寫(xiě)成*(a+i)的形式,C語(yǔ)言標(biāo)準(zhǔn)要求編譯器必須具備這個(gè)概念性的行為。
編譯器自動(dòng)把下標(biāo)值的步長(zhǎng)調(diào)整到數(shù)組元素的大小。如果整型數(shù)的長(zhǎng)度是4個(gè)字節(jié),那么a[i+1]和a[i]在內(nèi)存中的距離就是4。對(duì)起始地址執(zhí)行加法操作之前,編譯器會(huì)負(fù)責(zé)計(jì)算每次增加的步長(zhǎng)。這就是為什么指針總是有類(lèi)型限制,每個(gè)指針只能指向一種類(lèi)型的原因所在,因?yàn)榫幾g器需要知道對(duì)指針進(jìn)行解除引用操作時(shí)應(yīng)該取幾個(gè)字節(jié),以及每個(gè)下標(biāo)的步長(zhǎng)應(yīng)取幾個(gè)字節(jié)。規(guī)則2、下標(biāo)總是和指針的偏移量相同。
把數(shù)組下標(biāo)作為指針加偏移量是c語(yǔ)言從BCPL(C語(yǔ)言的祖先)繼承過(guò)來(lái)的技巧。在人們的常規(guī)思維中,在運(yùn)行時(shí)增加對(duì)c語(yǔ)言下標(biāo)的范圍檢查是不切實(shí)際的。因?yàn)槿∠聵?biāo)操作只是表示將要訪(fǎng)問(wèn)該數(shù)組,但并不保證一定要訪(fǎng)問(wèn)。而且程序員完全可以使用指針來(lái)訪(fǎng)問(wèn)數(shù)組,從而繞過(guò)下標(biāo)操作符。在這種情況下,數(shù)組下標(biāo)范圍檢測(cè)并不能檢測(cè)所有對(duì)數(shù)組的訪(fǎng)問(wèn)的情況。事實(shí)上,下標(biāo)范圍檢測(cè)被認(rèn)為不值得加入到c語(yǔ)言當(dāng)中。
還有一個(gè)說(shuō)法是,在編寫(xiě)數(shù)組算法時(shí),使用指針比使用數(shù)組更有效率。這個(gè)頗為人們所接收的說(shuō)法在通常情況下是錯(cuò)誤的。使用現(xiàn)代的產(chǎn)品質(zhì)量?jī)?yōu)化的編譯器,一維數(shù)組和指針引用所產(chǎn)生的代碼并不具有顯著的差別。不管怎樣,數(shù)組下標(biāo)是定義在指針的基礎(chǔ)上,所以?xún)?yōu)化器常?梢园阉D(zhuǎn)化為更有效率的指針表達(dá)式,并生成相同的機(jī)器指令。
規(guī)則3、在函數(shù)參數(shù)的聲明中,數(shù)組名被編譯器當(dāng)作指向該數(shù)組第一個(gè)元素的指針。
在函數(shù)形參定義這個(gè)特殊情況下,編譯器必須把數(shù)組形式改寫(xiě)成指向數(shù)組第一個(gè)元素的指針形式。編譯器只
向函數(shù)傳遞數(shù)組的地址,而不是整個(gè)數(shù)組的拷貝。這種轉(zhuǎn)換意味著在聲明函數(shù)的時(shí)候,以下三種形式都是合法的(同時(shí)無(wú)論實(shí)參是數(shù)組還是真的指針也都是合法的):
my_function(int*turnip){}my_function(intturnip[]){}my_function(intturnip[200]){}
用malloc為字符串分配存儲(chǔ)空間時(shí)的注意事項(xiàng)(c缺陷與陷阱3.2節(jié))
11
字符串常量(c和指針.P269.)
當(dāng)一個(gè)字符串常量出現(xiàn)在表達(dá)式中時(shí),它的值是指針常量。編譯器把該字符串的一份拷貝存儲(chǔ)在內(nèi)存的某個(gè)位置,并存儲(chǔ)一個(gè)指向第一個(gè)字符的指針。我們可以對(duì)字符串常量進(jìn)行下標(biāo)引用、間接訪(fǎng)問(wèn)以及指針運(yùn)算!皒yz”+1
字符串常量實(shí)際上是個(gè)指針,這個(gè)表達(dá)式計(jì)算“指針值加上1”的值。它的結(jié)果也是個(gè)指針,指向字符串中的第二個(gè)字符y*“xyz”
對(duì)一個(gè)指針執(zhí)行間接訪(fǎng)問(wèn)操作時(shí),其結(jié)果就是指針?biāo)赶虻膬?nèi)容。字符串常量的類(lèi)型是“指向字符的指針”,所以這個(gè)間接訪(fǎng)問(wèn)的結(jié)果就是它所指向的字符:x。注意表達(dá)式的結(jié)果并不是整個(gè)字符串,而只是它的第一個(gè)字符。“xyz”[2]
同樣可以推斷出上面這個(gè)表達(dá)式的值就是字符z。
#include
//接受一個(gè)無(wú)符號(hào)整型值,把它轉(zhuǎn)換成字符,并打印出來(lái)
//如果是打印16進(jìn)值的數(shù),可以用這種方法:putchar("0123456789ABCDEF"[value%16])voidbinary_to_ascii(unsignedlongvalue){
unsignedlongquotient;quotient=value/10;if(quotient!=0)binary_to_ascii(quotient);putchar("0123456789"[value%10]);}
intmain(){
//字符串常量實(shí)際上是個(gè)指針,這個(gè)表達(dá)式計(jì)算"指針值加上1"的值。它的結(jié)果也是個(gè)指針,
//指向字符串中的第二個(gè)字符:yprintf("%s\\n","xyz"+1);
//對(duì)一個(gè)指針執(zhí)行間接訪(fǎng)問(wèn)操作時(shí),其結(jié)果就是指針?biāo)赶虻膬?nèi)容。
//字符串常量的類(lèi)型是"指向字符的指針",所以這個(gè)間接訪(fǎng)問(wèn)的結(jié)果就是它所指向的字符:xprintf("%c\\n",*"abcdefg");
//同樣可以推斷出上面這個(gè)表達(dá)式的值就是字符zprintf("%c\\n","abcdefg"[3]);
binary_to_ascii(1234567);
getchar();
return0;}
用字符串常量初始化指針和數(shù)組(c專(zhuān)家編程.P87.)
定義指針時(shí),編譯器并不為指針?biāo)傅膶?duì)象分配空間,它只是分配指針本身的空間,除非在定義時(shí)同時(shí)賦給指針一個(gè)字符串常量進(jìn)行初始化。例如,下面的定義創(chuàng)建一個(gè)字符串常量(為其分配內(nèi)存):char*p=“breadfruit”;
注意只有對(duì)字符串常量才是如此。不能指望為浮點(diǎn)數(shù)之類(lèi)的變量分配空間,如:float*pip=3.14;/*錯(cuò)誤,無(wú)法通過(guò)編譯*/
在ANSIC中,初始化指針時(shí)所創(chuàng)建的字符串常量被定義為只讀。如果試圖通過(guò)指針修改這個(gè)字符串值,程序會(huì)出現(xiàn)未定義的行為。在有些編譯器中,字符串常量被存放在只允許讀取的文本段中,以防止它被修改。
數(shù)組也可以用字符串常量進(jìn)行初始化:chara[]=“gooseberry”;
與指針相反,由字符串常量初始化的數(shù)組是可以修改的。比如下面的語(yǔ)句:strncpy(a,“black”,5);將數(shù)組的值修改為“blackberry”。
#include#include
intmain(void){
char*p="thisisaexample";
//char*pi=3.14;//這樣定義是錯(cuò)誤的,無(wú)法通過(guò)編譯
//p[0]="T";//修改該字符串常量時(shí),編譯是沒(méi)問(wèn)題,但是運(yùn)行時(shí)會(huì)出現(xiàn)異常
chara[]="gooseberry";strncpy(a,"black",5);
printf("%s\\n",p);printf("%s\\n",a);
return0;}
二維數(shù)組下標(biāo)操作的相關(guān)概念(c和指針.P156.)
141516
指向一維、二維數(shù)組的指針(c和指針.P158.)
17array_name和&array_name的異同
前者是指向數(shù)組中第一個(gè)元素的指針,后者是指向整個(gè)數(shù)組的指針。chara[MAX];/*arrayofMAXcharacters*/char*p=a;/*p為指向數(shù)組的指針*/
char*pa=&a;/*該語(yǔ)句是不正確的,pa的類(lèi)型為"char*",而&a的類(lèi)型為"char(*)[MAX]’*/char(*pb)[MAX]=&a;/*該語(yǔ)句是正確的,pb的類(lèi)型為"char(*)[MAX]"*/
#include
voidmain(){
chara[5]={"a","b","c","d","\\0"};char*p=a;
//運(yùn)行下面這句后,vc6.0提示的錯(cuò)誤為:cannotconvertfrom"char(*)[5]"to"char*",&a的類(lèi)型應(yīng)該是指向一個(gè)數(shù)組的指針//char*pa=&a;
//所以,應(yīng)該定義一個(gè)指向相同類(lèi)型和大小的數(shù)組的指針來(lái)獲得“&a”的值char(*point_to_str)[5];point_to_str=&a;
printf("%d\\n%d\\n",&p,&point_to_str);printf("%s\\n%s\\n",p,point_to_str);}
運(yùn)行結(jié)果為:12450441245040abcdabcd
數(shù)組作為函數(shù)的參數(shù)時(shí),不能通過(guò)sizeof運(yùn)算符得到該數(shù)組的大小
當(dāng)把數(shù)組作為函數(shù)的參數(shù)時(shí),你無(wú)法在程序運(yùn)行時(shí)通過(guò)數(shù)組參數(shù)本身告訴函數(shù)該數(shù)組的大小,因?yàn)楹瘮?shù)的數(shù)組參數(shù)相當(dāng)于指向該數(shù)組第一個(gè)元素的指針。這意味著把數(shù)組傳遞給函數(shù)的效率非常高,也意味著程序員必須通過(guò)某種機(jī)制告訴函數(shù)數(shù)組參數(shù)的大小。為了告訴函數(shù)數(shù)組參數(shù)的大小,人們通常采用以下兩種方法:第一種方法是將數(shù)組和表示數(shù)組大小的值一起傳遞給函數(shù),例如memcpy()函數(shù)就是這樣做的:memcpy(dest,source,length);
第二種方法是引入某種規(guī)則來(lái)結(jié)束一個(gè)數(shù)組,例如在C語(yǔ)言中字符串總是以ASCII字符NUL("\\0")結(jié)束,而一個(gè)指針數(shù)組總是以空指針結(jié)束。請(qǐng)看下述函數(shù),它的參數(shù)是一個(gè)以空指針結(jié)束的字符指針數(shù)組,這個(gè)空指針告訴該函數(shù)什么時(shí)候停止工作:
voidprintMany(char*strings[]){
inti=0;
while(strings[i]!=NULL){
puts(strings[i++]);}}
C程序員經(jīng)常用指針來(lái)代替數(shù)組下標(biāo),因此大多數(shù)C程序員通常會(huì)將上述函數(shù)編寫(xiě)得更隱蔽一些:voidprintMany(char*strings[]){
while(*strings){
puts(*strings++);}}
盡管你不能改變一個(gè)數(shù)組名的值,但是strings是一個(gè)數(shù)組參數(shù),相當(dāng)于一個(gè)指針,因此可以對(duì)它進(jìn)行自增運(yùn)算,并且可以在調(diào)用puts()函數(shù)時(shí)對(duì)strings進(jìn)行自增運(yùn)算。
用strlen()求字符串的長(zhǎng)度(c和指針.P159.)
庫(kù)函數(shù)strlen的原型為:size_tstrlen(charconst*string);
strlen返回一個(gè)類(lèi)型為size_t的值。這個(gè)類(lèi)型是在頭文件stddef.h中定義的,它是一個(gè)無(wú)符號(hào)整型類(lèi)型。在表達(dá)式中使用無(wú)符號(hào)數(shù)可能導(dǎo)致不可預(yù)期的結(jié)果。例如,下面兩個(gè)表達(dá)式看起來(lái)是相等的:
if(strlen(str1)>=strlen(str2))…if(strlen(str1)-strlen(str2)>=0)…
但事實(shí)上它們是不相等的,第1條語(yǔ)句會(huì)按照預(yù)想的那樣工作,但第2條語(yǔ)句的結(jié)果將永遠(yuǎn)是真的。strlen的結(jié)果是無(wú)符號(hào)數(shù),所以操作符>=左邊的表達(dá)式也將是無(wú)符號(hào)數(shù),而無(wú)符號(hào)數(shù)決不可能是負(fù)的。表達(dá)式中如果同時(shí)包含了無(wú)符號(hào)數(shù)和有符號(hào)數(shù),可能會(huì)產(chǎn)生奇怪的結(jié)果。和上面的一對(duì)語(yǔ)句一樣,下面兩條語(yǔ)句并不相等,原因相同。
if(strlen(str1)>=10)…if(strlen(str1)-10>=0)…
如果將strlen的結(jié)果值強(qiáng)制轉(zhuǎn)換成int,就可以消除這個(gè)問(wèn)題。類(lèi)似的,sizeof()的返回類(lèi)型也是size_t,和strlen()一樣,也存在類(lèi)似的現(xiàn)象(sizeof()是一個(gè)運(yùn)算符,不是函數(shù))。
對(duì)無(wú)符號(hào)類(lèi)型的建議:
盡量不要在代碼里面使用無(wú)符號(hào)類(lèi)型,以免增加不必要的復(fù)雜性。尤其是,不要僅僅因?yàn)闊o(wú)符號(hào)數(shù)不存在負(fù)值而用它來(lái)表示數(shù)量。
盡量使用像int這樣的有符號(hào)類(lèi)型,這樣在涉及升級(jí)混合類(lèi)型的復(fù)雜細(xì)節(jié)時(shí),不必?fù)?dān)心邊界情況。
對(duì)于返回類(lèi)型為無(wú)符號(hào)的函數(shù)(strlen()、sizeof()),最好將結(jié)果轉(zhuǎn)換成整型((int)strlen(…)、(int)sizeof(…)),這樣可以避免出現(xiàn)比較微妙的bug(在java里面,就沒(méi)有無(wú)符號(hào)數(shù))。
#include#include#include
intmain(){
charstr1[]="0123456789";charstr2[]="abcdefghijk";
//sizeof()的返回類(lèi)型也是無(wú)符號(hào)類(lèi)型,無(wú)符號(hào)類(lèi)型的運(yùn)算結(jié)果也被轉(zhuǎn)換成無(wú)符號(hào)類(lèi)型,不可能為負(fù)//(int)sizeof(str1)(int)sizeof(str2)>0這個(gè)表達(dá)式將得到預(yù)期結(jié)果if(sizeof(str1)-sizeof(str2)>0){printf(""sizeof(str1)-sizeof(str2)"的計(jì)算結(jié)果是無(wú)符號(hào)型的,不可能為負(fù)\\n");}if(strlen(str1)>=strlen(str2)){printf("strlen的返回值為無(wú)符號(hào)整型類(lèi)型,把兩個(gè)無(wú)符號(hào)整型類(lèi)型做比較,會(huì)得到預(yù)期的結(jié)果\\n");}if(strlen(str1)-strlen(str2)>=0){printf("strlen(str1)=%d\\n",strlen(str1));printf("strlen(str2)=%d\\n",strlen(str2));
printf("(strlen(str1)-strlen(str2)>=0)表達(dá)式的值為:%d\\n",strlen(str1)-strlen(str2)>=0);printf(""strlen(str1)-strlen(str2)"的結(jié)果是無(wú)符號(hào)類(lèi)型,無(wú)符號(hào)數(shù)不可能是負(fù)值,所以該條件永遠(yuǎn)成立\\n");}//注意:sizeof()和strlen()取得的值是不相等的//sizeof()求得的長(zhǎng)度包括字符串末尾的那個(gè)空字符"\\0"http://strlen()求得的長(zhǎng)度不包括字符串末尾的空字符printf("sizeof(str1)=%d\\nstrlen(str1)=%d\\n",sizeof(str1),strlen(str1));
getchar();return0;}
‘char**’和‘constchar**’的兼容性問(wèn)題(c專(zhuān)家編程.P19.)
有時(shí)候必須非常專(zhuān)注的閱讀ANSIC標(biāo)準(zhǔn)才能找到某個(gè)問(wèn)題的答案。一位銷(xiāo)售工程師把下面的代碼作為測(cè)試?yán)影l(fā)給SUN的編譯器小組。
#include
voidfoo(constchar**P){}
intmain(intargc,char**argv){
foo(argv);
return0;}
在VC6.0下編譯這段代碼,編譯器會(huì)發(fā)出警告:
cannotconvertparameter1from"char**"to"constchar**"
提交代碼的工程師想知道為什么會(huì)產(chǎn)生類(lèi)似的警告,他認(rèn)為,實(shí)參char*s與形參constchar*p應(yīng)該是相容的,標(biāo)準(zhǔn)庫(kù)中所有的字符串處理函數(shù)都是這樣的。那么,為什么實(shí)參char**argv與形參constchar**P實(shí)際上不能相容呢?答案是肯定的,它們并不相容。現(xiàn)在我們回顧一下標(biāo)準(zhǔn)中有關(guān)簡(jiǎn)單賦值的部分,它位于ANSIC第6.3.16.1節(jié),描述了下列約束條件:要使上述賦值形式合法,必須滿(mǎn)足下列條件之一:
兩個(gè)操作數(shù)都是指向有限定符或無(wú)限定符的相容類(lèi)型的指針,左邊指針?biāo)赶虻念?lèi)型必須具有右邊指針?biāo)赶蝾?lèi)型的全部限定符。
正是這個(gè)條件,使得函數(shù)調(diào)用中實(shí)參char*能夠與形參constchar*匹配。它之所以合法,是因?yàn)樵谙旅娴拇a中:char*cp;
constchar*cpp;cpp=cp;
左操作數(shù)是一個(gè)指向有const限定符的char的指針;右操作數(shù)是一個(gè)指向沒(méi)有限定符的char的指針;
char類(lèi)型和char類(lèi)型是相容的,左操作數(shù)所指向的類(lèi)型具有右操作數(shù)所指向類(lèi)型的限定符(無(wú)),再加上自身的限定符const(注意反過(guò)來(lái)不能賦值)。
標(biāo)準(zhǔn)第6.3.16.1節(jié)沒(méi)有說(shuō)明char**實(shí)參與constchar**形參是否相容。標(biāo)準(zhǔn)6.1.2.5節(jié)中講述實(shí)例的部分聲稱(chēng):constfloat*類(lèi)型并不是一個(gè)有限定符的類(lèi)型,它的類(lèi)型是“指向一個(gè)具有const限定符的float類(lèi)型的指針”,也就是說(shuō)const限定符是修飾指針?biāo)赶虻念?lèi)型,而不是指針。類(lèi)似地,constchar**也是一個(gè)沒(méi)有限定符的指針類(lèi)型,它的類(lèi)型是“指向有const限定符的char類(lèi)型的指針的指針”。由于char**和constchar**都是沒(méi)有限定符的指針類(lèi)型,但它們所指向的類(lèi)型不一樣(前者指向char*,后者指向constchar*),因此它們是不相容的。因此類(lèi)型為char**的實(shí)參和類(lèi)型為constchar**的形參是不相容的,編譯器會(huì)產(chǎn)生一條診斷信息。
空指針相關(guān)的問(wèn)題(c缺陷與陷阱3.5節(jié))
#include#includeintmain(){char*p=NULL;if(p==(char*)0){printf("pisanullpoint\\n");}else{printf("pisnotanullpoint\\n");}//該語(yǔ)句不會(huì)引起編譯錯(cuò)誤,但是運(yùn)行時(shí)會(huì)出現(xiàn)異常if(strcmp(p,(char*)0)==0){printf("can"tdereferencep\\n");}
//該語(yǔ)句不會(huì)引起編譯錯(cuò)誤,但是運(yùn)行時(shí)會(huì)出現(xiàn)異常printf("%d",*p);
getchar();return0;}
NULL和NUL的區(qū)別
NULL是在頭文件中專(zhuān)門(mén)為空指針定義的一個(gè)宏。NUL是ASCII字符集中第一個(gè)字符的名稱(chēng),它對(duì)應(yīng)于一個(gè)零值。C語(yǔ)言中沒(méi)有NUL這樣的預(yù)定義宏。注意:在ASCII字符集中,數(shù)字0對(duì)應(yīng)于十進(jìn)制值48,不要把數(shù)字0和"\\0"(NUL)的值混同起來(lái)。
NULL可以被定義為(void*)0,而NUL可以被定義為"\\0"。NULL和NUL都可以被簡(jiǎn)單地定義為0,這時(shí)它們是等價(jià)的,可以互換使用,但這是一種不可取的方式。為了使程序讀起來(lái)更清晰,維護(hù)起來(lái)更容易,你在程序中應(yīng)該明確地將NULL定義為指針類(lèi)型,而將NUL定義為字符類(lèi)型。
對(duì)指針進(jìn)行解引用操作可以獲得它的值。從定義來(lái)看,NULL指針并未指向任何東西。因此,對(duì)一個(gè)NULL指針進(jìn)行解引用操作是非法的。在對(duì)指針進(jìn)行解引用操作之前,必須確保它并非NULL指針。
未初始化的指針和NULL指針的區(qū)別(c和指針.P95.)
未初始化的指針
NULL指針
26理解函數(shù)的聲明(c缺陷與陷阱2.1節(jié))
2728
函數(shù)參數(shù)的傳值調(diào)用(c和指針.P122.)
#include
charga[]="abcdefghijklm";
voidmy_array_func(charca[10]){
//&ca相當(dāng)于一個(gè)指向字符數(shù)組的指針的地址
char**pp=&ca;
printf("&ca=%#x\\n",&ca);printf("ca=%#x\\n",ca);
printf("&(ca[0])=%#x\\n",&(ca[0]));printf("&(ca[1])=%#x\\n",&(ca[1]));
printf("sizeof(ca)=%d\\n\\n",sizeof(ca));}
voidmy_pointer_func(char*pa){
//&pa相當(dāng)于一個(gè)指向字符數(shù)組的指針的地址
char**pp=&pa;
printf("&pa=%#x\\n",&pa);printf("pa=%#x\\n",pa);
printf("&(pa[0])=%#x\\n",&(pa[0]));printf("&(pa[1])=%#x\\n",&(pa[1]));
printf("sizeof(pa)=%d\\n\\n",sizeof(pa));}
intmain(){
//&ga相當(dāng)于一個(gè)指向字符數(shù)組的指針的地址char(*pp)[14]=&ga;
printf("&ga=%#x\\n",&ga);printf("ga=%#x\\n",ga);
printf("&(ga[0])=%#x\\n",&(ga[0]));printf("&(ga[1])=%#x\\n",&(ga[1]));
printf("sizeof(ga)=%d\\n\\n",sizeof(ga));my_array_func(ga);my_pointer_func(ga);getchar();
return0;}
//摘自《c專(zhuān)家編程》p216頁(yè),做了部分修改
運(yùn)行結(jié)果為:
從結(jié)果可以看出,數(shù)組參數(shù)的地址和數(shù)組參數(shù)的第一個(gè)元素的地址是不一樣的,比如&ca和ca的值。
函數(shù)指針(c和指針.P260.)
31作為函數(shù)參數(shù)的多維數(shù)組(c和指針.P159.)
#include
intfunc1(int*vec){printf("一維數(shù)組做為參數(shù):通過(guò)"intfunc1(int*vec)"的形式調(diào)用函數(shù)\\n");return0;}
intfunc2(intvec[]){printf("一維數(shù)組做為參數(shù):通過(guò)"intfunc2(intvec[])"的形式調(diào)用函數(shù)\\n");return0;}
voidfunc3(intmat[3][10]){
printf("二維數(shù)組做為參數(shù):通過(guò)"voidfunc3(intmat[3][10])"的形式調(diào)用函數(shù)\\n");
return;}
voidfunc4(intmat[][10]){
printf("二維數(shù)組做為參數(shù):通過(guò)"voidfunc4(intmat[][10])"的形式調(diào)用函數(shù)\\n");return;}
voidfunc5(int(*mat)[10]){
printf("二維數(shù)組做為參數(shù):通過(guò)"voidfunc5(int(*mat)[10])"的形式調(diào)用函數(shù)\\n");return;}
voidfunc6(int**p){
printf("二維數(shù)組做為參數(shù):通過(guò)"voidfunc6(int**p)"的形式調(diào)用函數(shù)\\n");return;}
intmain(){
intvector[10]={1,2,3,4,5,6,7,8,9,10};func1(vector);func2(vector);intmatrix[3][10]={{1,2,3,4,5,6,7,8,9,10},{1,2,3,4,5,6,7,8,9,10},{1,2,3,4,5,6,7,8,9,10}};func3(matrix);func4(matrix);func5(matrix);//將matrix參數(shù)傳遞給函數(shù)"voidfunc6(int**p)"會(huì)出現(xiàn)異常//func6(matrix);
getchar();return0;}
只有當(dāng)二維數(shù)組是一個(gè)指向字符串的指針數(shù)組時(shí),函數(shù)的聲明才可以采取char**my_array的形式。這是因?yàn)樽址椭羔樁加幸粋(gè)顯示的越界值(分別是NUL和NULL),可以作為結(jié)束標(biāo)記。至于其它的類(lèi)型,并沒(méi)有一種類(lèi)似的通用且可靠的值,所以并沒(méi)有一種內(nèi)置的方法知道何時(shí)到達(dá)數(shù)組某一維的結(jié)束位置。即使是指向字符串的指針數(shù)組,通常也需要一個(gè)計(jì)數(shù)參數(shù)argc,以紀(jì)錄字符串的數(shù)量。
#include#include
voidmy_func(char**p){
while(*p!=NULL)printf("%s\\n",*p++);}
intmain(){
char*p[]={"one","two","three","four","five",NULL};
my_func(p);
return0;}
強(qiáng)制類(lèi)型轉(zhuǎn)換相關(guān)概念(c專(zhuān)家編程.P187.)
強(qiáng)制類(lèi)型轉(zhuǎn)換(cast)這個(gè)術(shù)語(yǔ)從C語(yǔ)言一誕生就開(kāi)始使用,即用于類(lèi)型轉(zhuǎn)換,也用于消除類(lèi)型歧義。可以很容易地把某種類(lèi)型的數(shù)據(jù)強(qiáng)制轉(zhuǎn)換為基本類(lèi)型的數(shù)據(jù):在括號(hào)里寫(xiě)上新類(lèi)型的名稱(chēng),然后把它們放在需要轉(zhuǎn)化類(lèi)型的表達(dá)式的前面。在強(qiáng)制轉(zhuǎn)換一個(gè)更為復(fù)雜的類(lèi)型時(shí),可以采取如下的方法:1、2、3、
作為一個(gè)例子,程序員經(jīng)常發(fā)現(xiàn)他們需要強(qiáng)制類(lèi)型轉(zhuǎn)換以便使用qsort()庫(kù)函數(shù)。這個(gè)庫(kù)函數(shù)接收4個(gè)參數(shù),其中一個(gè)是指向比較函數(shù)的指針,qsort()函數(shù)的聲明如下:
voidqsort(void*buf,size_tnum,size_tsize,int(*comp)(constvoid*ele1,constvoid*ele2));
一個(gè)對(duì)象的聲明,它的類(lèi)型就是想要轉(zhuǎn)換的結(jié)果類(lèi)型;
刪去標(biāo)識(shí)符以及任何類(lèi)似extern之類(lèi)的存儲(chǔ)限定符,并把剩余的內(nèi)容放在一對(duì)括號(hào)里面;把第二步產(chǎn)生的內(nèi)容放在需要進(jìn)行類(lèi)型轉(zhuǎn)換的對(duì)象的左邊。
當(dāng)調(diào)用qsort函數(shù)時(shí),可以向它傳遞一個(gè)你所喜歡的比較函數(shù)。你的比較函數(shù)將接收實(shí)際的數(shù)據(jù)類(lèi)型而不是void*參數(shù),就像下面這樣:
intintcompare(constint*i,constint*j){return*i-*j;}
這個(gè)函數(shù)并不與qsort()的comp()參數(shù)完全匹配,所以要進(jìn)行強(qiáng)制類(lèi)型轉(zhuǎn)換。假定有一個(gè)數(shù)組a,它具有10個(gè)元素,需要對(duì)它們進(jìn)行排序,根據(jù)上面列出的3個(gè)步驟,可以發(fā)現(xiàn)對(duì)qsort()的調(diào)用將會(huì)是下面這個(gè)樣子:qsort(a,10,sizeof(a[0]),(int(*)(constvoid*,constvoid*))intcompare);
#include#include
//對(duì)整型數(shù)進(jìn)行排序
intintcompare(constint*i,constint*j){
return*i-*j;}
//對(duì)double類(lèi)型的數(shù)進(jìn)行排序
intdoublecompare(constdouble*i,constdouble*j){
if(*i>*j)return1;elseif(*i<*j)return-1;elsereturn0;}
intmain(){
intbase[]={3,102,5,-2,98,52,18,56,38,70};inti,len=sizeof(base)/sizeof(base[0]);for(i=0;i printf("\\n"); qsort(base,len,sizeof(base[0]),(int(*)(constvoid*,constvoid*))intcompare);for(i=0;i printf("%d,",base[i]); printf("\\n\\n"); doublebase1[]={1.2,2.6,3.4,8.6,14.3,0.2,7.9,1.6,9.2,10.8,5.55};intlen1=sizeof(base1)/sizeof(base1[0]);for(i=0;i printf("%5.2f,",base1[i]);} printf("\\n"); qsort(base1,len1,sizeof(base1[0]),(int(*)(constvoid*,constvoid*))doublecompare);for(i=0;i printf("%5.2f,",base1[i]); return0;} malloc()、calloc()、realloc() 函數(shù)malloc()和calloc()都可以用來(lái)分配動(dòng)態(tài)內(nèi)存空間,但兩者稍有區(qū)別。malloc()函數(shù)有一個(gè)參數(shù),即要分配的內(nèi)存空間的大。 void*malloc(size_tsize); calloc()函數(shù)有兩個(gè)參數(shù),分別為元素的數(shù)目和每個(gè)元素的大小,兩個(gè)參數(shù)的乘積就是要分配的空間的大小:void*calloc(size_tnumElements,size_tsizeOfElement);如果調(diào)用成功,函數(shù)malloc()和calloc()都將返回所分配的內(nèi)存空間的首地址。 malloc()函數(shù)和calloc()函數(shù)的主要區(qū)別是前者不能初始化所分配的內(nèi)存空間,而后者能。如果由malloc()函數(shù)分配的內(nèi)存空間原來(lái)沒(méi)有被使用過(guò),則其中的每一位可能都是0;反之,如果這部分內(nèi)存空間曾經(jīng)被分配、釋放和重新分配,則其中可能遺留各種各樣的數(shù)據(jù)。也就是說(shuō),使用malloc()函數(shù)的程序開(kāi)始時(shí)(內(nèi)存空間還沒(méi)有被重新分配)能正常運(yùn)行,但經(jīng)過(guò)一段時(shí)間后(內(nèi)存空間已被重新分配)可能會(huì)出現(xiàn)問(wèn)題。 calloc()函數(shù)會(huì)將所分配的內(nèi)存空間中的每一位都初始化為零,也就是說(shuō),如果你是為字符類(lèi)型或整數(shù)類(lèi)型的元素分配內(nèi)存,那么這些元素將保證會(huì)被初始化為零;如果你是為指針類(lèi)型的元素分配內(nèi)存,那么這些元素通常(但無(wú)法保證)會(huì)被初始化為空指針;如果你是為實(shí)數(shù)類(lèi)型的元素分配內(nèi)存,那么這些元素可能(只在某些計(jì)算機(jī)中)會(huì)被初始化為浮點(diǎn)型的零。 為了明確是為一個(gè)數(shù)組分配內(nèi)存空間,有些程序員會(huì)選用calloc()函數(shù)。但是,除了是否初始化所分配的內(nèi)存空間這一點(diǎn)之外,絕大多數(shù)程序員認(rèn)為以下兩種函數(shù)調(diào)用方式?jīng)]有區(qū)別:calloc(numElements,sizeOfElement);malloc(numElements*sizeOfElement); 需要解釋的一點(diǎn)是,理論上(按照ANSIC標(biāo)準(zhǔn))指針的算術(shù)運(yùn)算只能在一個(gè)指定的數(shù)組中進(jìn)行,但是在實(shí)踐中,即使C編譯程序或翻譯器遵循這種規(guī)定,許多C程序還是沖破了這種限制。因此,盡管malloc()函數(shù)并不能返回一個(gè)數(shù)組,它所分配的內(nèi)存空間仍然能供一個(gè)數(shù)組使用(對(duì)realloc()函數(shù)來(lái)說(shuō)同樣如此,盡管它也不能返回一個(gè)數(shù)組)?傊(dāng)你在calloc()函數(shù)和malloc()函數(shù)之間作選擇時(shí),你只需考慮是否要初始化所分配的內(nèi)存空間,而不用考慮函數(shù)是否能返回一個(gè)數(shù)組。 realloc函數(shù)用于修改一個(gè)原先已經(jīng)分配的內(nèi)存塊的大小。使用這個(gè)函數(shù),你可以使一塊內(nèi)存擴(kuò)大或者縮小。如果它用于擴(kuò)大一個(gè)內(nèi)存塊,那么這塊內(nèi)存原先的內(nèi)容依然保留,新增加的內(nèi)存添加到原先內(nèi)存塊的后面,新內(nèi)存并未以任何方法進(jìn)行初始化。如果它用于縮小一個(gè)內(nèi)存塊,該內(nèi)存塊尾部的部分內(nèi)存便被拿掉,剩余部分內(nèi)存的原先內(nèi)容依然保留。如果原先的內(nèi)存塊無(wú)法改變大小,realloc將分配另外一塊正確大小的內(nèi)存,并把原先那塊內(nèi)存的內(nèi)容復(fù)制到新的塊上,因此,在使用realloc之后,就不能再使用指向舊內(nèi)存的指針,而是應(yīng)該改用realloc所返回的新指針。 常見(jiàn)的動(dòng)態(tài)內(nèi)存錯(cuò)誤(c和指針.P223.) 在使用動(dòng)態(tài)內(nèi)存分配的程序中,常常會(huì)出現(xiàn)許多錯(cuò)誤。這些錯(cuò)誤包括對(duì)NULL指針進(jìn)行解引用操作、對(duì)分配的內(nèi)存進(jìn)行操作時(shí)越過(guò)邊界、釋放并非動(dòng)態(tài)分配的內(nèi)存、試圖釋放一塊動(dòng)態(tài)分配的內(nèi)存的一部分以及一塊動(dòng)態(tài)內(nèi)存被釋放之后還繼續(xù)使用它。以下是一些需要注意的事項(xiàng): 1、在請(qǐng)求動(dòng)態(tài)內(nèi)存分配時(shí),要檢查所請(qǐng)求的內(nèi)存是否成功分配。 2、操作內(nèi)存時(shí),不要超過(guò)動(dòng)態(tài)分配的內(nèi)存的邊界。對(duì)分配的內(nèi)存之外的區(qū)域進(jìn)行訪(fǎng)問(wèn)可能會(huì)破壞別的數(shù)據(jù),產(chǎn)生一些莫名其妙的很難發(fā)現(xiàn)的bug。 3、傳遞給free的指針必須是一個(gè)從malloc、calloc、realloc函數(shù)返回的指針。 4、動(dòng)態(tài)分配的內(nèi)存必須整塊一起釋放,不允許釋放一塊動(dòng)態(tài)分配的內(nèi)存的一部分(realloc函數(shù)可以縮小一塊動(dòng)態(tài)分配的內(nèi)存,有效地釋放它尾部的部分內(nèi)存)。 在程序退出main()函數(shù)之后,還有可能執(zhí)行一部分代碼嗎? 可以,但這要借助C庫(kù)函數(shù)atexit()。利用atexit()函數(shù)可以在程序終止前完成一些—清理‖工作如果將指向一組函數(shù)的指針傳遞給atexit()函數(shù),那么在程序退出main()函數(shù)后(此時(shí)程序還未終止)就能自動(dòng)調(diào)用這組函數(shù)。在使用atexit()函數(shù)時(shí)你要注意這樣兩點(diǎn): 第一:由atexit()函數(shù)指定的要在程序終止前執(zhí)行的函數(shù)要用關(guān)鍵字void說(shuō)明,并且不能帶參數(shù);第二:由atexit()函數(shù)指定的函數(shù)在入棧時(shí)的順序和調(diào)用atexit()函數(shù)的順序相反,即它們?cè)趫?zhí)行時(shí)遵循后進(jìn)先出(LIFO)的原則。 #include#include voidmy_exit1(void){ printf("my_exit1()function!\\n");} voidmy_exit2(void){ printf("my_exit2()function!\\n");} voidmain(){ atexit(my_exit1);atexit(my_exit2); printf("now,eixtthisprogram...\\n");} 輸出結(jié)果為: now,eixtthisprogram...my_exit2()function!my_exit1()function! 總線(xiàn)錯(cuò)誤和段錯(cuò)誤相關(guān)概念(c專(zhuān)家編程.P157.) 在UNIX上編程時(shí),經(jīng)常會(huì)遇到如下兩個(gè)常見(jiàn)的運(yùn)行時(shí)錯(cuò)誤:buserror (總線(xiàn)錯(cuò)誤) segmentationfault(段錯(cuò)誤) 總線(xiàn)錯(cuò)誤 總線(xiàn)錯(cuò)誤幾乎都是由于未對(duì)齊的讀或?qū)懺斐傻。它之所以稱(chēng)為總線(xiàn)錯(cuò)誤,是因?yàn)槌霈F(xiàn)未對(duì)齊的內(nèi)存訪(fǎng)問(wèn)請(qǐng)求時(shí),被堵塞的組件就是地址總線(xiàn)。對(duì)齊的意思就是數(shù)據(jù)項(xiàng)只能存儲(chǔ)在地址是數(shù)據(jù)項(xiàng)大小的整數(shù)倍的內(nèi)存位置上。在現(xiàn)代的計(jì)算機(jī)架構(gòu)中,尤其是RISC架構(gòu),都需要數(shù)據(jù)對(duì)齊,因?yàn)榕c任意的對(duì)齊有關(guān)的額外邏輯會(huì)使整個(gè)內(nèi)存系統(tǒng)更大且更慢。通過(guò)迫使每個(gè)內(nèi)存訪(fǎng)問(wèn)局限在一個(gè)cache行或一個(gè)單獨(dú)的頁(yè)面內(nèi),可以極大地簡(jiǎn)化如cache控制器或內(nèi)存管理單元這樣的硬件。 我們表達(dá)“數(shù)據(jù)項(xiàng)不能跨越頁(yè)面或cache邊界”規(guī)則的方法多少有些間接,因?yàn)槲覀冇玫刂穼?duì)齊這個(gè)術(shù)語(yǔ)來(lái)陳述這個(gè)問(wèn)題,而不是直截了當(dāng)說(shuō)是禁止內(nèi)存跨頁(yè)訪(fǎng)問(wèn),但它們說(shuō)的是同一回事。例如,訪(fǎng)問(wèn)一個(gè)8字節(jié)的double數(shù)據(jù)時(shí),地址只允許是8的整數(shù)倍。所以一個(gè)double數(shù)據(jù)可以存儲(chǔ)于地址24、8008、32768,但不能存儲(chǔ)于地址1006,頁(yè)和cache的大小是經(jīng)過(guò)精心設(shè)計(jì)的,這樣只要遵守對(duì)齊規(guī)則就可以保證一個(gè)原子數(shù)據(jù)項(xiàng)不會(huì)跨越一個(gè)頁(yè)或cache塊的邊界。 段錯(cuò)誤 段錯(cuò)誤通常是由于解除引用一個(gè)未初始化或非法值的指針引起的。以發(fā)生頻率為序,最終可能導(dǎo)致段錯(cuò)誤的常見(jiàn)編程錯(cuò)誤是: 1、壞指針錯(cuò)誤:在指針賦值之前就用它來(lái)引用內(nèi)存;或者向庫(kù)函數(shù)傳遞一個(gè)壞指針(如果調(diào)試器顯示系統(tǒng)程序中出現(xiàn)了段錯(cuò)誤,很可能并不是系統(tǒng)程序引起的段錯(cuò)誤,問(wèn)題可能就出現(xiàn)在自己的代碼中);或者指針被釋放后還繼續(xù)訪(fǎng)問(wèn)它的內(nèi)容。 2、改寫(xiě)錯(cuò)誤:越過(guò)數(shù)組邊界寫(xiě)入數(shù)據(jù),在動(dòng)態(tài)分配的內(nèi)存空間以外寫(xiě)入數(shù)據(jù),或改寫(xiě)一些堆管理數(shù)據(jù)結(jié)構(gòu)(在動(dòng)態(tài)分配的內(nèi)存之前的區(qū)域?qū)懭霐?shù)據(jù)就很容易發(fā)生這種情況)。 3、指針釋放引起的錯(cuò)誤:釋放同一塊內(nèi)存兩次,或釋放一塊未曾使用malloc分類(lèi)的內(nèi)存,或釋放一個(gè)無(wú)效的指針。一個(gè)極為常見(jiàn)的與釋放內(nèi)存有關(guān)的錯(cuò)誤就是在for(p=start;p;p=p->next)這樣的循環(huán)中迭代一個(gè)鏈表,并在循環(huán)體內(nèi)使用free(p)這樣的語(yǔ)句。這樣,在下一次循環(huán)迭代時(shí),程序就會(huì)對(duì)已經(jīng)釋放的指針進(jìn)行解除引用操作,從而導(dǎo)致不可預(yù)料的結(jié)果。 怎樣判斷一個(gè)字符是數(shù)字、字母或其它類(lèi)別的符號(hào)? 在頭文件ctype.h中定義了一批函數(shù),它們可用來(lái)判斷一個(gè)字符屬于哪一類(lèi)別。下面列出了這些函數(shù):-----------------------------------------------------------------------------函數(shù)字符類(lèi)別返回非零值的字符 -----------------------------------------------------------------------------isdigit()十進(jìn)制數(shù)0--9 isxdigit()十六進(jìn)制數(shù)0--9,af,或A--Fisalnum()字母數(shù)字符號(hào)0--9,a--Z,或A--Zisalpha()字母az或A--Zislower()小寫(xiě)字母azisupper()大寫(xiě)字母A--Z isspace()空白符空格符,水平制表符,垂直制表符,換行符,換頁(yè)符,或回車(chē)符isgraph()非空白字符任何打印出來(lái)不是空白的字符(ASCII碼從21到7E)isprint()可打印字符 所有非空白字符,加上空格符 ispunct()標(biāo)點(diǎn)符除字母數(shù)字符號(hào)以外的所有非空白字符 iscntrl()控制字符除可打印字符外的所有字符(ASCII碼從00到1F,加上7F)------------------------------------------------------------------------------調(diào)用上述這些宏而不是自己編寫(xiě)測(cè)試字符類(lèi)別的程序有三點(diǎn)好處:首先,這些宏運(yùn)算速度快,因?yàn)樗鼈兊膶?shí)現(xiàn)方式通常都是利用位屏蔽技術(shù)來(lái)檢查一個(gè)表,所以即使是進(jìn)行一項(xiàng)相當(dāng)復(fù)雜的檢查,也比真正去比較字符的值要快得多。其次,這些宏都是正確的。如果你自己編寫(xiě)一個(gè)測(cè)試程序,你很容易犯邏輯上或輸入上的錯(cuò)誤,例如引入了一個(gè)錯(cuò)誤的字符(或漏掉了一個(gè)正確的字符)。第三,這些宏是可移植的。信不信由你,并非所有的人都使用同樣的含PC擴(kuò)充字符的ASCII字符集。也許今天你還不太在意,但是,當(dāng)你發(fā)現(xiàn)你的下一臺(tái)計(jì)算機(jī)使用的是Unicode字符集而不是ASCII字符集,你就會(huì)慶幸自己原來(lái)沒(méi)有按照字符集中的字符值來(lái)編寫(xiě)程序。 其他字符轉(zhuǎn)換函數(shù): isascii(測(cè)試字符是否為ASCII碼字符) intisascii(intc); 檢查參數(shù)c是否為ASCII碼字符,也就是判斷c的范圍是否在0到127之間。若參數(shù)c為ASCII碼字符,則返回TRUE,否則返回NULL(0)。 toascii(將整型數(shù)轉(zhuǎn)換成合法的ASCII碼字符) inttoascii(intc); toascii()會(huì)將參數(shù)c轉(zhuǎn)換成7位的unsignedchar值,第八位則會(huì)被清除,此字符即會(huì)被轉(zhuǎn)成ASCII碼字符。將轉(zhuǎn)換成功的ASCII碼字符值返回。 tolower(將大寫(xiě)字母轉(zhuǎn)換成小寫(xiě)字母) inttolower(intc); 若參數(shù)c為大寫(xiě)字母則將該對(duì)應(yīng)的小寫(xiě)字母返回。返回轉(zhuǎn)換后的小寫(xiě)字母,若不須轉(zhuǎn)換則將參數(shù)c值返回。 toupper(將小寫(xiě)字母轉(zhuǎn)換成大寫(xiě)字母) inttoupper(intc); 若參數(shù)c為小寫(xiě)字母則將該對(duì)映的大寫(xiě)字母返回。返回轉(zhuǎn)換后的大寫(xiě)字母,若不須轉(zhuǎn)換則將參數(shù)c值返回。 以isalmun為例,說(shuō)明這些函數(shù)的用法[剩余的其他函數(shù)跟它類(lèi)似]:intisalnum(intc) 檢查參數(shù)c是否為英文字母或阿拉伯?dāng)?shù)字,若參數(shù)c為字母或數(shù)字,則返回TRUE,否則返回NULL。此為宏定義,非真正函數(shù)。 #include#includeintmain(){ charstr[]="123c@#FDsP[e?"; for(inti=0;str[i]!=0;i++){ if(isalnum(str[i])) printf("%cisanalphanumericcharacter\\n",str[i]);} return0;} 怎樣將數(shù)字轉(zhuǎn)換為字符串? C語(yǔ)言提供了幾個(gè)標(biāo)準(zhǔn)庫(kù)函數(shù),可以將任意類(lèi)型(整型、長(zhǎng)整型、浮點(diǎn)型等)的數(shù)字轉(zhuǎn)換為字符串。以下是用itoa() #include#include intmain(){ intnum=435443435;charstr[30]; itoa(num,str,10); printf("Thenumber"num"is%dandthestring"str"is%s.\\n",num,str);getchar();return0;} 函數(shù)將整數(shù)轉(zhuǎn)換為字符串的一個(gè)例子: itoa()函數(shù)有3個(gè)參數(shù):第一個(gè)參數(shù)是要轉(zhuǎn)換的數(shù)字,第二個(gè)參數(shù)是要寫(xiě)入轉(zhuǎn)換結(jié)果的目標(biāo)字符串,第三個(gè)參數(shù)是轉(zhuǎn)移數(shù)字時(shí)所用的基數(shù)。在上例中,轉(zhuǎn)換基數(shù)為10。 ----------------------------------------------------------函數(shù)名作用 ----------------------------------------------------------itoa()將整型值轉(zhuǎn)換為字符串ltoa()將長(zhǎng)整型值轉(zhuǎn)換為字符串ultoa()將無(wú)符號(hào)長(zhǎng)整型值轉(zhuǎn)換為字符串 ---------------------------------------------------------- 請(qǐng)注意,上述函數(shù)與ANSI標(biāo)準(zhǔn)是不兼容的。能將整數(shù)轉(zhuǎn)換為字符串而且與ANSI標(biāo)準(zhǔn)兼容的方法是使用sprintf()函數(shù),請(qǐng)看下例: #include#include intmain(){ intnum=123456;charstr[50]; sprintf(str,"%d",num); printf("Thenumber"num"is%dandthestring"str"is%s.\\n",num,str); getchar();return0;} gcvt(將浮點(diǎn)型數(shù)轉(zhuǎn)換為字符串,取四舍五入) char*gcvt(doublenumber,size_tndigits,char*buf); gcvt()用來(lái)將參數(shù)number轉(zhuǎn)換成ASCII碼字符串,參數(shù)ndigits表示顯示的位數(shù)。gcvt()與ecvt()和fcvt()不同的地方在于,gcvt()所轉(zhuǎn)換后的字符串包含小數(shù)點(diǎn)或正負(fù)符號(hào)。若轉(zhuǎn)換成功,轉(zhuǎn)換后的字符串會(huì)放在參數(shù)buf指針?biāo)傅目臻g。該函數(shù)返回一字符串指針,此地址即為buf指針。 ecvt():將雙精度浮點(diǎn)型值轉(zhuǎn)換為字符串,轉(zhuǎn)換結(jié)果中不包含十進(jìn)制小數(shù)點(diǎn)fcvt():以指定位數(shù)為轉(zhuǎn)換精度,其余同ecvt() #include#include intmain(){ doublea=123.45546;doubleb=-1234.56;charptr[20]={0}; gcvt(a,7,ptr); printf("avalue=%s\\n",ptr);char*p=gcvt(b,5,ptr);printf("bvalue=%s\\n",p);} 怎樣將字符串轉(zhuǎn)換為數(shù)字? 下列函數(shù)可以將字符串轉(zhuǎn)換為數(shù)字: ------------------------------------------------------------------------函數(shù)名 作用 ------------------------------------------------------------------------atof()將字符串轉(zhuǎn)換為雙精度浮點(diǎn)型值atoi()將字符串轉(zhuǎn)換為整型值atol()將字符串轉(zhuǎn)換為長(zhǎng)整型值 strtod()將字符串轉(zhuǎn)換為雙精度浮點(diǎn)型值,并報(bào)告不能被轉(zhuǎn)換的所有剩余數(shù)字strtol()將字符串轉(zhuǎn)換為長(zhǎng)整值,并報(bào)告不能被轉(zhuǎn)換的所有剩余數(shù)字strtoul()將字符串轉(zhuǎn)換為無(wú)符號(hào)長(zhǎng)整型值,并報(bào)告不能被轉(zhuǎn)換的所有剩余數(shù)字 ------------------------------------------------------------------------atof(將字符串轉(zhuǎn)換成浮點(diǎn)型數(shù))doubleatof(constchar*nptr); atof()會(huì)掃描參數(shù)nptr字符串,跳過(guò)前面的空格字符,直到遇上數(shù)字或正負(fù)符號(hào)才開(kāi)始做轉(zhuǎn)換,而再遇到非數(shù)字或字符串結(jié)束時(shí)("\\0")才結(jié)束轉(zhuǎn)換,并將結(jié)果返回。參數(shù)nptr字符串可包含正負(fù)號(hào)、小數(shù)點(diǎn)或E(e)來(lái)表示指數(shù)部分,如123.456或123e-2。該函數(shù)返回轉(zhuǎn)換后的浮點(diǎn)型數(shù)。atof()與使用strtod(nptr,(char**)NULL)結(jié)果相同。atoi(將字符串轉(zhuǎn)換成整型數(shù))intatoi(constchar*nptr); atoi()會(huì)掃描參數(shù)nptr字符串,跳過(guò)前面的空格字符,直到遇上數(shù)字或正負(fù)符號(hào)才開(kāi)始做轉(zhuǎn)換,而再遇到非數(shù)字或字符串結(jié)束時(shí)("\\0")才結(jié)束轉(zhuǎn)換,并將結(jié)果返回。該函數(shù)返回轉(zhuǎn)換后的整型數(shù)。atoi()與使用strtol(nptr,(char**)NULL,10);結(jié)果相同。atol(將字符串轉(zhuǎn)換成長(zhǎng)整型數(shù))longatol(constchar*nptr); atol()會(huì)掃描參數(shù)nptr字符串,跳過(guò)前面的空格字符,直到遇上數(shù)字或正負(fù)符號(hào)才開(kāi)始做轉(zhuǎn)換,而再遇到非數(shù)字或字符串結(jié)束時(shí)("\\0")才結(jié)束轉(zhuǎn)換,并將結(jié)果返回。該函數(shù)返回轉(zhuǎn)換后的長(zhǎng)整型數(shù)。atol()與使用strtol(nptr,(char**)NULL,10);結(jié)果相同。 #include#include intmain(){ char*a0="-1000";char*b0="456"; intc0=atoi(a0)+atoi(b0); printf("atoi:c0=%d\\n",c0); char*a1="-100.23";char*b1="200e-2"; floatc1=atof(a1)+atof(b1);printf("atof:c1=%f\\n",c1); char*a2="4563453";char*b2="4563431237"; longc2=atol(a2)+atol(b2);printf("atol:c2=%ld\\n",c2);} strtod(將字符串轉(zhuǎn)換成浮點(diǎn)數(shù)) doublestrtod(constchar*nptr,char**endptr); strtod()會(huì)掃描參數(shù)nptr字符串,跳過(guò)前面的空格字符,直到遇上數(shù)字或正負(fù)符號(hào)才開(kāi)始做轉(zhuǎn)換,到出現(xiàn)非數(shù)字或字符串結(jié)束時(shí)("\\0")才結(jié)束轉(zhuǎn)換,并將結(jié)果返回。若endptr不為NULL,則會(huì)將遇到不合條件而終止的nptr中的字符指針由endptr傳回。參數(shù)nptr字符串可包含正負(fù)號(hào)、小數(shù)點(diǎn)或E(e)來(lái)表示指數(shù)部分。如123.456或123e-2。該函數(shù)返回轉(zhuǎn)換后的浮點(diǎn)型數(shù)。strtol(將字符串轉(zhuǎn)換成長(zhǎng)整型數(shù)) longintstrtol(constchar*nptr,char**endptr,intbase); strtol()會(huì)將參數(shù)nptr字符串根據(jù)參數(shù)base來(lái)轉(zhuǎn)換成長(zhǎng)整型數(shù)。參數(shù)base范圍從2至36,或0。參數(shù)base代表采用的進(jìn)制方式,如base值為10則采用10進(jìn)制,若base值為16則采用16進(jìn)制等。當(dāng)base值為0時(shí)則是采用10進(jìn)制做轉(zhuǎn)換,但遇到如"0x"前置字符則會(huì)使用16進(jìn)制做轉(zhuǎn)換。一開(kāi)始strtol()會(huì)掃描參數(shù)nptr字符串,跳過(guò)前面的空格字符,直到遇上數(shù)字或正負(fù)符號(hào)才開(kāi)始做轉(zhuǎn)換,再遇到非數(shù)字或字符串結(jié)束時(shí)("\\0")結(jié)束轉(zhuǎn)換,并將結(jié)果返回。若參數(shù)endptr不為NULL,則會(huì)將遇到不合條件而終止的nptr中的字符指針由endptr返回。該函數(shù)返回轉(zhuǎn)換后的長(zhǎng)整型數(shù),否則返回ERANGE并將錯(cuò)誤代碼存入errno中(ERANGE指定的轉(zhuǎn)換字符串超出合法范圍) strtoul(將字符串轉(zhuǎn)換成無(wú)符號(hào)長(zhǎng)整型數(shù)) unsignedlongintstrtoul(constchar*nptr,char**endptr,intbase); strtoul()會(huì)將參數(shù)nptr字符串根據(jù)參數(shù)base來(lái)轉(zhuǎn)換成無(wú)符號(hào)的長(zhǎng)整型數(shù)。參數(shù)base范圍從2至36,或0。參數(shù)base代表采用的進(jìn)制方式,如base值為10則采用10進(jìn)制,若base值為16則采用16進(jìn)制數(shù)等。當(dāng)base值為0時(shí)則是采用10進(jìn)制做轉(zhuǎn)換,但遇到如"0x"前置字符則會(huì)使用16進(jìn)制做轉(zhuǎn)換。一開(kāi)始strtoul()會(huì)掃描參數(shù)nptr字符串,跳過(guò)前面的空格字符串,直到遇上數(shù)字或正負(fù)符號(hào)才開(kāi)始做轉(zhuǎn)換,再遇到非數(shù)字或字符串結(jié)束時(shí)("\\0")結(jié)束轉(zhuǎn)換,并將結(jié)果返回。若參數(shù)endptr不為NULL,則會(huì)將遇到不合條件而終止的nptr中的字符指針由endptr返回。該函數(shù)返回轉(zhuǎn)換后的長(zhǎng)整型數(shù),否則返回ERANGE并將錯(cuò)誤代碼存入errno中(ERANGE指定的轉(zhuǎn)換字符串超出合法范圍) #include#includeintmain(){ chara[]="1000000000abcde";charb[]="1000000000abcde";charc[]="ffffOPQRST";char**p; printf("a=%d,p=%s\\n",strtol(a,p,10),*p);printf("b=%d,p=%s\\n",strtol(b,p,2),*p);printf("c=%d,p=%s\\n",strtol(c,p,16),*p);} 輸出結(jié)果為: a=1000000000,p=abcdeb=512,p=abcdec=65535,p=OPQRST 字符串拷貝和內(nèi)存拷貝函數(shù) strcpy(拷貝字符串) 定義函數(shù):char*strcpy(char*dest,constchar*src); strcpy()函數(shù)只能拷貝字符串。strcpy()函數(shù)將源字符串src的每個(gè)字節(jié)拷貝到目的字符串dest中,src字符串末尾的"\\0"也被拷貝過(guò)去。strcpy()函數(shù)返回參數(shù)dest的起始地址。如果參數(shù)dest所指的內(nèi)存空間不夠大,可能會(huì)造成緩沖溢出的錯(cuò)誤情況(程序員必須保證目標(biāo)字符數(shù)組的空間足夠容納需要復(fù)制的字符串。如果src字符串比dest字符串長(zhǎng),多余的字符仍將被復(fù)制,它們將覆蓋原先存儲(chǔ)于dest數(shù)組后面的內(nèi)存空間的值),在編寫(xiě)程序時(shí)請(qǐng)?zhí)貏e留意,或者用strncpy()來(lái)取代。如果參數(shù)src和dst在內(nèi)存中出現(xiàn)重疊,其結(jié)果是未定義的。 strncpy(拷貝字符串) 定義函數(shù):char*strncpy(char*dest,constchar*src,size_tn); strncpy()會(huì)將參數(shù)src字符串拷貝前n個(gè)字符至參數(shù)dest所指的地址。函數(shù)返回參數(shù)dest的字符串起始地址。 注意n的取值范圍,不要超過(guò)src和dest的長(zhǎng)度。 #include#include intmain(){ chara1[30]="string(1)";charb1[]="STRING(2)"; printf("beforestrcpy():%s\\n",a1); printf("afterstrcpy():%s\\n",strcpy(a1,b1)); chara2[30]="string(1)";charb2[]="STRING(2)"; printf("beforestrncpy():%s\\n",a2); printf("afterstrncpy():%s\\n",strncpy(a2,b2,6));} memcpy(拷貝內(nèi)存內(nèi)容) 定義函數(shù):void*memcpy(void*dest,constvoid*src,size_tn); memcpy()用來(lái)拷貝src所指的內(nèi)存內(nèi)容前n個(gè)字節(jié)到dest所指的內(nèi)存地址上。與strcpy()不同的是,memcpy()會(huì)完整的復(fù)制n個(gè)字節(jié),不會(huì)因?yàn)橛龅阶址Y(jié)束"\\0"而結(jié)束。memcpy()函數(shù)可以拷貝任意類(lèi)型的數(shù)據(jù)。memcpy()函數(shù)返回指向dest的指針。指針src和dest所指的內(nèi)存區(qū)域不可重疊。在拷貝字符串時(shí),通常都使用strcpy()函數(shù);在拷貝其它數(shù)據(jù)(例如結(jié)構(gòu))時(shí),通常都使用memcpy()函數(shù)。memmove(拷貝內(nèi)存內(nèi)容) 定義函數(shù):void*memmove(void*dest,constvoid*src,size_tn); memmove()與memcpy()一樣都是用來(lái)拷貝src所指的內(nèi)存內(nèi)容前n個(gè)字節(jié)到dest所指的地址上。不同的是,當(dāng)src和dest所指的內(nèi)存區(qū)域重疊時(shí),memmove()仍然可以正確的處理,不過(guò)執(zhí)行效率上會(huì)比使用memcpy()略慢些。該函數(shù)返回指向dest的指針。 #includeintmain(){ chara[30]="string(1)";charb[]="string(2)"; printf("beforestrcpy():%s\\n",a); printf("afterstrcpy():%s\\n",strcpy(a,b)); a[30]="string(1)";b[]="string(2)"; printf("beforestrncpy():%s\\n",a); printf("afterstrncpy():%s\\n",strncpy(a,b,6));} memccpy(拷貝內(nèi)存內(nèi)容) 定義函數(shù):void*memccpy(void*dest,constvoid*src,intc,size_tn);memccpy()用來(lái)拷貝src所指的內(nèi)存內(nèi)容前n個(gè)字節(jié)到dest所指的地址上。與memcpy()不同的是,memccpy()會(huì)在復(fù)制時(shí)檢查參數(shù)c是否出現(xiàn),若是則返回dest中值為c的下一個(gè)字節(jié)地址。該函數(shù)返回指向dest中值為c的下一個(gè)字節(jié)指針。返回值為NULL表示在src所指內(nèi)存前n個(gè)字節(jié)中沒(méi)有值為c的字節(jié)。 #include#includeintmain(){ chara[]="string(a)";charb[]="string(b)";char*p; p=(char*)memccpy(a,b,"k",sizeof(b)); if(p==NULL){ //注意p為NULL的情況,這時(shí)不能讀取p所指的地方的內(nèi)容printf("thereturnpointerofmymccpyisnull!\\n");}else{ printf("memccpy():%s,*p=%c\\n",a,*p);}} bcopy(拷貝內(nèi)存內(nèi)容) 定義函數(shù):voidbcopy(constvoid*src,void*dest,intn); bcopy()與memcpy()一樣都是用來(lái)拷貝src所指的內(nèi)存內(nèi)容前n個(gè)字節(jié)到dest所指的地址,不過(guò)參數(shù)src與dest在傳給函數(shù)時(shí)是相反的位置。建議使用memcpy()取代。 字符串和內(nèi)存數(shù)據(jù)比較函數(shù) strcmp(比較字符串) intstrcmp(constchar*s1,constchar*s2); strcmp()用來(lái)比較參數(shù)s1和s2字符串。字符串大小的比較是以ASCII碼表上的順序來(lái)決定,此順序亦為字符的值。strcmp()首先將s1第一個(gè)字符值減去s2第一個(gè)字符值,若差值為0則再繼續(xù)比較下個(gè)字符,若差值不為0則將差值返回。例如字符串"Ac"和"ba"比較則會(huì)返回字符"A"(65)和"b"(98)的差值(-33)。若參數(shù)s1和s2字符串相同則返回0。s1若大于s2則返回大于0的值。s1若小于s2則返回小于0的值。 strcoll(采用目前區(qū)域的字符排列次序來(lái)比較字符串)intstrcoll(constchar*s1,constchar*s2); strcoll()會(huì)依環(huán)境變量LC_COLLATE所指定的字符排列次序來(lái)比較s1和s2字符串。若參數(shù)s1和s2字符串相同則返回0,s1若大于s2則返回大于0的值,s1若小于s2則返回小于0的值。若LC_COLLATE為"POSIX"或"C",則strcoll()與strcmp()作用完全相同。 strcasecmp(忽略大小寫(xiě)比較字符串) intstrcasecmp(constchar*s1,constchar*s2); strcasecmp()用來(lái)比較參數(shù)s1和s2字符串,比較時(shí)會(huì)自動(dòng)忽略大小寫(xiě)的差異。若參數(shù)s1和s2字符串相同則返回0,s1長(zhǎng)度大于s2長(zhǎng)度則返回大于0的值,s1長(zhǎng)度若小于s2長(zhǎng)度則返回小于0的值。 strncasecmp(忽略大小寫(xiě)比較字符串) intstrncasecmp(constchar*s1,constchar*s2,size_tn); strncasecmp()用來(lái)比較參數(shù)s1和s2字符串前n個(gè)字符,比較時(shí)會(huì)自動(dòng)忽略大小寫(xiě)的差異。若參數(shù)s1和s2字符串相同則返回0。s1若大于s2則返回大于0的值,s1若小于s2則返回小于0的值。 strcmpi()或stricmp():對(duì)兩個(gè)字符串進(jìn)行大小寫(xiě)不敏感的比較。strncmp():對(duì)兩個(gè)字符串的一部分進(jìn)行大小寫(xiě)敏感的比較。strnicmp():對(duì)兩個(gè)字符串的一部分進(jìn)行大小寫(xiě)不敏感的比較。 #include#include intmain(){ char*a="aBcDeF";char*b="AbCdEf";char*c="aacdef";char*d="ABCDEF"; printf("strcmpi(a,b):%d\\n",strcmpi(a,b));printf("stricmp(a,b):%d\\n",stricmp(a,b));printf("strcmp(a,d):%d\\n",strcmp(a,d)); printf("strncmp(a,b,3):%d\\n",strncmp(a,b,3));printf("strnicmp(a,b,3):%d\\n",strnicmp(a,b,3)); getchar();return0;} memcmp(比較內(nèi)存內(nèi)容) intmemcmp(constvoid*s1,constvoid*s2,size_tn); memcmp()用來(lái)比較s1和s2所指的內(nèi)存區(qū)間前n個(gè)字符。字符串大小的比較是以ASCII碼表上的順序來(lái)決定。memcmp()首先將s1第一個(gè)字符值減去s2第一個(gè)字符的值,若差為0則再繼續(xù)比較下個(gè)字符,若差值不為0則將差值返回。例如,字符串"Ac"和"ba"比較則會(huì)返回字符"A"(65)和"b"(98)的差值(-33)。若參數(shù)s1和s2所指的內(nèi)存內(nèi)容都完全相同則返回0值。s1若大于s2則返回大于0的值。s1若小于s2則返回小于0的值。 bcmp(比較內(nèi)存內(nèi)容) intbcmp(constvoid*s1,constvoid*s2,intn); bcmp()用來(lái)比較s1和s2所指的內(nèi)存區(qū)間前n個(gè)字節(jié),若參數(shù)n為0,則返回0。若參數(shù)s1和s2所指的內(nèi)存內(nèi)容都完全相同則返回0值,否則返回非零值。建議使用memcmp()取代。 #include#includeintmain(){ char*a="aBcDeF";char*b="AbCdEf";char*c="aacdef";char*d="aBcDeF"; printf("memcmp(a,b):%d\\n",memcmp((void*)a,(void*)b,6));printf("memcmp(a,c):%d\\n",memcmp((void*)a,(void*)c,6));printf("memcmp(a,d):%d\\n",memcmp((void*)a,(void*)d,6));} 友情提示:本文中關(guān)于《《C專(zhuān)家編程》總結(jié)》給出的范例僅供您參考拓展思維使用,《C專(zhuān)家編程》總結(jié):該篇文章建議您自主創(chuàng)作。 來(lái)源:網(wǎng)絡(luò)整理 免責(zé)聲明:本文僅限學(xué)習(xí)分享,如產(chǎn)生版權(quán)問(wèn)題,請(qǐng)聯(lián)系我們及時(shí)刪除。
《《C專(zhuān)家編程》總結(jié)》由互聯(lián)網(wǎng)用戶(hù)整理提供,轉(zhuǎn)載分享請(qǐng)保留原作者信息,謝謝!
鏈接地址:http://www.seogis.com/gongwen/748323.html