語言的數據結構教學課件_第1頁
語言的數據結構教學課件_第2頁
語言的數據結構教學課件_第3頁
語言的數據結構教學課件_第4頁
語言的數據結構教學課件_第5頁
已閱讀5頁,還剩135頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

機器語言計算1+1101110000000000100000000000001010000000100000000匯編語言MOVAX,1101110000000000100000000ADDAX,1000001010000000100000000BASIC語言PRINT1+1C語言#include<stdio.h>main(){ printf("%d\n",1+1);}C語言的創世紀一切從一個叫“SpaceTravel”的電子游戲開始……為了讓他的游戲能在PDP-7上運行,KenThompson用匯編語言給PDP-7寫了一個操作系統——UNIX匯編太不好用了,Thompson需要高級語言試驗了一些高級語言,包括Fortran,都不理想他在BCPL基礎上,自己設計了一個B語言UNIX開始發展,B也不夠用了DennisRitchie加入,把B改造成C開始用C重寫UNIXRitchie和Thompson在開發UNIX接受美國國家技術勛章C程序設計語言是一種高級語言高級語言并不是“高級”,只是相對低級語言,在一個高的級別上進行編程歷史悠久,戰勛卓著誕生于上世紀70年代初,成熟于80年代(C89),修訂與90年代(C99)很多重量級軟件都是用C寫的上天入地,無所不能幾乎沒有不能用C寫出來的軟件,沒有不支持C的系統很多流行語言、新生語言都借鑒了它的思想、語法從C++,到Java,再到C#,還有php等C語言的祖師爺

DennisM.Ritchiecs.bell-labs/who/dmr//Ritchie漫畫像Cisquirky,flawed,andanenormoussuccess.計算機基本工作過程整個過程的執行者是硬件,但硬件是受軟件控制的編程,就是編寫軟件,使硬件按照人的意圖工作編譯運行編譯過程程序員(Programmer)編寫程序源代碼(SourceCode)編譯器(Compiler)把源代碼轉換為可被計算機理解的機器代碼(MachineCode),并把機器代碼以可執行文件(ExecutableFile)的形式保存在磁盤上軟件的運行計算機把機器代碼讀入到內存(Memory),由CPU運行這些代碼,讀取輸入(Input),產生輸出(Output),完成程序員預定的功能編譯語言一種編譯語言對應一種編譯器程序員按照該語言的語法編寫程序源代碼,把自己的意圖融入到代碼中編譯器讀入源代碼,把程序員的意圖轉換成可執行程序,供他人使用C語言可執行程序編譯器解釋運行解釋運行過程程序員編寫程序源代碼解釋器讀入源代碼,并執行源代碼解釋運行的語言特點執行速度慢好學易用先編譯、后解釋把源代碼編譯成更容易解釋的中間代碼,然后再解釋運行C程序設計語言第1章觀其大略Hello,World#include<stdio.h>main(){ printf("hello,world\n");}超級無敵考考你:

如何把“hello”和“world”分別打印在兩行?hello.c打印華氏溫度與攝氏溫度對照表計算公式:

C=(5/9)(F-32)打印華氏溫度與攝氏溫度對照表#include<stdio.h>/*對fahr=0,20,...,300

打印華氏溫度與攝氏溫度對照表*/main(){

int

fahr,celsius;

int

lower,upper,step;

lower=0;/*溫度表的下限*/

upper=300;

/*溫度表的上限*/

step=20;/*步長*/

fahr

=lower;

while(fahr<=upper){

celsius=5*(fahr-32)/9;

printf("%d\t%d\n",fahr,celsius);

fahr=fahr+step;

}}fc1.c代碼風格#include<stdio.h>/*對fahr=0,20,...,300

打印華氏溫度與攝氏溫度對照表*/main(){

int

fahr,celsius;

int

lower,upper,step;

lower=0;/*溫度表的下限*/

upper=300;

/*溫度表的上限*/

step=20;/*步長*/

fahr

=lower;

while(fahr<=upper){

celsius=5*(fahr-32)/9;

printf("%d\t%d\n",fahr,celsius);

fahr=fahr+step;

}}fc1.c更簡單、精確的對照表打印程序#include<stdio.h>#defineLOWER0/*表的下限*/#defineUPPER300/*表的上限*/#defineSTEP20/*步長*//*打印華氏-攝氏溫度對照表*/main(){

intfahr;

for(fahr=LOWER;fahr<=UPPER;fahr=fahr+STEP) printf("%3d#%6.1f\n", fahr, (5.0/9.0)*(fahr-32));}fc2.c字符輸入輸出c=getchar()從鍵盤讀入一個字符,賦值給變量cputchar(c)把c輸出到屏幕拷貝的基本思想:

