C語言深度剖析筆記_第1頁
C語言深度剖析筆記_第2頁
C語言深度剖析筆記_第3頁
C語言深度剖析筆記_第4頁
C語言深度剖析筆記_第5頁
已閱讀5頁,還剩12頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡介

32個關(guān)鍵字簡記注:該筆記根據(jù)《C語言深度剖析》一書而成sizeof是32個關(guān)鍵字之一什么是聲明?什么是定義?詳見P11頁定義與聲明的區(qū)別:定義創(chuàng)立了對象并為這個對象分配了內(nèi)存,聲明沒有分配內(nèi)存,可以先聲明把位子占了〔如函數(shù)的聲明〕,然后再定義auto關(guān)鍵字〔P12〕:可以當(dāng)其不存在,默認(rèn)情況下所有變量都是auto型的register關(guān)鍵字〔P12〕:請求編譯器盡可能的將變量存儲在cpu內(nèi)部的存放器中,是盡可能不是絕對,一個cpu的存放器也就幾個或者幾十個,存取速度最快register變量必須是一個單個的值,而且其長度最長為一個整型的長度。對這種變量不可以使用取址運(yùn)算符來獲取其地址,因為它不再內(nèi)存中static關(guān)鍵字〔P13〕:可以修飾變量也可以修飾函數(shù)、存儲區(qū)為內(nèi)存中的靜態(tài)存儲區(qū)、修飾函數(shù)時是為了將該函數(shù)的作用域局限于本文件內(nèi),解決同名問題例:#include<stdio.h>#include<stdlib.h>staticintj;voidfun1(void){staticinti=0; i++;printf("%d\t",i);}voidfun2(void){ j=0; j++;}intmain(){intk;for(k=0;k<10;k++) { fun1(); fun2(); }printf("\nj=%d\n",j);//此處的i不能輸出,因為fun1〔〕函數(shù)的返回值是void型,staticinti的作用域為整個fun1〔〕函數(shù)內(nèi),超出無效//程序完結(jié)時,i=10;j=1;returnEXIT_SUCCESS;}》》》》1 2 3 4 5 6 7 8 9 10 j=1C語言的數(shù)據(jù)類型:根本數(shù)據(jù)類型關(guān)鍵字〔P14〕:short、long、int、char、float、double〔共6種〕構(gòu)造數(shù)據(jù)類型:數(shù)組、結(jié)構(gòu)體struct、公用體union、枚舉類型enum指針類型空類型void32位系統(tǒng)中:short為2個字節(jié),int為4個字節(jié),long為4個字節(jié),float為4個字節(jié)、double為8個字節(jié),char為1個字節(jié)〔一般情況下〕隱式類型轉(zhuǎn)換:short,char→→int→unsignedint→long→double〔主干道〕←←floatchar+char是先隱式轉(zhuǎn)換為int的,再進(jìn)行運(yùn)算任何數(shù)據(jù)都的先跑到主干道,才能運(yùn)算〔int到double為主干道,32位的問題〕變量名的命名規(guī)那么〔P15〕:注意x與X之間,1〔數(shù)字1〕與l〔字母l〕之間,0與o之間的區(qū)別,不建議使用這些易混淆的定義了變量的同時千萬別忘了對其初始化不同數(shù)據(jù)之間的運(yùn)算要注意精度擴(kuò)展問題,一般是低精度數(shù)據(jù)向高精度數(shù)據(jù)擴(kuò)展sizeof關(guān)鍵字〔P19〕:例:inti=0;Asizeof〔int〕Bsizeof〔i〕CsizeofintDsizeofi毫無疑問,在32位系統(tǒng)下A、B的值為4,其實D也是4注:函數(shù)名后面沒有括號是絕對不行的,編譯C時,編譯器會提示出錯我們可以在int前加unsight、const等關(guān)鍵字但是不能加sizeof關(guān)鍵字sizeof在計算變量的大小時括號可以省略但是在計算類型的大小時括號是不可以省略的sizeof〔int〕*p是什么意思?個人認(rèn)為相當(dāng)于是sizeof〔〔int〕*p〕例:#include<stdio.h>#include<stdlib.h>voidfun(intb[100]){printf("\nsizeof(b)=%d\n",sizeof(b));}intmain(){int*p=NULL;printf("sizeof(p)=%d\n",sizeof(p));printf("sizeof(*p)=%d\n",sizeof(*p));inta[100];printf("\nsizeof(a)=%d\n",sizeof(a));//此處的大小為100,其余均為四printf("sizeof(a[100])=%d\n",sizeof(a[100]));//a[100]存在嗎??越了界還是按4累加?printf("sizeof(&a)=%d\n",sizeof(&a));printf("sizeof(&a[0])=%d\n",sizeof(&a[0]));intb[100]; fun(b);returnEXIT_SUCCESS;}》》》sizeof(p)=4sizeof(*p)=4sizeof(a)=400sizeof(a[100])=4sizeof(&a)=4sizeof(&a[0])=4sizeof(b)=4---------------------------------------------------------------------signed、unsigned關(guān)鍵字〔P20〕:在計算機(jī)系統(tǒng)中,任何數(shù)據(jù)到了底層都會被轉(zhuǎn)化成0、1在缺省的情況下,系統(tǒng)默認(rèn)為是singed型在計算機(jī)系統(tǒng)中數(shù)值用補(bǔ)碼存儲的原因是:使用補(bǔ)碼,可以將符號位與其他位統(tǒng)一處理,同時減法也可以按加法處理。兩個用補(bǔ)碼表示的數(shù)相加時,如果最高位〔符號位〕有進(jìn)位,那么進(jìn)位被舍棄-0和0在內(nèi)存里是怎么存儲的???例:#include<stdio.h>#include<stdlib.h>#include<string.h>intmain(){chara[1000];inti;for(i=0;i<1000;i++) { a[i]=-1-i; }printf("strlen(a)=%d\n",strlen(a));returnEXIT_SUCCESS;}》》》strlen(a)=255例:#include<stdio.h>#include<stdlib.h>#include<string.h>intmain(){inti=-20;unsignedintj=10;printf("i+j=%d\n",i+j);returnEXIT_SUCCESS;}》》》》i+j=-10例:#include<stdio.h>#include<stdlib.h>intmain(){unsignedi;for(i=9;i>=0;i--) {printf("%u\n",i); }returnEXIT_SUCCESS;}》》》程序無限循環(huán),失控,輸出的貌似是內(nèi)存地址,從第一個隨機(jī)十位數(shù)開始,依次減一,無限下去if、else關(guān)鍵字〔P21〕:bool型的數(shù)據(jù)初始化時一般初始化為false,這樣比擬好注意float型數(shù)據(jù)與0值進(jìn)行比擬的問題,float、double型數(shù)據(jù)的精度是有限度的注:不要在很大的浮點數(shù)與很小的浮點數(shù)之間進(jìn)行運(yùn)算,其結(jié)果會讓你無奈注意指針變量與0值的比擬,推薦寫法:if(NULL==p);if(NULL!=p);else始終與同括號內(nèi)最近的未匹配的if語句結(jié)合,最好用標(biāo)準(zhǔn)的寫法,相見P23不推薦使用tab鍵,要寫空語句時不要只寫一個“;〞要寫成“NULL;〞寫if語句時,一般情況下先處理正常情況再處理異常情況,以便提高效率,可讀性也較好switch、case關(guān)鍵字〔P25〕:注意default的使用,不要把其當(dāng)case用也不要舍棄不用,另:case后只能是整型或字符型常量、為了可讀性等注意case的合理排序,注:不要為了使用case語句而刻意制造一個變量do、while、for關(guān)鍵字〔P29〕:在switch、case語句中能否使用continue??注:循環(huán)的嵌套不要超過三層goto關(guān)鍵字〔P31〕:能不用這不用,假設(shè)可以,舍棄吧?。oid關(guān)鍵字〔P32〕:void*為空類型指針,可以指向任何類型的數(shù)據(jù)、void真正的作用是:1、對函數(shù)返回的限定2、對函數(shù)參數(shù)的限定。任何類型的指針都可以直接賦值給void*型指針,這并不意味著void*也可以無需強(qiáng)制類型轉(zhuǎn)換直接賦值給其他類型的指針,如可以說“男人或者女人都是人〞,但不能說“男人是人〞在C語言中,但凡不見返回時類型限定的函數(shù),編譯器會默認(rèn)為返回的是整型。在C語言中,可以給無參數(shù)的函數(shù)傳送任意類型的參數(shù),但在c++編譯器中會出錯,假設(shè)函數(shù)不接受任何參數(shù),一定要指明函數(shù)參數(shù)為void型ANSI規(guī)定:不能對void指針進(jìn)行算法操作,GUN那么規(guī)定:void*的算法與char*的算法一致,我們以ANSI為標(biāo)準(zhǔn)??梢赃@樣進(jìn)行算法操作:void*ptr;〔char*〕ptr++;〔char*〕ptr+=1;如果函數(shù)的參數(shù)可以是任意類型的指針,那么應(yīng)聲明其參數(shù)的類型為void*return關(guān)鍵字〔P35〕:用于終止一個函數(shù)并返回其后跟的值return〔val〕;括號一般不省略,尤其返回是一個表達(dá)式的值時。return;返回的到底是什么??const關(guān)鍵字〔P36〕:注:define不是關(guān)鍵字const修飾的只讀變量必須在定義的同時初始化,case后面是否可以是const修飾的只讀變量——不可以const與define的區(qū)別詳見P36編譯器通常不會為const只讀變量分配存儲空間,而是將他們保存在符號表中,這是的它成為一個編譯期間的值,值分配一次內(nèi)存空間,存放在靜態(tài)區(qū)#defineM3//宏常量,注意語句的最后無分號constintN=6;//此時并未將N放入內(nèi)存中...inti=N;//此時為N分配內(nèi)存,以后不在分配!intI=M;//預(yù)編譯期間進(jìn)行宏替換,分配內(nèi)存intj=N;//沒有內(nèi)存分配intJ=M;//再次進(jìn)行宏替換,又一次分配內(nèi)存!const修飾指針時注意:constint*p;//p可變,p指向的對象不可變intconst*p;//p可變,p指向的對象不可變int*constp;//p不可變,p指向的對象可變constint*constp;//指針p和p指向的對象都不可變const可以修飾一般變量、數(shù)組、指針、函數(shù)的參數(shù)、函數(shù)的返回值。volatile關(guān)鍵字〔P38〕:與const一樣,是一種類型修飾符,用它修飾的變量表示可以被某些編譯器未知的因素更改volatile可以保證對特殊地址的穩(wěn)定訪問。extern關(guān)鍵字〔P38〕:該關(guān)鍵字可以置于變量和函數(shù)前,以標(biāo)識變量或者函數(shù)的定義在別的文件中。struct關(guān)鍵字〔P39〕:其作用是將一些相關(guān)的數(shù)據(jù)打包成一個整體,方便使用函數(shù)的參數(shù)一般情況下不要超過四個,假設(shè)超過那么將參數(shù)包裝成一個結(jié)構(gòu)體。結(jié)構(gòu)體所占內(nèi)存的大小是其成員所占內(nèi)存之和例:structstudent{}stu;sizeof〔stu〕的值是一而不是〇〔具體原因看書〕柔性數(shù)組:C99標(biāo)準(zhǔn),具體看書struct與class其實本質(zhì)上是一樣的,只不過struct默認(rèn)的是public型,而class默認(rèn)的是private型,在c++里面可以通用union關(guān)鍵字〔P41〕:其用法與struct非常類似,union維護(hù)足夠的空間來放置多個數(shù)據(jù)成員中的一種,而不是為每個數(shù)據(jù)成員配置空間。所有的數(shù)據(jù)成員公用一個空間,同一時刻只能存儲其中的一個。大小端模式對union有影響,大小端的判定參看本節(jié);注:某些系統(tǒng)可以同時支持大端和小端模式例:#include<stdio.h>#include<stdlib.h>#include<string.h>intmain(){inta[5]={1,2,3,4,5};int*ptr1=(int*)(&a+1);int*ptr2=(int*)((int)a+1);printf("%x,%x",ptr1[-1],*ptr2);return0;}》》》5,2000000enum關(guān)鍵字〔P44〕:枚舉、枚舉與define的區(qū)別參看本書例:#include<stdio.h>#include<stdlib.h>intmain(void){enumcolor//枚舉類型,Green=1,Red=2依次累加1 {Green=1,Red,Blue,//Blue=3Yellow,//Yellow=4Red_Blue=10,Green_Bule//Green_Blue=11 }ColorVal;printf("sizeof(ColorVal)=%d\n",sizeof(ColorVal));returnEXIT_SUCCESS;}》》》》》sizeof(ColorVal)=4//枚舉類型貌似只能賦整形值typedef關(guān)鍵字〔P45〕:給一個已經(jīng)存的數(shù)據(jù)類型〔注:是數(shù)據(jù)不是變量〕取一個別名,而費(fèi)定義一個新的數(shù)據(jù)類型。具體細(xì)節(jié)參看本節(jié)第二章符號編譯器編譯時會將代碼段中的注釋局部用空格代替//thisisa\ cup?這是一條合法的注釋,因為“\〞是鏈接符〔“/*〞總是與離他最近的“*/〞想鏈接〕‘a(chǎn)’與“a〞,前者占一個字節(jié),后者占兩個字節(jié)。在進(jìn)行邏輯運(yùn)算符時,假設(shè)“||〞之前的那個等式為真,那么“||〞后面的式子那么不經(jīng)行運(yùn)算,沒有必要了,邏輯與〔&&〕也有這個問題,需要注意。位運(yùn)算符:&、|、~、^(按位異或)、<<〔左移〕、>>〔右移〕按位異或操作可以實現(xiàn)不用第三個臨時變量交換兩個變量的值,a^=b;b^=a;a^=b;不推薦這么做,讀起來太費(fèi)力“>>〞其作用是將其左邊的數(shù)向右移動〔“>>〞右邊的數(shù)〕位,注意:對于有符號的數(shù),右移時符號位將隨同移動。當(dāng)為正數(shù)時最高位補(bǔ)0,當(dāng)為符號〔1〕時,最高位補(bǔ)1還是0取決于具體的編譯器。另:左移時高位舍棄,地位補(bǔ)0。例:ox01<<2+3;的結(jié)果是32〔100000〕。因為+號的優(yōu)先級比<<的優(yōu)先級高注:左移或者右移的位數(shù)不能大于數(shù)據(jù)的長度,不能小于零。K=i+++i+++i++;i會在遇到分號或逗號時才進(jìn)行自加關(guān)于符號優(yōu)先級的問題在P60,可看看。關(guān)于含有負(fù)數(shù)的除法運(yùn)算在P59頁,注:余數(shù)與被除數(shù)的正負(fù)號相同,余數(shù)可能為負(fù)數(shù)。第三章預(yù)處理ANSI定義的C語言預(yù)處理指令:#define、#undef、#include、#if、#else、#elif、#endif、#ifdef、#ifndef、#line、#error、#pragma宏定義#define:它可以出現(xiàn)在代碼的任何地方,從本行宏定義開始,以后的代碼就都認(rèn)識這個宏了,也可以把任何東西定義成宏。除了定義宏常數(shù)之外,經(jīng)常還用來定義字符串,尤其是路徑。用宏定義注釋符號是不行的,因為注釋先于預(yù)處理指令被處理#undef:是用來撤銷宏定義的,詳見P66#ifdef:如果標(biāo)識符已被#define命令定義過那么對其下的程序進(jìn)行編譯,否那么編譯#else下面的程序〔#else及其下的代碼可以缺省〕#ifndef:如果標(biāo)識符沒有被#define命令定義過那么對其下的程序進(jìn)行編譯,否那么編譯#else下面的程序〔#else及其下的代碼可以缺省〕另:還有#if常量表達(dá)式+#else+#endif的形式至于#elif命令的意義與elseif相同,形成一個if–elseif的階梯狀語句,可以進(jìn)行多種編譯選那么注意#include<stdio.h>與#include“filename〞的區(qū)別,#include“filename〞表示現(xiàn)在當(dāng)前目錄查找名為filename的文件,沒有的話再去C庫中查找。蟻跡尋蹤:·代表當(dāng)前目錄,··代表上層目錄#error預(yù)處理指令的作用是:編譯程序時,只要遇到#error就會生成一個編譯錯誤提示消息,并停止編譯。其語法格式為:#errorerror-message#line預(yù)處理:其作用是改變當(dāng)前的行數(shù)和文件名稱,他們是在編譯程序中預(yù)先定義的標(biāo)識符的形式為:#linenumber[“filename〞],其中[]內(nèi)的文件名可以省略,具體參看P68#pragma預(yù)處理:該指令最為復(fù)雜,其作用是設(shè)定編譯器的狀態(tài)或者是指示編譯器完成一些特定的動作。#pragma指令對每個編譯器給出了一個方法。在保持與C、C++語言完全兼容的情況下,給出主機(jī)或操作系統(tǒng)專有的特性。一句定義,編譯指示是主機(jī)或者操作系統(tǒng)專有的,且對于每個編譯器都是不同的。一般形式:#pragmapara,其中para為參數(shù)#pragmamessage:參數(shù)message能夠在編譯信息輸出窗口中輸出相應(yīng)的信息,這對于源代碼信息的控制是非常重要的。詳見P68,一看即明白。#pragmacode_seg:格式為:#pragmacode_seg〔[“section-name〞[,“section-class〞]]〕他可以設(shè)置程序中函數(shù)代碼存放的代碼段,當(dāng)我們開發(fā)驅(qū)動程序時就會使用到。#pragmaonce:只要在頭文件中參加這條指令就能夠保證頭文件被編譯一次。#pragmahdrstop:表示預(yù)編譯頭文件到此為止,后面的文件不進(jìn)行預(yù)編譯。注:具體原因參看P69.#pragmaresource:#pragmaresource“*.dmf〞表示把*.dmf文件中的資源參加工程。*.dmf中包括窗體外觀的定義。#pargmawarning:參看P69#pragmacomment:參看P70#pragmapack:關(guān)于內(nèi)存對齊的問題p71頁有詳細(xì)的講解,沒事看看例:structstudent{Charc1;Shorts;Charc2;Inti;}Sizeof〔student〕=12;內(nèi)存對齊不僅要考慮超過4個字節(jié)就要重新開始,還要考慮其實地址要為偶數(shù),而不是奇數(shù)。詳見P71復(fù)雜類型的默認(rèn)對其方式是它最長的成員的對其方式,不管數(shù)據(jù)的類型是什么,對其的邊界一定是按1、2、4、8、16、32、64…中的一個#pragmapack(push)//保存當(dāng)前對其方式到packingstack#pragmapack(push,n)等效于#pragmapack(push)#pragmapack(n)//n=1,2,4,8,16保存當(dāng)前對齊方式,設(shè)置按n字節(jié)對齊#pragmapack(pop)//packingstack出棧,并將對其方式設(shè)置為出棧的對齊方#運(yùn)算符:可以用于宏函數(shù)的替換局部。詳見P73,一目了然##運(yùn)算符:##就是個粘合劑,將前后兩局部粘合起來例:#defineXNAME(n)如果這樣使用宏:XNAME(8)那么會被展開成這樣:x8第四章指針和數(shù)組注:本章非常重要,需要好好看本章什么是指針?什么是數(shù)組?指針和數(shù)組之間有什么樣的關(guān)系??在32位系統(tǒng)下,不管什么樣的指針類型,其大小都為4byte??梢栽囈幌聅izeof(void*),也為4任何一種數(shù)據(jù)類型我們都可以把它當(dāng)一個模子int*p=NULL和*p=NULL有什么區(qū)別?不知道的話參看P76int*p=(int*)0x12ff7c;//將地址賦值給指針變量時必須進(jìn)行強(qiáng)制類型轉(zhuǎn)換*p=0x100;此三句代碼不懂的話參看P77*(int*)0x12ff7c=0x100;數(shù)組首元素的首地址和數(shù)組的首地址是兩碼事,具體細(xì)節(jié)參看P81頁4.3之上。編譯器并不會為數(shù)組分配一塊內(nèi)存來存儲其地址,而指針那么會,注意區(qū)別。數(shù)組的每一個元素都是沒有名字的,a[2]并不是數(shù)組的元素名Inta[5];sizeof(a)的值為sizeof(int)*5,32位系統(tǒng)下為20。sizeof(a[0])的值為sizeof(int),32位系統(tǒng)下為4。sizeof(a[5])的值在32位系統(tǒng)下為4。并沒有出錯,為什么呢?我們講過sizeof是關(guān)鍵字不是函數(shù)。函數(shù)求值是在運(yùn)行的時候,而關(guān)鍵字sizeof求值是在編譯的時候。雖然并不存在a[5]這個元素,但是這里也并沒有去真正訪問a[5],而是僅僅根據(jù)數(shù)組元素的類型來確定其值。所以這里使用a[5]并不會出錯。sizeof(&a[0])的值在32位系下為4,這很好理解。取元素a[0]的首地址。sizeof(&a)的值在32位系統(tǒng)下也為4,這也很好理解。取數(shù)組a的首地址。但是在VisualC++6.0上,這個值為20,我認(rèn)為是錯誤的。&a[0]代表的是數(shù)組首元素的首地址&a代表的是數(shù)組的首地址C語言引入一個術(shù)語----“可修改的左值〞。意思就是,出現(xiàn)在賦值符左邊的符號所代表的地址上的內(nèi)容一定是可以被修改的,也就是只能給非只讀變量賦值。a作為右值時其意義與&a[0]是一樣的,代表的是數(shù)組首元素的首地址,而不是數(shù)組的首地址。我們可以把a(bǔ)[i]當(dāng)左值,而無法把a(bǔ)當(dāng)左值。其實我們完全可以把a(bǔ)當(dāng)一個普通的變量來看,只不過這個變量內(nèi)局部為很多小塊,我們只能通過分別訪問這些小塊來到達(dá)訪問整個變量a的目的。指針和數(shù)組之間其實沒有任何關(guān)系指針就是指針,指針變量在32位系統(tǒng)下,永遠(yuǎn)占4個byte,其值為某一個內(nèi)存的地址,指針可以指向任何地方,但是不是任何地方你都能通過這個指針變量訪問到。數(shù)組就是數(shù)組,其大小與元素的類型和個數(shù)有關(guān)。定義數(shù)組時必須指定其元素的類型和個數(shù)。數(shù)組可以存任何類型的數(shù)據(jù),但不能存函數(shù)。以指針的形式p[4]和以下標(biāo)的形式*(p+4)訪問指針沒有區(qū)別,只是寫法上不同罷了。以指針的形式*(a+4)和以下標(biāo)的形式a[4]訪問數(shù)組:1〕,以指針的形式:*(a+4)。a這時候代表的是數(shù)組首元素的首地址,假設(shè)為0x0000FF00,然后加上4個字符的偏移量,得到新的地址0x0000FF04。然后取出0x0000FF04地址上的值。2〕,以下標(biāo)的形式:a[4]。編譯器總是把以下標(biāo)的形式的操作解析為以指針的形式的操作。a[4]這個操作會被解析成:a作為數(shù)組首元素的首地址,然后加上中括號中4個元素的偏移量,計算出新的地址,然后從新的地址中取出值。注:偏移量的單位是元素的個數(shù)。由上面的分析可知,指針和數(shù)組根本就是兩個完全不一樣的東西。只是它們都可以“以指針形式〞和“以下標(biāo)形式〞進(jìn)行訪問。一個是完全匿名的訪問,一個是典型的具名+匿名訪問。一定要注意的是這個“XXX的形式的訪問〞這種表達(dá)方式。例:main(){Inta[5]={1,2,3,4,5};Int*ptr=(int*)(&a+1);printf("%d,%d",*(a+1),*(ptr-1));}對指針進(jìn)行加1操作,得到的是下一個元素的地址,而不是原有地址值直接加1。一個類型為T的指針的移動,以sizeof(T)為移動單位的。&a+1:取數(shù)組a的首地址,該地址的值加上sizeof(a)的值,即&a+5*sizeof(int),也就是下一個數(shù)組的首地址,顯然當(dāng)前指針已經(jīng)越過了數(shù)組的界限。(int*)(&a+1):那么是把上一步計算出來的地址,強(qiáng)制轉(zhuǎn)換為int*類型,賦值給ptr。*(a+1):a,&a的值是一樣的,但意思不一樣,a是數(shù)組首元素的首地址,也就是a[0]的地址,而&a是數(shù)組的首地址,a+1是數(shù)組下一元素的首地址,即a[1]的地址,&a+1是下一個數(shù)組的首地址。所以輸出2*(ptr-1):因為ptr是指向a[5],并且ptr是int*類型,所以*(ptr-1)是指向a[4],輸出5。我們把文件1中定義的數(shù)組在文件2中聲明為指針會發(fā)生錯誤。同樣的,如果在文件1中定義為指針,而在文件中聲明為數(shù)組也會發(fā)生錯誤。注:P86頁有一張關(guān)于指針和數(shù)組的表格,記得看。指針數(shù)組與數(shù)組指針:指針數(shù)組:首先它是一個數(shù)組,數(shù)組的元素都是指針,數(shù)組占多少個字節(jié)由數(shù)組本身決定。它是“存儲指針的數(shù)組〞的簡稱。數(shù)組指針:首先它是一個指針,它指向一個數(shù)組。在32位系統(tǒng)下永遠(yuǎn)是占4個字節(jié),至于它指向的數(shù)組占多少字節(jié),不知道。它是“指向數(shù)組的指針〞的簡稱。例:A),int*p1[10];指針數(shù)組B),int(*p2)[10];數(shù)組指針另:[]的優(yōu)先級比*的優(yōu)先級高在C語言里,賦值符號“=〞號兩邊的數(shù)據(jù)類型必須是相同的,如果不同需要顯示或隱式的類型轉(zhuǎn)換。例:Intmain(){Chara[5]={'A','B','C','D'};Char(*p3)[5]=&a;//對Char(*p4)[5]=a;//錯,等號兩邊的數(shù)據(jù)類型不同,編譯時會彈出警告return0;}注:P89頁有個關(guān)于此的研究問題,在軟件上跑一下,看看p3+1和p4+1的結(jié)果個是什么。P3+1的結(jié)果是加5個char型,假設(shè)為Char(*p3)[10]=&a;那么是加10個char型。P91頁上面有一個經(jīng)典問題,如下:要直到結(jié)果的由來#include<stdio.h>intmain(){inta[4]={1,2,3,4};int*ptr1=(int*)(&a+1);int*ptr2=(int*)((int)a+1);printf("%x,%x",ptr1[-1],*ptr2);return0;}>>>>>>>>>>>>>>>>>4,2000000此結(jié)果為小端字序的結(jié)果上例之注釋:小端字序的情況:00000001000000000000000000000000a00000010000000000000000000000000a0x200000000000010000000000000000000000000a大端字序的情況:0000000000000000000000000000000100000000000000000000000000000010a0x10000000000000000000000000100000000a[0]的后三段數(shù)字與a[1]的第一段數(shù)字相接,得到0x100注:要想清楚的弄懂,需要先了解數(shù)據(jù)以大端字序和小端字序的存放情況把以上數(shù)據(jù)再寫一次,以便理解。如下:00000001000000000000000000000000000000100000000000000000000000000x20000000000001000000000000000000000000000000000000000000000000000000001000000000000000000000000000000100x10000000000000000000000000100000000多維數(shù)組與多級指針:編譯器總是將二維數(shù)組看成是一個一維數(shù)組,而一維數(shù)組的每一個元素又都是一個數(shù)組例:#include<stdio.h>Intmain(intargc,char*argv[]){Inta[3][2]={(0,1),(2,3),(4,5)};int*p;p=a[0];printf("%d",p[0]);}>>>>>>>>>>>>>>>>>>結(jié)果是1不是0很多人都覺得這太簡單了,很快就能把答案告訴我:0。不過很可惜,錯了。答案應(yīng)該是1。如果你也認(rèn)為是0,那你實在應(yīng)該好好看看這個題?;ɡㄌ柪锩媲短椎氖切±ㄌ枺皇腔ɡㄌ?!這里是花括號里面嵌套了逗號表達(dá)式!其實這個賦值就相當(dāng)于inta[3][2]={1,3,5};所以,在初始化二維數(shù)組的時候一定要注意,別不小心把應(yīng)該用的花括號寫成小括號了。例:Inta[5][5];Int(*p)[4];p=a;問&p[4][2]-&a[4][2]的值為多少?#include<stdio.h>intmain(){inta[5][5];int(*p)[4]; p=a;printf("a_ptr=%#p,p_ptr=%#p\n",&a[4][2],&p[4][2]);printf("%p,%d\n",&p[4][2]-&a[4][2],&p[4][2]-&a[4][2]);return0;}>>>>>>>>>>>>>>>>a_ptr=0xbf97db40,p_ptr=0xbf97db300xfffffffc,-4當(dāng)數(shù)組名a作為右值時,代表的是數(shù)組首元素的首地址。根據(jù)定義,p是指向一個包含4個元素的數(shù)組的指針。也就是說p+1表示的是指針p向后移動了一個“包含4個int類型元素的數(shù)組〞。這里最重要的一點就是明白數(shù)組指針p所指向的內(nèi)存到底是什么。解決這類問題的最好方法就是畫內(nèi)存布局圖〔條形圖,尺子〕。二級指針〔**p〕:char**p;定義了一個二級指針變量p。p是一個指針變量,毫無疑問在32位系統(tǒng)下占4個byte。它與一級指針不同的是,一級指針保存的是數(shù)據(jù)的地址,二級指針保存的是一級指針的地址。任何指針變量都可以被初始化為NULL,二級指針也不例外。要取出二級指針?biāo)嬲赶虻臄?shù)據(jù),需要使用兩次鑰匙〔“*〞〕。數(shù)組參數(shù)與指針參數(shù):C語言中,當(dāng)一維數(shù)組作為函數(shù)參數(shù)的時候,編譯器總是把它解析成一個指向其首元素首地址的指針。同樣的,函數(shù)的返回值也不能是一個數(shù)組,而只能是一個指針。函數(shù)本身是沒有類型的,只有函數(shù)的返回值才有類型。main函數(shù)內(nèi)的變量不是全局變量,而是局部變量,只不過它的生命周期和全局變量一樣長而已。全局變量一定是定義在函數(shù)外部的。無法把指針變量本身傳遞給一個函數(shù),而是將此指針復(fù)制一份給了函數(shù)例:#include<stdio.h>voidGetMemory〔char*p,intnum〕{p=(char*)malloc(num*sizeof(char));}intmain(){char*str=NULL;GetMemory〔str,10〕;strcpy(str,〞hello〞);free〔str〕;//free并沒有起作用,內(nèi)存泄漏,因為str并沒有傳遞給GetMemoryreturn0;}上例問題能通過兩種方式解決:1、利用return返回p2、利用二級指針。詳見P100Voidfun〔chara[][4]〕;P101頁解釋了為什么二維數(shù)組中的第一個數(shù)字可以省略而第二個數(shù)字卻不能省略注意:C語言中,當(dāng)一維數(shù)組作為函數(shù)參數(shù)的時候,編譯器總是把它解析成一個指向其首元素首地址的指針。這條規(guī)那么并不是遞歸的,也就是說只有一維數(shù)組才是如此,當(dāng)數(shù)組超過一維時,將第一維改寫為指向數(shù)組首元素首地址的指針之后,后面的維再也不可改寫。比方:a[3][4][5]作為參數(shù)時可以被改寫為〔*p〕[4][5]。函數(shù)指針〔比擬少見,P101〕:定義:顧名思義,函數(shù)指針就是函數(shù)的指針。它是一個指針,指向一個函數(shù)。char*(*fun1)(char*p1,char*p2);這里fun1不是什么函數(shù)名,而是一個指針變量,它指向一個函數(shù)。這個函數(shù)有兩個指針類型的參數(shù),函數(shù)的返回值也是一個指針。同樣,我們把這個表達(dá)式改寫一下:char*(*)(char*p1,char*p2)fun1;這樣子是不是好看一些呢?只可惜編譯器不這么想。例:#include<stdio.h>voidFunction(){printf("Call--Function!\n");}intmain(){void(*p)(); *(int*)&p=(int)Function; (*p)();return0;}>>>>>>>>>>>Call--Function!注釋:void(*p)();這行代碼定義了一個指針變量p,p指向一個函數(shù),這個函數(shù)的參數(shù)和返回值是void。&p是求指針變量p本身的地址,這是一個32位的二進(jìn)制常數(shù)〔32位系統(tǒng)〕。(int*)&p表示將地址強(qiáng)制轉(zhuǎn)換成指向int類型數(shù)據(jù)的指針。(int)Function表示將函數(shù)的入口地址強(qiáng)制轉(zhuǎn)換成int類型的數(shù)據(jù)。*(int*)&p=(int)Function;表示將函數(shù)的入口地址賦值給指針變量p。(*p)();就是表示對函數(shù)的調(diào)用。其實函數(shù)指針與普通指針沒什么差異,只是指向的內(nèi)容不同而已。使用函數(shù)指針的好處在于,可以將實現(xiàn)同一功能的多個模塊統(tǒng)一起來標(biāo)識,這樣一來更容易后期的維護(hù),系統(tǒng)結(jié)構(gòu)更加清晰。或者歸納為:便于分層設(shè)計、利于系統(tǒng)抽象、降低耦合度以及使接口與實現(xiàn)分開。(*(void(*)())0)();逐層剝離,理解其意第一步:void(*)(),可以明白這是一個函數(shù)指針類型。這個函數(shù)沒有參數(shù),沒有返回值。第二步:(void(*)())0,這是將0強(qiáng)制轉(zhuǎn)換為函數(shù)指針類型,0是一個地址,也就是說一個函數(shù)存在首地址為0的一段區(qū)域內(nèi)。第三步:(*(void(*)())0),這是取0地址開始的一段內(nèi)存里面的內(nèi)容,其內(nèi)容就是保存在首地址為0的一段區(qū)域內(nèi)的函數(shù)。第四步:(*(void(*)())0)(),這是函數(shù)調(diào)用。Ps:(*(char**(*)(char**,char**))0)(char**,char**);什么意思? 函數(shù)指針數(shù)組→→P104例:#include<stdio.h>#include<string.h>char*fun1(char*p){printf("%s",p);returnp;}char*fun2(char*p){printf("%s",p);returnp;}char*fun3(char*p){printf("%s",p);returnp;}intmain(){char*(*pf[3])(char*p); pf[0]=fun1;//可以直接用函數(shù)名 pf[1]=&fun2;//可以用函數(shù)名加上取地址符 pf[2]=&fun3; pf[0]("fun1"); pf[0]("fun2"); pf[0]("fun3");return0;}>>>>>>>>>>fun1fun2fun3<<<<<<<<<<<<<函數(shù)指針數(shù)組的指針→→P105函數(shù)指針數(shù)組指針不就是一個指針嘛。只不過這個指針指向一個數(shù)組,這個數(shù)組里面存的都是指向函數(shù)的指針,僅此而已。char*(*(*pf)[3])(char*p);注意,這里的pf和上一節(jié)的pf就完全是兩碼事了。上一節(jié)的pf并非指針,而是一個數(shù)組名,這里的pf確實是實實在在的指針。這個指針指向一個包含了3個元素的數(shù)組;這個數(shù)字里面存的是指向函數(shù)的指針;這些指針指向一些返回值類型為指向字符的指針、參數(shù)為一個指向字符的指針的函數(shù)。這比上一節(jié)的函數(shù)指針數(shù)組更拗口。其實你不用管這么多,明白這是一個指針就ok了。其用法與前面講的數(shù)組指針沒有差異。下面列一個簡單的例子。例:#include<stdio.h>#include<string.h>char*fun1(char*p){printf("%s",p);returnp;}char*fun2(char*p){printf("%s",p);returnp;}char*fun3(char*p){printf("%s",p);returnp;}intmain(){char*(*a[3])(char*p);char*(*(*pf)[3])(char*p); pf=&a; a[0]=fun1; a[1]=&fun2; a[2]=&fun3; pf[0][0]("fun1"); pf[0][1]("fun2"); pf[0][2]("fun3");return0;}>>>>>>>>>>>>>>>>fun1fun2fun3<<<<<<<<<<<<<<<<<<<第五章內(nèi)存管理關(guān)于野指針的問題:定義指針變量的同時最好初始化為NULL,用完指針之后也將指針變量的值設(shè)置為NULL。也就是說除了在使用時,別的時間都把指針“栓〞到0地址處。這樣它就老實了。一般來說,我們可以簡單的理解為內(nèi)存分為三個局部:靜態(tài)區(qū),棧,堆。靜態(tài)區(qū):保存自動全局變量和static變量〔包括static全局和局部變量〕。靜態(tài)區(qū)的內(nèi)容在整個程序的生命周期內(nèi)都存在,由編譯器在編譯的時候分配。棧:保存局部變量。棧上的內(nèi)容只在函數(shù)的范圍內(nèi)存在,當(dāng)函數(shù)運(yùn)行結(jié)束,這些內(nèi)容也會自動被銷毀。其特點是效率高,但空間大小有限。堆:由malloc系列函數(shù)或new操作符分配的內(nèi)存。其生命周期由free或delete決定。在沒有釋放之前一直存在,直到程序結(jié)束。其特點是使用靈活,空間比擬大,但容易出錯。常見的內(nèi)存錯誤及對策:1.指針沒有指向一塊合法的內(nèi)存。a.結(jié)構(gòu)體成員指針未初始化structstudent{char*name;intscore;}stu,*pstu;intmain(){strcpy(,"N.Yang"); stu.score=99;return0;}這里定義了結(jié)構(gòu)體變量stu,但是他沒想到這個結(jié)構(gòu)體內(nèi)部char*name這成員在定義結(jié)構(gòu)體變量stu時,只是給name這個指針變量本身分配了4個字節(jié)。name指針并沒有指向一個合法的地址,這時候其內(nèi)部存的只是一些亂碼。所以在調(diào)用strcpy函數(shù)時,會將字符串"N.Yang"往亂碼所指的內(nèi)存上拷貝,而這塊內(nèi)存name指針根本就無權(quán)訪問,導(dǎo)致出錯。解決的方法是為name指針malloc一塊空間。structstudent{char*name;intscore;}stu,*pstu;intmain(){ pstu=(structstudent*)malloc(sizeof(structstudent));strcpy(pstu->name,"N.yang"); pstu->score=99;free(pstu);return0;}本例為指針變量pstu分配了內(nèi)存,但是同樣沒有給name指針分配內(nèi)存。錯誤與上面第一種情況一樣,解決的方法也一樣。這里用了一個malloc給人一種錯覺,以為也給name指針分配了內(nèi)存。Pstu分配了內(nèi)存并不代表name也分配到了內(nèi)存。b.沒有為結(jié)構(gòu)體指針分配足夠的內(nèi)存例:intmain(){ pstu=(structstudent*)malloc(sizeof(structstudent*));//此處有問題strcpy(pstu->name,"n.yang"); pstu->score=99;free(pstu);return0;}為pstu分配內(nèi)存的時候,分配的內(nèi)存大小不適宜。這里把sizeof(structstudent)誤寫為sizeof(structstudent*)。當(dāng)然name指針同樣沒有被分配內(nèi)存。解決方法同上。c.函數(shù)的入口校驗不管什么時候,我們使用指針之前一定要確保指針是有效的。一般在函數(shù)入口處使用assert(NULL!=p)對參數(shù)進(jìn)行校驗。具體用法及assert函數(shù)的定義詳見→→P1102.為指針分配的內(nèi)存太小不要因為char類型大小為1個byte就省略sizof〔char〕這種寫法。這樣只會使你的代碼可移植性下降。只有字符串常量才有結(jié)束標(biāo)志符。該處的例子為拷貝字符串時用strlen來申請空間,忽略了字符串后面的休止符/03.內(nèi)存分配成功但是并沒有初始化犯這個錯誤往往是由于沒有初始化的概念或者是以為內(nèi)存分配好之后其值自然為0。記得要初始化

溫馨提示

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

評論

0/150

提交評論