讀一個字符

while(該字符不是文件結束指示符)

輸出剛讀進的字符

讀下一個字符拷貝(Copy)#include<stdio.h>/*用于將輸入復制到輸出的程序;第1個版本*/main(){

intc;

c=getchar();

while(c!=EOF){ putchar(c); c=getchar(); }}copy1.c一個更好的版本#include<stdio.h>/*用于將輸入復制到輸出的程序;第2個版本*/main(){

intc; while((c=getchar())!=EOF) putchar(c);}copy2.c計算行數#include<stdio.h>/*統計輸入的行數*/main(){ intc;

longnl; nl=0;

while((c=getchar())!=EOF) {

if(c=='\n') nl++; } printf("%d\n",nl);}counter.c加法器#include<stdio.h>/*計算輸入的兩個整數的和*/main(){

inta,b; printf("Pleaseinputtwointegers:"); scanf("%d%d",&a,&b); printf("Sum=%d\n",a+b);}add.c平均分#include<stdio.h>/*計算某科成績的平均值*/#defineTOTAL_NUMBER10/*總人數*/main(){

floatsum=0,score[TOTAL_NUMBER];

inti; printf("Input%dscores:\n",TOTAL_NUMBER);

for(i=0;i<TOTAL_NUMBER;i++) { scanf("%f",&score[i]); sum=sum+score[i]; } printf("Average=%f\n",sum/TOTAL_NUMBER);}average.c函數(Function)前面使用了系統提供的函數:printf,scanf,getchar,putchar使用函數時,我們不用知道這個函數內部是如何運作的,只按照我們的需要和它的參數形式調用它即可我們也可以定義自己的函數“一個程序應該是輕靈自由的,它的函數就象串在一根線上的珍珠。”(《編程之道》)power函數/*power:求底的n次冪;n>=0*/intpower(intbase,intn){ inti,p;

p=1;

for(i=1;i<=n;++i) p=p*base;

returnp;}power.cpower函數的調用(Call)#include<stdio.h>intpower(intbase,intn);/*測試power函數*/main(){

intm,n; m=power(2,1); n=power(-3,3); printf("%d%d\n",m,n);

return0;}power.c這一章我們學到了#include<stdio.h>#definemain()printf(),scanf()getchar(),putchar()<=,>=,==,!=int,long,float數組while,for,if代碼風格注釋、縮進、空行、命名……函數C程序設計語言第2章類型、運算符與表達式標識符(Identifiers)用戶自定義的符號叫標識符如變量名、函數名、宏和類型名標識符由字母、數字和下劃線組成,大小寫敏感不可以是數字開頭標識符要直觀,能表達它的功能下劃線和大小寫通常用來增強可讀性variablenamevariable_name,VARIABLE_NAMEVariableName,variableName關鍵字(keyword)不可作為標識符int,float,for,while,if等(教材164頁)某些功能的變量采用習慣命名如:for語句所采用的循環變量習慣用i,j,k基本數據類型(DataType)int整數,在目前絕大多數機器上占4個字節TC2中是2個字節所占字節數取決于機器字長float單精度浮點數,一般是4個字節長double雙精度浮點數,一般是8個字節長char字符,一般是1個字節長用來表示256個ASCII字符,或者0~255的整數數據類型修飾符shortshort

int,短整數,一般2個字節長。通常簡寫為shortlonglong

int,長整數,一般是4個字節長。通常簡寫為longlong

double,高精度浮點數,一般是10個字節長。signed用來修飾char、int、short和long,說明他們是有符號的整數(正整數、0和負整數)。一般缺省都是有符號的,所以這個修飾符通常省略unsigned用來修飾char、int、short和long,說明他們是無符號的整數(正整數和0)超出取值范圍會怎樣?TC2中int的范圍是-32767~32767如果我們給它一個小于-32767或者大于32767的數會如何呢?現場編程測驗……小蛇能吞下大象嗎?溢出(Overflow)造成的危害一臺安裝了Windows95/98的機器,如果連續運行49.7天沒有重新啟動,可能死機原因:Windows自啟動時刻起,有一個計數器,記錄系統已經運行了多少毫秒。這個計數器是個unsigned

long類型的變量unsigned

long的最大值是:4294967295一天有24*60*60*1000=86400000毫秒4294967295/86400000=49.71026961805……當49.7天的時候,此計數器會溢出,引起死機浮點數的陷阱#include<stdio.h>main(){

floatf; f=123.456;

if(f==123.456) printf("fisequalto123.456indeed.");

else printf("Infact,fisequalto%f\n",f);}運行結果會是什么?float.c浮點數的陷阱float的精度低,較易發生精度帶來的相等性判斷問題double精度高,這個問題發生的概率小一些,但也存在解決辦法:

if(fabs(f–123.456)<1E-5)

……根據精度要求設定使用變量要注意不要對變量所占的字節數想當然用sizeof獲得變量或者數據類型的長度用ANSIC定義的宏確定數據的表示范圍,解決溢出問題sizeof.c常數(Constant)整型常數123、456123456123l、123L、123456l、123456L浮點常數123.45、456.781e-2、4.5e3123.45f、456.78F、1e-2f、4.5e3F123.45l、456.78L、1e-2l、4.5e3L八進制與十六進制常數以數字“0”開始的整型常數是八進制數010和10大小不一樣因為八進制并不常用,所以此種表示法比較少見,因而常被用錯以“0x”或者“0X”開始的整型常數是十六進制A~F和a~f用來表示十進制的10~150x11,0x05,0xFA,0xFF十六進制的形式比較常用,尤其在進行位一級的控制的時候字符常數字符常數的表示方法'a','A','5','%','$'……單引號內只能有一個字符,除非用“\”開頭就是一個普通整數,也可以參與各種數學運算每個字符具有一個0~255之間的數值,可從ASCII表查出注意:'5'和5的區別,A和'A'的區別字符的數學運算在密碼學內用得比較多ascii.c字符常數轉義字符一些特殊字符(無法從鍵盤輸入或者在C語言里有它用)用轉義字符表示轉義的思想在網絡協議和文件格式中經常使用字符串(String)常數用雙引號括住的由0個或多個字符組成的字符序列"Iamastring"""表示空字符串轉義字符也可以在字符串中使用引號只作為字符串開始和結束的標志C語言內部用'\0'表示字符串的結束除注釋外,是唯一可以出現中文的地方"x"和'x'是不同的<string.h>里定義了一系列專門的字符串處理函數枚舉(Enumeration)常數一個幾乎被遺忘的角色從程序來窺其一斑

enumweeks{MON,TUE,WED,THU,FRI,SAT,SUN};

enumweekstoday,tomorrow;

today=MON;tomorrow=today+1;

if(tomorrow==TUE)printf("TomorrowisTuesday.\n");

elseprintf("TomorrowisNOTTuesday.\n");enum.c變量聲明變量必須“先定義,后使用”所有變量必須在第一條可執行語句前定義聲明的順序無關緊要一條聲明語句可聲明若干個同類型的變量,變量名之間用逗號分隔變量定義后,即占用內存,可向其存入各種數據,并可通過變量名使用數據聲明變量,是初始化變量的最好時機不被初始化的變量,其值為危險的隨機數 charesc='\\'; inti=0; intlimit=MAXLINE+1; floateps=1.0e-5;常量用const修飾定義的變量為常量const

inti=0;常量只能在定義時賦值,然后不能再改變其值常數、常量、宏和枚舉,都可以用來表示一個永遠不會改變的數前者不建議直接使用,而用后三者代替后三者的工作機理是完全不同的,達到的效果也不盡相同計算機只會計算任何事物都要被表示成數字和公式的形式后,才能被計算機計算(被計算機處理)事物到數字和公式的轉換過程叫數學建模因為:事物在計算機內的處理都是一種計算又因為:計算就要有操作數、運算法則和計算結果所以:事物在計算機內的處理都有操作數、運算法則和計算結果計算結果你可以留用,也可以忽略算術運算符+,-,*,/加、減、乘、除運算四則混合運算中,先算乘除,后算加減,

先算左,后算右%求余運算C語言中的運算關系運算符>,>=,<,<=,==,!=大于,大于等于,小于,小于等于,等于,不等于關系運算符運算出的結果為0和10,表示假,即該關系不成立1,表示真,即該關系成立在所有涉及到真假判斷的地方,0表示假,非0表示真找別扭inta=1;

if(a==0)

printf("OK");inta=0;

if(a==0)

printf("OK");inta=1;

if(a=0)

printf("OK");inta=0;

if(a=0)

printf("OK");==和=inta;

a=0;

a==1;inta;

a==0;

a=1;一定要分清==和=下面用法能起點小作用:inta=0;

if(0==a)

printf("OK");inta=0;

if(0=a)

printf("OK");編譯出錯邏輯運算符邏輯運算也被稱為布爾(Boolean)運算,運算結果也是1和0&&與運算(a>b&&b>c);a大于b,并且b大于c||或運算(a>b||b>c);a大于b,或者b大于c!求反(!a);如果a是0,結果非0;如果a是非0,結果是0并不改變a的值類型轉換在進行賦值操作時,會發生類型轉換將取值范圍小的類型轉為取值范圍大的類型是安全的反之是不安全的如果大類型的值在小類型能容納的范圍之內,則平安無事但是,浮點數轉為整數,會丟失小數部分(非四舍五入)反之,轉換后的結果必然是錯誤的,具體結果與機器和實現方式有關。避免如此使用字符串與數值類型之間的轉換inti="123"這樣用是不行地atof(),atoi(),atol()把字符串轉為double,int和long定義在stdlib.h中sprintf()可以用來把各種類型的數值轉為字符串定義在stdio.h中自動類型轉換兩個同種數據類型的運算結果,還是該類型兩個不同種數據類型的運算結果,是兩種類型中取值范圍更大的那種long

double>double>float>long>int>short>char只要兩者中有一個是unsigned,就都轉為unsigned再計算把數據賦值給另外一種類型變量也會發生自動類型轉換從小到大,順利轉換從大到小,發出警告(好的編譯器會給出)類型強轉可以通過“(類型)表達式”的方式把表達式的值轉為任意類型強轉時,你必須知道你在做什么強轉與指針,并稱C語言兩大神器,用好了可以呼風喚雨,用壞了就損兵折將屠龍

刀倚天劍加一和減一運算符i++,i--,++i,--i++讓參與運算的變量加1,--讓參與運算的變量減1運算符為后綴,先取i的值,然后加/減1運算符為前綴,先加/減1,然后取i的值在一行語句中,使用加1或者減1運算的變量只能出現不僅可讀性差,而且因為編譯器實現的方法不同,容易導致不同編譯器運行效果不一樣,貽害無窮位操作運算符&按位與運算|按位或運算^按位異或運算<<按位左移運算>>按位右移運算~按位求反賦值運算符賦值運算的結果是被賦值變量賦值后的值a=b=c=0;下面兩個語句是等價的i=i+2;i+=2;+、-、*、/、%、<<、>>、&、^、|運算符都可以按此種方式處理這種形式看起來更直觀,而且執行效率一般也能更高一些條件表達式把a和b中的最大值放入z中if(a>b)

z=a;

else

z=b;

z=(a>b)?a:b;

此種表達式切忌用得過于繁雜優先級()[]->.!~++--+-*&(類型)sizeof*/%+-<<>><<=>>===!=&^|&&||?:=+=-=*=/=%=&=^=|=<<=>>=,優先級能背下優先級表的人鳳毛麟角腦細胞太寶貴了,不能用來死記硬背用括號來控制運算順序更直觀、方便,并減少出錯的概率先算乘除,后算加減,有括號就先算括號里的括號太多,有時候不清晰注意用空格做好分隔實在不行就拆分表達式C程序設計語言第3章控制流三種基本結構順序結構、選擇結構、循環結構已經證明,任何程序均可只用這三種結構實現B?hm,Corrado,andJacopiniGuiseppe.

"Flowdiagrams,Turingmachinesandlanguageswithonlytwoformationrules."

CommunicationofACM,9(5):366-371,May1966.只用這三種結構的程序,叫結構化程序程序“必須”符合結構化規則流程圖順序結構選擇結構truefalsetruefalse循環結構語句塊(Block){}括住的若干條語句構成一個語句塊語句塊內可以定義變量變量必須在語句塊的開頭定義變量僅在定義它的語句塊內(包括下層語句塊)有效(scope.c)同一個語句塊內的變量不可同名,不同語句塊可以同名(homonym.c)各司其職、下層優先盡量不要在下層語句塊內定義變量,也盡量不要定義同名變量語句塊可以用在任何可以使用語句的地方,但沒有道理要亂加語句塊if-else選擇結構的一種最常用形式if(表達式)

語句塊1;

else

語句塊2;

語句塊3表達式值非0時,執行語句塊1,然后語句塊3;

表達式值為0時,執行語句塊2,然后語句塊3else部分可以沒有。當表達式值為0時,直接執行語句3if-else嵌套使用時,注意else和誰配套的問題if.c表達式!=0?YN語句塊1語句塊2語句塊3else-ifif的一種擴展if(表達式1)

語句塊1;

elseif(表達式2)

語句塊2;

elseif(表達式3)

語句塊3;

…………

else

語句塊4;

語句塊5;else部分可以沒有表達式1!=0?YN語句塊1語句塊2語句塊5表達式2!=0?表達式3!=0?語句塊3N語句塊4NYYswitch多路選擇switch(表達式){

case

整型常數1:

語句1;

case

整型常數2:

語句2;

…………

default:

語句3;

}default可以沒有現場編程完成計算器……不要忘記breakswitch和else-if的比較else-if比switch的條件控制更強大一些else-if可以依照各種邏輯運算的結果進行流程控制switch只能進行==判斷,并且只能是整數判斷switch比else-if更清晰兩者都要盡量避免用得過多、過長,尤其不要嵌套得太多它們大大增加程序的分支,使邏輯關系顯得混亂,不易維護,易出錯循環——while,forwhile(表達式)

語句塊;for(表達式1;表達式2;表達式3)

語句塊;whilewhile(表達式)

語句塊1;

語句塊2;只要表達式的值為非0,就重復執行語句塊1,直到表達式值為0時止,開始執行語句塊2表達式!=0?YN語句塊1語句塊2forfor(表達式1;表達式2;表達式3)

語句塊;首先執行表達式1。如果表達式2的值為非0,就重復執行語句塊和表達式3,直到表達式2的值為0時止相當于:

表達式1;

while(表達式2){

語句塊;

表達式3;

}for的所有表達式均可省略表達式2!=0?YN語句塊表達式3表達式1注意在for和while語句之后一般沒有分號有分號表示循環體就是分號之前的內容,即循環體不存在while(i<100);

i++;for(i=0;i<100;i++);

printf("%d",i);for通常有一個循環變量控制循環的次數,不要在循環體內改變這個變量循環——do-whiledo

語句塊1;

while(表達式);

語句塊2;首先執行語句,然后判斷表達式的值。如果表達式為0,繼續向下執行,否則,再次執行語句,再次判斷表達式的值語句塊1會被執行至少一次表達式!=0?YN語句塊1語句塊2選擇三種循環的一般思路如果循環次數已知,用for如果循環次數未知,用while如果循環體至少要執行一次,用do-while只是思路,不是定律break和continue對for、while、do-while循環進行內部手術break,退出循環continue,中斷此次循環的執行,開始下一次break和continue少用為妙它們增加了循環執行的分支,break更增加了循環的出口它們可以用來處理程序異常,而盡量不要用來處理正常流程C程序設計語言第4章函數與程序結構函數(function)和模塊(module)函數是C語言中模塊化編程的最小單位可以把每個函數看作一個模塊若干相關的函數可以合并作一個“模塊”main()printf()scanf()power()putchar()getchar()main()stdio:printf()scanf()putchar()getchar()mymdl:power()函數的分類函數生來都是平等的,沒有高低貴賤之分,只有main()稍微特殊一點點庫函數ANSIC定義的標準庫函數符合標準的C語言編譯器必須提供這些函數函數的行為也要符合ANSIC的定義第三方庫函數由其它廠商自行開發的C語言函數庫不在標準范圍內,能擴充C語言的功能自定義函數自己編寫的函數包裝后,也可成為函數庫,供別人使用函數定義(definition)類型函數名(類型參數1,類型參數2,……)

{

函數體;

return

表達式;

}返回值類型標識符參數表返回值函數出口函數定義(definition)函數是這樣的一種運算:函數名說明運算規則參數是運算的操作數返回值是運算的結果當函數執行到return語句或}時,函數的運算停止。程序從當次調用函數的地方繼續執行函數可以有多個return,但最好只有一個且是最后一行用void定義返回值類型函數沒有運算結果,沒有返回值return語句之后不需要任何表達式用void定義參數,表示沒有參數參數表里的參數(叫形式參數,parameter)也是函數的語句塊內的變量函數調用(call)函數名(表達式1,表達式2,……);調用一個函數之前,先要對其返回值類型、函數名和參數進行聲明(declare)不對函數進行聲明是非常危險的函數定義也有聲明函數的效果調用函數時,提供的表達式(叫實際參數,argument)和該函數的形式參數必須匹配數目一致類型一一對應(會發生自動類型轉換)表達式的值賦值給對應的參數返回值可以按需處理realeql.c函數調用的過程函數的每次執行都會建立一個全新的獨立的環境在“棧”中為函數的每個變量(包括形式參數)分配內存把實際參數的值復制給形式參數開始執行函數內的第一條語句函數內的代碼在這個獨立的環境內工作函數退出時求出返回值,將其存入一個可以被調用者訪問的地方(x86中通常使用EAX寄存器)收回分配給所有變量(包括形式參數)的內存程序控制權交給調用者,調用者拿到返回值,將其作為函數調用表達式的結果main()、printf()和scanf()

特殊嗎?main()C語言允許不對函數參數和返回值類型進行說明甚至可以連函數名都不聲明此時默認該函數的參數是不定個數的int型該函數返回值為int型永遠不要利用此特性!printf()、scanf()變長參數表,<stdarg.h>缺點:對參數類型和個數無法嚴格驗證,易使用出錯使用函數要注意每個函數只完成一個功能(包括main())對函數的功能可以用不含連詞的一句話描述函數不能過長1986年IBM在OS/360的研究結果:大多數有錯誤的函數都大于500行1991年對148,000行代碼的研究表明:小于143行的函數比更長的函數更容易維護函數一定要對傳進來的非法參數做點什么向調用者提供錯誤信息assert()safediv.c全局變量(GlobalVariable)在所有函數之外定義的變量是全局變量,在定義它的位置以后都有效全局變量自動初始化為0全局變量使函數之間的數據交換更容易,效率也高一些但是不推薦使用,甚至禁止使用程序的任何部分都可以改寫全局變量,很難確定在程序的哪里改寫了它,程序結構混亂不得不用的時候(這種情況比較少見),要嚴格控制對它的改寫靜態變量(static)函數的內部變量在函數退出后失效(內存釋放)。再次進入函數,變量重新定義每次函數執行都建立一個全新的執行環境,不受其它函數的干擾把此變量定義為static,則變量的值可以保存到下次進入函數static

inti;靜態變量自動初始化為0static.c遞歸(Recursion)函數直接或間接調用自己為遞歸unsigned

intfunc(unsigned

intn)

{

if(n==0)

return1;

else

returnn*func(n-1);

}recur.c模塊模塊包含兩部分源文件(xxx.c):一系列相關函數的定義頭文件(xxx.h):這些函數的聲明等必要信息函數聲明、外部變量聲明、宏定義、類型定義……可以將模塊編譯為.obj文件,同.h文件一起供別人使用,從而保護了源代碼使用模塊的過程建立一個工程(project)把各模塊都加入到工程中#include模塊的頭文件開始使用此模塊編寫模塊的技術模塊的信息隱藏用static定義的函數和全局變量只在此模塊內有效(建議采用)允許被其它模塊使用的全局變量在源文件中定義,不加static修飾在頭文件中進行聲明,加extern修飾預編譯指令編譯器在開始正式編譯之前處理的指令,叫預編譯指令它們不會存在于最后生成的目標代碼中文件包含:#include用#include指定的文件內容替換#include所在的行用<>或者""括上文件名<>表示在編譯器的include目錄內查找文件""表示在當前目錄查找文件文件名中可以帶有路徑#define#define

宏名字

替換文本在#define之后,所有獨立出現“宏名字”的地方(除了字符串內)都被“替換文本”替換“替換文本”中可以有空格宏可以有參數#definemax(A,B)((A)>(B)?(A):(B))能想出帶參數的宏和函數的區別嗎?定義宏的時候注意替換發生后產生的非預想結果一般用括號可以避免,如上例宏名中間不要有空格與#define配套者#undef,從現在開始取消#define的定義#undefMAXLINE#if,#else,#elif,#endif#ifdef,#ifndef這些預編譯指令通常用來處理多文件工程和程序多版本的問題。(程序多版本一般是不同平臺的版本,不同用戶等級的版本,不同開發階段的版本等)使用預編譯指令的目的增強程序可讀性但是調錯時宏可能帶來很多難題精簡源代碼,提取變化這一點更多時候用函數的效果更好,但宏也有其不可替代的優勢不編譯無用代碼,精煉目標代碼C程序設計語言第5章指針與數組計算機內的存儲部件,活動中的所有指令和數據都保存在內存內速度快,但是掉電即失可以隨機訪問只要指名要訪問的內存單元的地址,就可以立即訪問到該單元地址是一個無符號整數(通常用16進制數),其字長與主機相同內存中的每個字節都有唯一的一個地址內存(RandomAccessMemory)

地址(Address)指針的故事“該程序執行了非法操作,即將關閉”這種錯誤幾乎全是由指針和數組導致的黑客攻擊服務器利用的bug絕大部分都是指針和數組造成的有些非計算機專業的人,盡量避免使用指針指針的故事鐵桿C/C++程序員最摯愛的武器:指針指針造就了C/C++的高效和強大很多不可能的任務由指針完成main(){char*a="main(){char*a=%c%s%c;printf(a,34,a,34);}";printf(a,34,a,34);}關于指針的原則學習原則一定要學會其實通常的應用很簡單就是一個變量復雜的應用也不建議使用使用原則永遠要清楚每個指針指向了哪里永遠要清楚指針指向的位置是什么數組(Array)若干類型相同的相關數據湊到一起,就是數組定義類型數組名[整型常數1][整型常數2]……[整型常數n];inta[6][4];使用a[0][0]、a[1][2]、a[5][3]每個元素都是一個普通變量下標可以是任意整型表達式數組的各個元素在內存中分布在一起,分布規律是……array.c思考一下一維和三維數組怎么分布呢?從類型的角度理解數組inta[10];定義了一個有10個int類型元素的數組a的類型可以看作int[10](只是看作,語法并不允許這么定義:int[10]a)inta[20][10];定義了一個有20個int[10]類型元素數組a[0]、a[1]……a[9]的類型是int[10],所以a[0][0]、a[0][1]……a[19][9]的類型是intinta[30][20][10];這個呢?這種特性決定了數組元素在內存的分布規律,也解釋了數組的很多語法現象數組初始化數組定義后的初值仍然是隨機數,一般需要我們來初始化inta[5]={12,34,56,78,9};inta[5]={0};inta[]={11,22,33,44,55};數組大小最好用宏來定義,以適應未來可能的變化#defineSIZE10

inta[SIZE];數組的使用數組的下標都是從0開始對數組每個元素的使用與普通變量無異可以用任意表達式作為下標,動態決定訪問哪個元素for(i=0;i<SIZE;i++)

a[i]=2*i;下標越界是大忌!使用大于最大下標的下標,將訪問數組以外的空間。那里的數據不是我們所想定的情況,可能帶來嚴重后果有時,故意越界訪問數組會起到特別效果,但一定要對自己在做什么了如指掌sizeof可以用來獲得數組所占字節數sizeof(a)sizeof(a[0])數組的用處與特點保存大量同類型的相關數據快速地隨機訪問一旦定義,不能再改變大小在編譯階段就確定了數組的大小數組名幾乎就是一個指針指針(Pointer)int*p;定義了一個指針變量p,簡稱指針pp是變量,int*是類型變量都占用內存空間,p的大小是sizeof(int*)p用來保存地址。此時這個地址是哪呢(p指向哪呢)?inti;

p=&i;*p就像普通的變量一樣使用,其值是p指向的內存的內容,類型是int(在上例和i等價)p可以動態(任意)地指向不同內存,從而使*p代表不同的變量p=0;p=&a[0];指針指針也是數據類型。指向不同數據類型的指針,分別為不同的數據類型int*、float*、char*、int**、int***……指針指向非其定義時聲明的數據類型,將引起warningvoid*類型的指針可以指向任意類型的變量指針在初始化時一般int*p=NULL;NULL表示空指針,即無效指針但它只是邏輯上無效,并不是真正地無效如果指針指向一個非你控制的內存空間,并對該空間進行訪問,將可能造成危險&與*運算符&運算的結果指向該變量的指針inti,*p;

p=&i;int*p,a[10];

p=a;int*p,a[10];

p=&a[0];int*p,a[10];

p=&a[5];*和指針的組合是一個變量,該變量的地址和類型分別是指針指向的地址和指針定義時指向的類型inti,*p;

p=&i;

*p=0;int*p,a[10];

p=a;

*p=0;int*p,a[10];

p=&a[0];

*p=0;int*p,a[10];

p=&a[5];

*p=0;指針與數組數組名可以看作一個指針只是不能修改這個指針的指向常指針inta[10];a的類型是int[10]a的類型也是int*指針可當作數組名使用,

反之亦然int*p,a[10];

p=a;

p[1]=0;

*a=0;指針運算int*p=NULL;

p++;

/*p的值會是多少?*/指針的加減運算是以其指向的類型的字長為單位的int*p,a[10];

p=a;*(p+3)等價于a[3]p++;

*p等價于a[1]指針運算int*p,*q,a[10];

p=a;

q=&a[2];q-p==?q=p+3;運算法則只能進行加減和關系運算只能同類型指針之間或指針與整數之間運算“類型”本不存在存儲器在保存數據時并不關心數據的類型完全以二進制方式工作我們向計算機發出的指令說明了某塊內存里數據的類型一塊內存內保存著(61626364)16以char類型看待每個字節:"abcd"以float類型看待每個字節:16777999408082104000000.000000以int類型看待每個字節:1684234849依天屠龍,強強聯手intmain(void)

{

inta[]={0,1,2,3,4,5,6,7,8};

inti;

unsigned

char*p;

p=(unsigned

char*)a;/*類型強轉了*/

for(i=0;i<sizeof(a);i++)

{

PrintHexChar(p[i]);/*把指針當數組用*/

putchar('');

}

}指針強轉后,可以把一塊內存當作另一種類型來處理強強聯手,我們可以隨意控制任意內存god.c指針與函數指針既然是數據類型,自然可以做函數的參數和返回值的類型指針做參數的經典例子:main()

{

intx,y;

swap(x,y);

}voidswap(intx,inty)

{

inttemp;

temp=x;

x=y;

y=temp;

}NotWork指針做參數main()

{

intx,y;

swap(&x,&y);

}voidswap(int*px,int*py)

{

inttemp;

temp=*px;

*px=*py;

*py=temp;

}這里的函數調用過程還是“實參”的內容復制到“形參”,千萬不要理解成什么

“傳引用調用”指針做返回值printf("%s",GetInput());

......

char*GetInput(void)

{

charstr[100];

scanf("%s",str);

returnstr;

}charstring[30];

printf("%s",GetInput(string));

......

char*GetInput(char*str)

{

scanf("%s",str);

returnstr;

}√三個月使用scanf目睹之怪現狀inti;

scanf("%d",i);

/*這樣會如何?*/inti;

scanf("%f",&i);

/*這樣又會如何?*/charc;

scanf("%d",&c);

/*這樣呢?*/i的值被當作地址。例如,i的值如果是100,那么輸入的整數就會從地址100開始寫入內存輸入被當作float,以float的二進制形式寫到i所在的內存空間輸入以int的二進制形式寫到c所在的內存空間。c所占內存不足以放下一個int,其后的空間也被覆蓋數組做參數和指針一回事兒……voidProcessArray(int*a)

{

......

}voidProcessArray(inta[])

{

......

}這里給定元素個數有意義嗎?動態分配內存在<stdlib.h>和<alloc.h>中均定義了下面的函數void*malloc(size_tsize);size_t是在<stddef.h>中定義的數據類型,就是一個unsigned

int向系統申請大小為size的內存塊,把指向首地址的指針返回。如果申請不成功,返回NULLvoidfree(void*block);釋放由malloc()申請的內存塊。block是指向此塊的指針malloc申請的內存,在被free之前,程序的任何部分都可以使用當然,要使用必須得到指向它的指針動態分配內存如果malloc()申請的內存不被free()程序就退出,將產生內存泄露(Memory

Leak)“內存泄露”一詞類似“原料泄露”。泄露出去的原料不能被利用,導致生產過程中原料不足malloc()時,系統找到一塊未占用的內存,將其標記為已占用,然后把地址返回,表明此程序占用此塊內存,其它程序不能再用它free()時,系統標記此塊內存為未占用,本程序不能繼續使用,所有程序可以申請使用如果malloc()之后不free(),此塊內存將永遠不會被任何程序使用,就好像這塊內存泄露出去一樣防止內存泄露之道在需要的時候才malloc,并盡量減少malloc的次數能用自動變量解決的問題,就不要用malloc來解決malloc一般在大塊內存分配和動態內存分配時使用malloc本身的執行效率就不高,所以過多的malloc會使程序性能下降可以重復利用malloc申請到的內存盡量讓malloc和與之配套的free在一個函數內盡量把malloc集中在函數的入口處,free集中在函數的出口處以上做法只能盡量降低產生泄露的概率。完全杜絕內存泄露,關鍵要靠程序員的細心與責任感字符串(String)

與字符數組、字符指針字符串一串以'\0'結尾的字符在C語言中被看作字符串用雙引號括起的一串字符是字符串常量,C語言自動為其添加‘\0’終結符"Helloworld!"把字符串常量作為表達式直接使用,得到的值是該常量的地址C語言并沒有為字符串提供任何專門的表示法,完全使用字符數組和字符指針來處理字符數組每個元素都是字符類型的數組charstring[100];字符指針指向字符類型的指針char*p;數組和指針可以等同看待,上面三者本質上是一回事字符串處理函數在<string.h>中定義了若干專門的字符串處理函數strcpy:string

copy

char*strcpy(char*dest,const

char*src);strlen:string

lengthsize_tstrlen(const

char*s);strcat:string

combinationchar*strcat(char

*dest,constchar

*src);strcmp:string

comparisonintstrcmp(const

char*s1,const

char*s2);stricmp:string

comparison

ignoring

caseintstricmp(const

char*s1,const

char*s2);指針、數組以及其它的類型混合基本數據類型int、long、char、short、float、double……指針是一種數據類型是從其它類型派生的類型XX類型的指針數組也是一種數據類型是從其它類型派生的類型每個元

溫馨提示

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

評論

0/150

提交評論