《C語言程序設計》 - 何欽銘


第一章 引言

1.2 程序與程序設計語言

馮 · 諾依曼模型的存儲程序概念:將程序與數據共同存儲

結構化的程序設計方法:將復雜程序划分為若干個相互獨立的模塊(一個模塊可以是一條語句、一段程序或一個函數等)使完成每個模塊的工作變得單純而明確,在設計一個模塊時不受其他模塊的牽連。同時,通過現有模塊積木式的擴展就可以形成復雜的、更大的程序模塊或程序

1.3 C語言的發展歷史與特點

C語言的特點:(擁有底層操作能力)

1.結構化語言

2.易於移植

3.簡潔緊湊,使用方便靈活

4.強大的處理能力

5.生成的目標代碼質量高,運行效率高

第二章 用C語言編寫程序

2.3 計算分段函數

#include <stdio.h>	//編譯預處理指令

程序運行時,首先從main函數開始運行

%f指定輸出時保留6位小數,%.2f則指定輸出時保留2位小數

image-20210326214344680

循環體語句只能是一條語句(如果循環體語句由多條語句組成,必須用大括號把他們括起來,變成一條復合語句)

整型數據的輸出格式控制說明%md,指定了數據的輸出寬度為m(包括符號位)。若數據的實際位數(含符號位)小於m,左端補空格;若大於m,按實際位數輸出

實型數據的輸出格式控制說明 %m.nf,指定了輸出浮點型數據時保留n位小數,且輸出寬度是m(包括符號位和小數點)。若數據的實際位數(含符號位)小於m,左端補空格;若大於m,按實際位數輸出

f是針對浮點型數據而言,若輸出數據為整型,則%m.nf應改為%md

賦值運算符左邊只能是一個變量

第三章 分支結構

ch = getchar()	//從鍵盤輸入一個字符,並賦值給變量ch
putchar(輸出參數);	//輸出參數是字符型變量或字符型常量

第四章 循環結構

else和 if 的匹配准則:else與最靠近它的、沒有與別的 else 匹配過的匹配

do-while語句:循環條件需要在循環體中明確,且至少會執行一次循環體

do {

​ 循環體語句

} while (表達式);

//判斷一個數是否為素數

#include <stdio.h>
int main()	{
	int i,m;
	
	printf("Enter a number:");
	scanf("%d", &m);
	for(i=2;i<=m/2;i++)
		if(m%i==0)
			break;	//若m能被某個i整除,則m不是素數,提前結束循環 
		if(i>m/2 && m!=1)	//若循環正常結束,說明m不能被任何一個i整除	
			printf("%d is a prime number! \n", m);
		else
			printf("No! \n");
	return 0;
}

break語句強制循環結束,不再執行循環體中位於其后的其他語句,break語句應該和if語句配合使用,即條件滿足時,才執行break跳出循環;否則若break無條件執行,意味着永遠不會執行循環體中break后面的其他語句

continue語句:跳過循環體中continue后面的語句,繼續下一次循環,continue語句一般也需要與if語句配合使用

break語句和continue語句的區別在於,break結束循環而continue跳過后面語句繼續循環

break除了可以中止循環外,還用於switch語句,而continue只能用於循環

//使用嵌套循環計算1!+2!+3!+...+100
#include <stdio.h>
int main()	{
    int i,j;
    double item,sum;	//變量item中存放階乘的值
    sum = 0;
    for(i=1;i<=100;i++)	{
        item = 1;	//置item的初值為1,以保證每次求階乘都從1開始連乘
        	for(j=1;j<=i;j++)	//內層循環重復i次,算出item=i!
                item = item * j;
        	sum = sum+item;		//把i!累加到sum中
    }
    printf("1!+2!+...+100!=%e \n",sum);	//用指數形式輸出結果
    
    return 0;
}

//在累加求和的外層for語句的循環體語句中,每次計算i!之前,都重新置item的初值為1,以保證每次計算階乘都從1開始連乘

對嵌套循環初始化時,一定要分清內外層循環

4.5 循環結構程序設計

if(循環次數已知)
    使用 for 語句
else			//循環次數未知
    if(循環條件在進入循環時明確)
        使用 while 語句
    else		//循環條件需要在循環體中明確
        使用 do-while 語句

第五章 函數

函數定義的一般形式為:

函數類型	函數名	(形式參數表)	/*函數首部,由  函數類型、函數名、形式參數表  組成,函數名是函數整體的稱謂
    函數類型一般指函數結果返回的類型,一般與return語句中表達式的類型一致
    
    函數不能嵌套定義
    
    函數首部后面不能加分號*/

函數體用return語句返回運算的結果(return語句只能返回一個值),return類型和函數類型一致

return是普通變量,不是形參,它只是函數實現過程中要用到的工作單元,只有必須從主調函數中得到的已知條件,才定義為形參,其他需要的工作單元都定義成普通變量

double cylinder (double r, double n)
/*函數的類型是double,即函數的結果類型也是double*/

5.1.3 函數的調用

充分理解函數調用與返回的實現過程,對學好函數程序設計是至關重要的

1.函數的調用過程

主調函數指調用其他函數的函數,如main( )

2.函數調用的形式:

函數名(實際參數表)

實參(實際參數):可以是變量、常量、表達式,例如,cylinder ( )中,使用變量 radius 和 height 作為實參

對於實現計算功能的函數,函數調用通常出現在兩種情況下:

3.參數傳遞

函數定義時->

位於其首部的參數被稱為形參

主調函數的參數被稱為實參。形參除了能接受實參的值外,使用方法與普通變量類似。形參和實參必須一一對應,兩者數量相同,類型盡量一致。

程序運行遇到函數調用時,實參的值傳給形參

//計算圓柱體積
#include <stdio.h>
int main(void)
{
    double height, radius, volume;
    double cylinder(double r, double h);
    printf(" Enter radius and height:");
    scanf("%lf %lf", &radius, &height);
    
    volume=cylinder (radius, height);
    printf("Volume=%.3f\n",volume);
    return 0;
}

double cylinder (double r,double h)
{
    double result;
    
    result=3.1415926*r*r*h;		//計算圓柱體積
    return result;				//返回結果
}
    
而 main()函數中:
volume = cylinder (radius, height);
說明 radius 和 height 是實參。
    函數調用時,實參 radius 和 height 的值將被依次傳給形參 r 和 h

實參需要傳值給形參,所以函數的形參必須是變量,用於接受實參傳遞過來的值;實參可以是常量、變量或表達式

實參主調函數的,形參自定義函數

一般情況下表達式的類型與函數類型應一致,如果兩者不一致,以函數類型為准

5.函數原型聲明

函數聲明的一般格式: 函數名(參數表);

5.2 數字金字塔

//輸出5行的數字金字塔
#include <stdio.h>
void pyramid (int n);	//函數聲明
int main()
{
    pyramid(5);
    
    return 0;
}

void pyramid (int n)	//函數定義,輸出n行數字金字塔 
{
    int i,j;
    
    for(i=1;i<=n;i++)		//需要輸出的行數 
    {
        for(j=1;j<=n-i;j++)			//輸出每行左邊的空格 
            printf(" " );
        for(j=1;j<=i;j++)			//輸出每行的數字 
            printf("%d ",i);
        putchar('\n');
    }
}

運行結果

image-20210403191528270

函數定義時,形參 n 決定了需要輸出的數字金字塔的層數

5.2.2 不返回結果的函數

在很多程序設計中,調用函數是為了讓它產生某些作用。具有類似作用的函數在有些語言中也稱為過程

不返回結果的函數定義:

void 函數名(形參表)			//函數首部
{
    函數實現過程				//函數體
}

函數類型為void,表示不返回結果,函數體中可以使用沒有表達式的 return 語句,也可以省略 return 。void 類型的函數雖然不直接返回一個值,但它的作用通常以屏幕輸出等方式體現

在不返回結果的函數定義中,void不能省略;否則函數類型被默認定義為int

由於函數沒有返回結果,函數調用不可能出現在表達式中,通常以獨立的調用語句方式,如pyramid(5)

不返回結果的函數適用的場合主要是把一些確定的、相對獨立的程序功能封裝成函數。
主函數通過調用不同的函數,體現算法步驟,而各步驟的實現由相應函數完成,從而簡化主函數結構,以體現結構化程序設計思想

5.2.3 結構化程序設計思想

按照自頂向下的方法分析問題,有助於后續的模塊化設計與測試,以及系統的集成

5.3 復數運算

5.3.2 局部變量與全局變量

1.局部變量

定義在函數內部的變量,它們的有效作用范圍局限於所在的函數內部,因此主調函數只有通過參數傳遞,才能把實參數據傳遞給函數使用;同樣,形參的改變也不會影響到實參變量。這種變量的有效使用范圍,確保了各函數之間的獨立性,避免函數之間相互干擾

C還允許定義作用於復合語句中的局部變量,有效使用范圍被局限於復合語句內,一般用作小范圍內的臨時變量

2.全局變量

定義在函數外而不屬於任何函數的變量稱為全局變量。全局變量的作用范圍是從定義開始到程序所在文件的結束,它對作用范圍內所有的函數都起作用。全局變量是為了解決多個函數間的變量共用

當某函數的局部變量與全局變量同名時,在該函數中全局變量不起作用,而局部變量起作用。**

對於其他不存在同名變量的函數,全局變量仍然有效。

當函數局部變量與復合語句的局部變量同名時,以復合語句為准

全局變量可以幫助解決函數多結果返回的問題,但全局變量更多地用於多函數間的全局數據表示

缺點:對於規模較大的程序,過多使用全局變量會導致各函數間相互干擾

因此在變量使用中,應盡量使用局部變量函數參數

5.3.3 變量生存周期和靜態局部變量

計算機都是從主函數開始運行的,使得main( )函數中的局部變量,一開始就在內存數據區中分配了存儲單元。而其他函數在被調用之前,其局部變量並未分配存儲單元,只有當函數被調用時,其形參和局部變量才被分配相應存儲單元;一旦函數調用結束返回主調函數,在函數中定義的所有形參和局部變量將不復存在,相應的存儲單元由系統收回。根據這種特性,把局部變量稱為自動變量。變量從定義開始分配存儲單元,到運行結束存儲單元被回收,整個過程稱為變量生存周期

自動變量的定義形式:

auto int x,y

在自動變量定義時,auto可以省略,其形式與以前定義的普通變量完全相同

變量的作用范圍生存周期是兩個概念

C語言把保存所有變量的數據區分為動態存儲區靜態存儲區

  • 動態存儲區是使用堆棧來管理的,適合函數動態分配 與 回收存儲單元
  • 靜態存儲區相對固定,用於存放全局變量 和 靜態變量

image-20210404131417745

3.靜態變量

靜態變量:在程序執行前系統就為之靜態分配(也即在運行時不再改變分配情況)存儲空間的一類變量

一般來說,靜態變量 = 全局變量,而即使在有明確區分全局和靜態變量的程序語言中,在編譯后的代碼里二者也以相同的方式獲取存儲空間。

在靜態存儲區中,除了全局變量外還有靜態局部變量,它存放在靜態存儲區,靜態局部變量的生命周期會持續到程序結束

靜態變量只能用於所定義函數,而不能用於其它函數

靜態變量定義格式:

​ static 類型名 變量表

若沒有靜態保存的要求,不建議使用靜態變量

第六章 回顧數據類型和表達式

6.3 類型轉換

C中,不同類型的數據可以混合運算,但這些數據首先要轉換成同一類型,然后再做運算

image-20210404151319314

賦值運算時,賦值兩側數據的類型最好相同,至少右側數據的類型比左側數據的類型級別低,或者右側數據的值在左側變量的取值范圍內

6.3.2 強制類型轉換

(類型名) 表達式;

表達式 (double) i 的類型是double,而i的類型並沒有改變,還是原來的

6.4 表達式

6.4.4 邏輯表達式

~是位運算符,在二進制中是按位取反的意思,0變為1,1變為0

^是雙目運算符,按位“異或”

6.4.8 其他運算

長度運算符sizeof是一個單目運算符,用來返回變量或數據類型的字節長度

(ch = getchar())! ='\n' 和 ch = getchar()! ='\n'不等價
    后者是一個賦值表達式,等價於ch = (getchar()! ='\n'),所以()中存放的是0或1

條件運算符的優先級較低只比賦值運算符高。結合方向是自右向左

移位運算:>>右移 <<左移

a>>b	//將a的二進制右移b位

操作數的移位並不改變原操作數的值

長度運算符sizeof是一個單目運算符,用來返回變量數據類型的字節長度

第七章 數組

int a [10];		//定義1個數組a,它有10個整型元素
//類型名 數組名 [數組長度];

在程序中使用數組,可以讓一批相同類型的變量使用同一個數組變量名,用下標來相互區分。它的優點是表達簡潔,可讀性好,便於使用循環結構

數組名存放數組內存空間的首地址,是一個地址常量(不允許修改)

只能引用單個的數組元素,而不能一次引用整個數組,數組下標從0開始

注意區分數組的定義數組元素的引用,兩者都要用到“數組名[整型表達式]”。

定義數組時,方括號內是常量表達式,代表數組長度,它可以包括常量和符號常量,但方括號內不能包括變量。也就是說,數組的長度在定義時必須指定,在程序的運行過程中是不能改變的。

引用數組元素時,方括號內是表達式,代表下標,可以是變量,下標的合理取值范圍是[0, 數組長度-1]

//允許對靜態數組和動態數組初始化
static int b [5] = {1,2,3,4,5};	
//初始化靜態數組b,靜態存儲的數組如果沒有初始化,系統自動給所有的數組元素賦0
static int b [5];					

int fib[20] = {0,1};	
//對數組fib的前2個元素賦初值,其余元素的值不確定不確定不確定

數組的應用離不開循環,將數組的下標作為循環變量,通過循環,就可以對數組的所有元素逐個進行處理

數組的長度在定義時必須確定,如果無法確定,需要估計其上限,並將該上限作為數組長度

7.2.2 二維數組的定義和引用

二維數組主要用於表示二維表矩陣,引用二位數組的元素要制定兩個下標(行下標和列下標)

由於二維數組的行(列)下標從0開始,而矩陣或二維表的行(列)從1開始,用二維數組表示二維表和矩陣時,就存在行(列)計數的不一致。為了解決這個問題,可以把矩陣或二維表的第k行(列)也看成從0開始,即如果二維數組的行(列)下標為k,就表示矩陣或二維表的第k行(列)。

二維數組的定義形式為:類型名 數組名 [行長度] [列長度]

7.2.3 二維數組的初始化

定義二維數組時,可以對數組元素賦初值,二維數組的初始化方法有兩種

1.分行賦初值(也可以只針對部分元素)(直觀清晰,不易出錯,推薦)

int a [3] [3] = {{1,2,3}{4,5,6}{7,8,9}};

由於二維數組的元素在內存中按行優先方式存放,將行下標作為外循環的循環變量,列下標作為內循環的循環變量,可以提高程序的執行效率

7.3 判斷回文

'\0' 是字符串結束符

字符串的操作:用結束符'\0'來控制循環

字符串的存儲和運算可以用一維字符數組來實現。數組長度取上限80,以回車符'\n'作為輸入結束符

//采用賦值的方法將字符串"a"存入數組s。它等價於:
static char s [80] = "a";

/*區分"a"和'a':
"a"是字符串常量,包括'a'和'\0'兩個字符,用一維字符數組存放;
'a'是字符常量,只有一個字符,可以賦給字符變量

輸入字符串時,需要實現設定一個輸入結束符。一旦輸入它,就表示字符串輸入結束,並將輸入結束符轉換為字符串結束符'\0'

將字符串存入字符數組時,由於它有一個結束符'\0',數組長度至少是字符串的有效長度+1,。例如,字符串"Happy"的有效長度是5,存儲它的數組的長度至少應該為6

(字符串由有效字符字符串結束符 '\0' 組成)

將字符串存入一維字符數組后,對字符串的操作就是對該字符數組的操作,一般通過比較數組元素的值是否等於 '\0' 來決定是否結束循環,即用結束符'\0'來控制循環

/*統計字符串中數字字符的個數*/
#include <stdio.h>
int main()
{
    int count,i;
    char str [80];
    
    /*輸入字符串*/
    printf("Enter a string:");
    i = 0;
    while ((str [i] =getchar() )! ='\n')
        i++;
    str [i] ='\0';
    
    /*統計字符串中數字字符的個數*/
    count=0;
    for(i=0;str[i]!='\0';i++)	/*循環條件:str[i]不等於'\0'*/
        if (str[i]<='9' && str [i] >='0')
            count++;
    printf("count=%d\n",count);
    
    return 0;
}
/*運行結果:
Enter a string: It's 512?
count = 3

程序首先輸入一個字符串,再處理該字符串,統計其中數字字符('0'、...、'9')的數量
輸入一串字符后,輸入結束符'\n'被轉換為字符串結束符'\0',字符串"It's 512?"存入數組str中
由於字符串"It's 512?"只占用了數組的一部分,所以處理不能針對str的所有80個元素,只能針對該字符串,即數組str中第1個'\0'前面的字符,在處理時,程序從數組的首元素str[0]開始,按下標遞增的順序,逐個處理數組元素,一旦遇到某個元素是'\0',說明字符串已結束,處理也隨之結束

第八章 指針

如果事先無法確定需要處理的數據數量,一種方法是估計一個上限,並將該上限作為數組長度,這常常會造成空間浪費;另一種方法是利用指針實現存儲空間的動態分配

使用指針可以對復雜數據進行處理,能對計算機的內存分配進行控制,在函數調用中使用指針還可以返回多個值

8.1 密碼開鎖

/*獲取密碼的兩種方法*/
#include <stdio.h>
int main()
{
    int x = 5342;
    int *p = NULL;	/*定義整型指針變量p,NULL值為0,代表空指針*/
    
    p = &x;		/*將變量x的地址存儲在p中*/
    
    /*通過變量名x輸出密碼值*/
    printf("If I know the name of the variable, I can get it's value by name:%d\n",x);
    
    /*通過變量x的地址輸出密碼值*/
    printf("If I know the address of the variable is:%x,then I also can get it's value by address:%d\n",p, *p);
    
    return 0;
}
/*程序中定義了變量x來存放密碼,
再定義一個特殊的指針變量p,用於存放變量x的地址。
這樣既可以通過變量名x直接得到密碼值,也可以在不知道變量名的情況下,通過指針變量p所存放的x的地址間接找到密碼值。

8.1.2 地址和指針

計算機為了對內存單元中的數據進行操作,一般是按“地址”存取的,如下圖

image-20210406212150489

舉例:調用函數printf("%d",x)程序執行時是將變量翻譯為它所在的內存地址進行操作的
同時可以描述為:將x所在的內存地址1000~1001單元的內容按照整型格式輸出。這種變量的方法叫做直接訪問

直接訪問:一般以變量所在的內存單元的第1個字節的地址作為它的地址,如變量x的內存地址是1000,y的地址是1002,z的地址是1004,變量x、y、z的內容分別為20、1和155

還有一種使用變量的方法,通過變量的地址進行操作:用指針(point)訪問內存和操縱地址

image-20210406212719320

假設再定義一個變量p,它位於2000單元,該單元中存放了變量x的地址1000,如上圖,此時取出變量p的值1000,就可以訪問內存1000單元,實現對變量x的操作,也就是說通過變量p,可以間接訪問變量x

使用變量p訪問變量x的過程實現了對變量x的間接操作。在C語言中把這種專門用來存放變量地址的變量稱為“指針變量”,簡稱為指針

指針是用來存放內存地址的變量

scanf("%d",&n)
/*把輸入的值存儲到變量n所在的內存單元里,	&n代表變量n的內存地址/存儲位置

&稱為地址運算符*/

8.1.3 指針變量的定義

如果在程序中聲明一個變量,並使用地址作為該變量的值,那么這個變量就是指針變量。

定義指針變量的一般形式為:
類型名 *指針變量名;

類型名指定指針變量所指向變量的類型,必須是有效的數據類型

指針變量名是指針變量的名稱

定義指針變量要使用指針聲明符 *

int i, *p;
//聲明變量i是int型,變量p是指向int型變量的指針
int *p
//定義一個指針變量p,指向整型變量

指針和指針變量在含義上存在一定的差異

->指針被認為是一個概念,是計算機內存地址的代名詞之一,而指針變量本身就是變量,和一般變量不同的是它存放的是地址

大多數情況下並不特別強調他們的區別,如果未加聲明,指針 = 指針變量

指針變量用來存放變量的地址,由於不同類型的變量在內存中占用不同大小的存儲單元,所以只知道內存地址,還不能確定該地址上的對象。因此在定義指針變量時,除了指針變量名,還需要說明該指針變量所指向的內存空間上所存放數據的類型

int *p;		//定義一個指針變量p,指向整型變量
char *cp;	//定義一個指針變量cp,指向字符型變量
float *fp;	//定義一個指針變量fp,指向實型變量

定義多個指針變量時,每一個指針變量前面都必須加上 *

指針變量的類型是指它所指向的變量的數據類型。無論何種類型的指針變量,它們都是用來存放地址的,因此指針變量自身所占的內存空間大小和它所指向的變量數據類型無關

盡管不同類型的變量所占的內存空間不同,但不同類型指針變量所占的內存空間大小都是相同的

&:地址運算符

*:間接訪問運算符,還被用於定義指針變量

指針變量也要先賦值再使用,當然指針變量被賦的值應該是地址

p = &i;
//將指針p和變量i關聯起來,這也是指針最常用的賦值方法
p = 0;
p = NULL;
//說明怎樣把特殊值0賦值給指針p,這時指針的值為NULL。
//常量NULL在系統文件stdio.h中被定義,其值為0,將它賦給指針時,代表空指針,空指針不指向任何單元

image-20210407135423040

在定義指針變量時,要注意以下幾點:

  • 指針變量名是一個標識符,要按照C標識符的命名規則對指針變量進行命名。
  • 指針變量的數據類型是它所指向的變量的類型,一般情況下一旦指針變量的類型被確定后,它只能指向同種類型的變量
  • 定義指針變量時需要使用指針聲明符 *,但指針聲明符並不是指針的組成部分。例如定義 int *p ;說明p是指針變量,而不是*p

8.1.4 指針的基本運算 (想同類型的指針還能進行賦值、比較和算術運算)

1.取地址運算和間接訪問運算

&用於給出變量的地址,例如:

int *p, a=3;
p=&a;
//用運算符& 取變量a 的地址,並將這個地址值作為指針p的值,使指針p指向變量a
//運算符& 的操作數必須是變量
  • :間接訪問運算符。除了被用於定義指針變量外,還被用於訪問指針所指向的變量

例如:當p指向a時,*p和 a 訪問同一個存儲單元,*p的值就是 a 的值,如下圖所示

image-20210407201604119

/*取地址運算和使用指針訪問變量*/
#include <stdio.h>
int main()	{
    int a = 3, *p;	//定義整型變量a 和整型指針p*
    
    p = &a;			//把變量a的地址賦給指針p,即p指向a
    printf("a=%d, *p = %d\n", a, *p);	//輸出變量a的值和指針p所指向變量的值
    *p = 10;		//對指針p所指向的變量賦值,相當於對變量a賦值
    printf("a=%d, *p=%d\n", a, *p);
    printf("Enter a:" );
    scanf("%d", &a);	//輸入a
    printf("a=%d, *p=%d\n", a, *p);
    (*p) ++;	//將指針所指向的變量加1
    printf("a=%d, *p=%d\n", a, *p);
    
    return 0;
}

/*運行結果
a = 3,*p = 3
a = 10, *p = 10
Enter a: 5
a = 5, *p = 5
a = 6, *p = 6
int a = 3, *p
//定義了指針變量,p是變量名,*表示其后的變量是指針
//*p代表指針p所指向的變量
int a = 1, x, *p;
p = &a;
x = *p++;
/*指針先指向a,其后的語句x = *p++,將p所指向的變量a的值賦給變量x,然后修改指針的值,使得指針p不再指向變量a*/

要正確理解指針的含義,帶有間接地址訪問符 * 的變量的操作在不同的情況下會有完全不同的含義,這既是C的靈活之處,也是初學者最容易出錯的地方

*p = *p+1、++*p 和 (*p)++
//都是將指針p所指向變量的值+1
    
//表達式 *p++ 等價於 *(p++),先取 *p 的值作為表達式的值,再將指針p的值+1,運算后,p不再指向變量a

2.賦值運算

一旦指針被定義並賦值后,就可以如同其他類型變量一樣進行賦值運算

int a = 3, *p1, *p2;		//定義整型變量指針p1和p2
p1 = &a;					//把變量a的地址賦給指針p1
p2 = p1;					//p1和p2都指向變量a
//此時,*p1、*p2和a訪問同一個存儲單元,它們的值一樣 

給指針賦值是使指針和所指向變量之間建立關聯的必要過程,指針之間的相互賦值只能在相同類型的指針之間進行

8.1.5 指針變量的初始化

int a;
int *p1=&a;		//在定義指針p1的同時給其賦值,使指針p1指向變量a
int *p2=p1;		//在定義指針p2的同時給其賦值,使p2和p1的值相同

進行指針初始化需要注意:不能用數值作為指針變量的初值

例如:

int *p = 1000;//是不對的
int *p = 0;//是將指針變量初始化為空指針。這里0是ASCII字符NULL的值

8.2 角色互換

函數參數包括實參和形參,兩者的類型要一致。如果將某個變量的地址作為函數的實參,相應的形參就是指針

實參 形參之間的數據傳遞是單向的“值傳遞”方式

調用函數不能改變實參變量的值,當指針變量作為函數參數時也遵循這一個規則

調用函數不能改變實參指針變量的值,但可以改變實參指針變量所指向的變量的值。這樣的機制被稱為引用調用

采用引用調用機制需要在函數定義時將指針作為函數的形參,在函數調用時把變量的地址作為實參

void swap1 (int x, int y)
{
    int t;
    t=x;
    x=y;
    y=t;
}
/*
函數swap1()使用的是普通變量調用(值調用),參數的傳遞是從實參變量到形參變量的單個方向上的值的傳遞。在swap1()被調用時,將實參a和b的值傳遞給形參x和y,在函數中通過變量t實現了變量x和y的值的交換。但當返回主調函數時后,函數swap1()中定義的變量都銷毀了,而主調函數中的變量a和b的值沒有任何改變

即在函數swap1()中,改變了形參的值,但不會反過來影響到實參的值。因此,調用swap1()不能改變函數main()中實參a和b的值。
即swap1()的調用過程沒有實現a和b的值的交換
*/

void swap2(int *px, int *py)
{
    int t;
    t = *px;
    *px = *py
    *py = t;
}

/*
函數swap2()的實參是指針變量pa和pb,其值分別是變量a和b的地址。在函數swap2()被調用時,將實參pa和pb的值傳遞給形參px和py。這樣px和py中分別存放了a和b的地址,px指向a,py指向b,由於*px和a代表同一個存儲單元,只要在函數中改變*px的值,就改變了該存儲單元的內容。返回主調函數后,由於a代表的單元的內容發生了變化,a的值就改變了。

在函數swap2()中交換*px和*py的值, 主調函數中a和b的值也相應交換了,即達到了數據交換的目的
*/

void swap3(int *px, int *py)
{
    int *pt;
    pt = px;
    px = py;
    py = pt;
}

/*
函數swap3()的參數形式與2一樣,調用時的參數傳遞過程也相同,但在函數swap3()中直接交換了形參指針px和py的值,由於相同的理由,形參px和py的改變不會影響實參pa和pb

因此調用該函數並不能改變主調函數main()中變量a和b的值
*/

​ 要通過函數調用來改變主調函數中某個變量的值,可以把指針作為函數的參數。在主調函數中,將 該變量的地址 或者 指向該變量的指針 作為實參,在被調函數中,用指針類型形參接受該變量的地址,並改變形參所指向變量的值

將指針作為函數的參數就能使函數返回多個值

/*輸入2000 和 61,輸出2000-3-1*/
#include <stdio.h>
void month-day(int year, int yearday, int *pmonth, int *pday);

int main()	{
    int day, month, year, yearday;
    printf("input year and yearday:" );
    scanf("%d%d", &year, &yearday );
    month-day(year,yearday, &month, &day);
    printf("%d-%d-%d \n",year month, day);
    
    return 0;
}

void month-day(int year, int yearday, int *pmonth, int *pday);	{
    int k, leap;
    int tab [2][13] = {
        {0,31,28,31,30,31,30,31,31,30,31,30,31},
        {0,31,29,31,30,31,30,31,31,30,31,30,31},
    };
    
    /*建立閏年判別條件leap*/
    leap = (year%4 == 0 && year%100 != 0)  || year % 400 == 0;
    
    for (k=1;yearday>tab[leap] [k]; k++)
        yearday-=tab[leap] [k];
    *pmonth=k;
    *pday=yearday;
}

/*在函數main()中調用函數 month_day()時,將變量 month 和 day 的地址作為實參,在被調函數中用形參指針 pmonth 和 pday 分別接收地址,並改變了形參所指向變量的值。因此,函數main()中month 和 day 的值也隨之改變

8.3 冒泡排序

指針和數組有很多相似之處,比如指針名和數組名都代表內存地址,不同的是指針名是一個變量,數組名是一個常量,也就是說,指針名所代表的地址是可以改變的,數組名所代表的地址是不能改變的

//冒泡排序算法:輸入n個正整數,將它們從小到大排序后輸出
#include <stdio.h>
void bubble(int a [], int n);
int main()	{
    int n, a[8];
    int i;
    
    printf("Enter n (n<=8):" );
    scanf("%d",&n);
    printf("Enter a [%d]:", n);
    for(i=0;i<n;i++)
        scanf("%d",&a[i]);
    bubble(a, n);
    printf("After sorted, a[%d] =", n);
    for (i=0; i<n; i++)
        printf("%d", a[i]);
    
    return 0;

}

void bubble(int a [], int n)	{		//n是數組a中待排序元素的數量
    int i, j, t;
    for (i=1;i<n;i++)					//外部循環
        for (j=0;j<n-1;j++)				//內部循環
            if (a[j] > a[j+1])	{		//比較兩個元素的大小
                t = a[j]; a[j] = a[j+1]; a[j+1] = t;//如果前一個元素大,則交換
            }
}

/*
bubble()實現數組元素的排序。它有2個形參,a是等待排序的整形數組名,n指明數組a待處理的數組元素的數量
*/

8.3.2 指針、數組和地址間的關系

數組的基地址是在內存中存儲數組的起始位置,它是數組中第一個元素的地址,因此數組名本身是一個地址即指針值

​ 在訪問內存方面,指針是以地址作為值的變量,而數組名的值是一個特殊的固定地址,可以把它看作是常量指針

雖然在很多方面數組和指針都能處理同樣的問題,但它們之間有一個本質的不同,數組a是指針常量,不是變量,所以像 a=p, a++這樣的表達式都是非法的,不能改變指針常量a的值

兩同類型的指針相減,表示他們之間相隔的數組元素數目

8.4.2 字符串和字符指針

字符串常量實質上是一個指向該字符串首字符的指針常量

第九章 結構 - 把一些數據分量聚合成一個整體的數據類型

像數組和指針一樣,結構也是一種構造數據類型,它與數組的區別在於:數組中所有的元素的數據類型必須是相同的
結構中各成員的數據類型可以不同

//輸出平均分最高的學生信息
#include <stdio.h>
struct student	{		//學生信息結構定義
    int num;			//學號
    char name [10];
    int computer, english, math;
    double average;
};

int main()	{
    int i,n;
    struct student s1, max;		//定義結構變量
    printf("Input n:");
    scanf("%d", &n);
    printf("Input the student's number, name and course scores\n" );
    for (i=1;i<=n;i++)	{
        printf("No.%d:", i);
        scanf("%d%s%d%d%d",&s1.num, &s1.name, &s1.math, &s1.english, &s1.computer);
        s1.average=(s1.math+s1.english+s1.computer) /3.0;
        if (i==1) max=s1;
        if (max.average < s1, average)
            max=s1;
    }
    printf("num:%d, name:%s, average:%.2f\n",max.num, max.name, max.average);
    
    return 0;
}

/*
在程序首部定義了結構類型 struct student,其中的成員分別代表學生的基本信息項,。在main()中用此結構類型定義了兩個結構變量s1、max

結構變量可以通過 結構成員操作符"." 對其某個成員進行引用

如果兩個結構變量的類型相同,也可以直接賦值,如max=s1,將一個結構變量的所有成員值都賦值給另一個
*/

結構能夠把有內在聯系的不同類型的數據匯聚成一個整體,使它們相互關聯;同時結構又是一個變量的集合,可以按照對基本數據類型的操作方法單獨使用其成員變量。

結構類型定義的一般形式為:

struct 結構名	{
    類型名 結構成員名1;
    類型名 結構成員名2;
    ...
    類型名 結構成員名n;
}

/*在struct之后,自行命名一個結構名,struct與結構名兩者合起來共同組成結構類型名,如struct student

大括號里的內容是結構所包括的是結構成員

第十章 函數與程序結構

函數設計時應減少全局變量的使用。應采用定義局部變量作為函數的臨時工作單元,使用參數和返回值作為函數與外部進行數據交換的方式。只有當確實需要多個函數共享的數據時,才定義為全局變量

//用遞歸函數實現求n!
include <stdio.h>
double fact(int n);

int main ()	{
    int n;
    
    scanf("%d",&n);
    printf("%f",fact(n));
    
    return 0;
}

double fact(int n)	{
    double result;
    if (n==1 || n==0)	//遞歸出口
        result = 1;
    else
        result = n*fact(n-1);	//函數遞歸調用
    	//注意不能寫成fact(n) = n*fact(n-1)
    return result;
}

/*fact()函數中,函數自己調用自己的形式稱為函數的遞歸調用

遞歸函數編程時,要抓住遞歸方法的兩個要點:遞歸出口與遞歸調用式子
fact()函數的核心語句if-else體現的就是這兩個要點

*/

任何遞歸函數都必須包含條件,來判斷是否要遞歸下去,一旦結束條件成立,遞歸克隆應該不再繼續,以遞歸出口值作為函數結果,然后返回,結束一個遞歸克隆函數體。

遞歸的實質是把問題簡化成形式相同、但較簡單一些的情況,程序書寫時只給出統一形式,到運行時再展開

10.2.3 遞歸程序設計

兩個關鍵點:

  • 遞歸出口:遞歸的結束條件,到何時不再遞歸調用下去
  • 遞歸式子:遞歸的表達式,如fact(n) = n*fact(n-1)

遞歸程序設計的技巧性要求比較高,關鍵是歸納出遞歸式子,不同的問題其遞歸式子也不同,需要具體分析,然后確定遞歸的盡頭——遞歸出口,在編寫程序時只給出運算規律,具體實現細節應該讓計算機去處理。千萬不要鑽到細節的實現上去

10.3 宏基本定義

宏 #define 用來定義一些符號常量,可以方便程序的編制

宏定義的格式:

#define  宏名  宏定義字符串
/*
#表示define在編譯預處理中起作用,不是真正的C語句,所以行尾無需跟分號
常采用大寫字母作宏名,宏名中間不能有空格
宏定義字符串時宏名對應的具體實現過程,可以是任意字符串,中間可以有空格,以回車符作結束

在程序編譯時,所有出現宏名的地方,都會用宏定義字符串來替換。所以宏也稱為宏替換

舉例:
#define PI 3.1415926
#define TRUE 1
#define FALSE 0
*/

宏的用途包括:

  • 符號常量,如PI、數組大小定義,以增加程序的靈活性
  • 簡單的函數實現(參數使用必不可少)
//帶參數的宏定義
#include <stdio.h>
#define MAX(a,b)	a>b? a:b
#define SQR(x)	x*x
int main()	{
    int x, y;
    scanf("%d%d", &x, &y);
    x = MAX(x,y);	//引用宏定義
    y = SQR(x);		//引用宏定義
    printf("%d %d \n", x, y);
    
    return 0;
}

/*宏引用形式 和 函數調用 的實現過程完全不同。
宏替換在程序編譯預處理時完成,對於MAX(x,y)的編譯預處理,首先用變量名x和y分別替換a、b,然后再用包含x、y的條件表達式替換MAX。編譯結束后,程序匯總MAX(x,y)便消失

如果定義函數max(x, y),對它的處理要到程序執行時才進行,首先進行參數傳遞,把實參值復制給形參a和b,然后主函數暫停執行,去執行函數max(),等求出較大值后,通過return語句返回,主函數再繼續運行

函數調用時,如果實參是表達式,要先計算表達式,再把結果值傳遞過去
宏替換不作計算,直接替換進去

10.3.4 文件包含(include)

文件包含的作用是把指定的文件模塊內容插入到#include所在的位置,當程序編譯連接時,系統會把所有#include指定的文件拼接生成可執行代碼,include不是真正的C語句

對於復雜問題常常有大量宏定義,並被多個程序使用,自定義頭文件是一個很好的解決辦法,避免了多處重復定義相關宏,並能做到定義的一致性,一旦定義好寫成頭文件,

C的編譯預處理功能主要包括文件包含(#include)、宏定義(#define)、條件編譯

一般的程序經過編譯后,所有的C語句都生成到目標程序中,如果只想把源程序中一部分語句生成目標代碼,可以使用條件編譯,可以為一個程序提供多個版本,不同的用戶使用不同的版本

#define FLAG 1

#if FLAG

​	程序段1

#else

​	程序段2

#endif

條件編譯指令均以#開頭,與if-else語句完全不同:

​ if-else的兩個分支程序段都會被生成到目標代碼中,由程序運行時根據條件決定執行哪一段;

​ 而條件編譯#if···#else···#endif不僅形式不同,而且它起作用的時刻在編譯預處理的時候。一旦經過處理后,只有一段程序生成到目標程序中,另一端被舍棄,#if的條件只能是宏名,不能是程序表達式

​ 條件編譯的好處:1.目標代碼精簡 2.系統代碼保護性更好

所有的編譯預處理指令都是在編譯預處理步驟中起作用

10.4.3 文件模塊間的通信(局部變量、全局變量、外部變量)

局部變量從屬於函數,僅在函數內部有效

全局變量可以在整個程序中起作用。全局變量只能在某個模塊中定義一次,如果其他模塊要使用該全局變量,需要通過外部變量的聲明,當程序連接時會統一指向全局變量定義的模塊。否則不經聲明而直接使用全局變量,程序編譯時會出現“變量未定義”的錯誤

​ 對於全局變量來說,還有一種稱為外部變量的形式。即全局變量的使用位置先於該全局變量的定義,在使用之前需要聲明為外部變量

外部變量聲明格式:

​ extern 變量名表;

它只起說明作用,不分配存儲單元,對應的存儲單元在全局變量定義時分配。

靜態全局變量:如果整個程序只有一個文件模塊,靜態全局變量與一般的全局變量作用完全相同。當程序由多個文件模塊構成時,靜態全局變量用於限制全局變量作用域的擴展

為避免自己定義的全局變量影響其他人編寫的模塊,即所謂的全局變量的副作用,C的靜態全局變量可以把變量的作用范圍僅局限於當前的文件模塊中,即使其他文件模塊使用外部變量聲明,也不能使用該變量

如果一個程序包括多個文件模塊,要實現在一個模塊中調用另一模塊中的函數時,就需要對函數進行外部聲明

聲明格式:

extern  函數類型  函數名(參數表說明);

/*
extern可以省略,編譯程序如果在當前文件模塊中找不到函數定義體,自動認為該函數是外部函數
*/

為了避免各文件模塊間相互干擾,C允許把函數定義成靜態的,以便把函數的使用范圍限制在文件模塊內

靜態的函數在C中也稱為內部函數,定義格式為:

static 函數類型 函數名(參數表說明);

第十一章 指針進階

11.1.2 指針數組的概念

一維指針數組定義的一般格式為:

​ 類型名 *數組名 [數組長度];

關鍵是要掌握指針數組中,每個數組元素中存放的都是地址,通過數組元素可以訪問它所指向的單元

指針數組既可以直接對數組元素進行賦值(地址值)和引用,也可以間接訪問數組元素所指向的單元內容

指針數組元素的操作與對同類型指針變量的操作相同

11.1.3 指向指針的指針(二級指針)

定義:類型名 * * 變量名;

int a = 10;
int *p = &a;
int **p = &p;

/*scanf("%d",&a); 表示從鍵盤接收一個整數存儲到內存中&a所指的地址,也就是變量a中
&a表示變量a的地址

12. 文件

程序在實現的過程中,依賴於把數據保存到變量中,而變量是通過內存單元存儲數據的,數據的處理完全由程序控制。當一個程序運行完成或中止運行,所有變量的值不再保存。

一般的程序都會有數據輸入與輸出,如果輸入輸出數據量較大就會受到限制。

文件可以解決上述問題,它通過把數據存儲在磁盤文件中,得以長久保存。當有大量數據輸入時,可通過編輯工具實現建立輸入數據的文件,程序運行時將不再從鍵盤輸入,而從指定的文件上讀入,從而實現數據一次輸入多次使用。同樣,當有大量輸入輸出時,可以將其輸出到指定文件,不受屏幕大小限制,並且任何時候都可以查看結果文件。一個程序的運算結果還可以作為其他程序的輸入,進行進一步加工

實際上,用記事本編輯文件時,輸入的數據先是在內存中,保存后,數據才被寫入到磁盤文件中

//文件操作的函數
fopen()
fscanf()
fclose()

為了提高數據存取訪問的效率,C程序對文件的處理采用緩沖文件系統的方式進行,這種方式要求程序與文件之間有一個內存緩沖區,程序與文件的數據交換通過該緩沖區來進行

文件系統分為緩沖文件系統非緩沖文件系統

緩沖文件系統:進行文件操作時,系統自動為每一個文件分配一塊文件內存緩沖區(內存單元),C對文件的所有操作就通過對文件緩沖區的操作來完成。當程序要向磁盤文件寫入數據時,先把數據存入緩沖區,然后再由操作系統把緩沖區的數據真正存入磁盤。若要從文件讀入數據到內存,先由操作系統把數據寫入緩沖區,然后程序把數據從緩沖區讀入到內存。

image-20210610191328535

12.1.7 文件處理步驟

(1)定義文件指針

(2)打開文件:文件指針指向磁盤文件緩沖區

(3)文件處理:文件讀寫操作;

(4)關閉文件

12.2 用戶信息加密和校驗

C中,基本的文件操作有2個:從磁盤文件中讀信息(讀操作)和把信息存放到磁盤文件(寫操作)中。為了實現讀寫操作,首先要定義文件指針,然后打開文件即請求系統分配文件緩沖區,接着進行文件讀寫操作,文件操作完成后要關閉文件。在文件操作中,通過調用系統函數來實現文件的所有操作

附錄A C語言基本語法

運算符 名稱
* 取指針內容
& 取地址

結構的基本操作:

​ if:

x 是一個可以修改的變量,其類型為struct s

y 是一個類型為struct s

m 是類型struct s 的一個成員變量的名字

v 是一個表達式

​ SO:

​ x:引用整個結構,其類型為struct s

​ y.m:引用結構變量y的成員變量m,其類型為m的類型

​ x.m=v:將表達式v的值保存到結構變量x的成員變量m中,其類型為m的類型

​ x=y:將y的值賦給x,結果的類型為struct s

​ f(y):調用函數f( )並將結構變量y的內容作為參數傳遞給該函數。在函數f( )的內部,形式參數的類型必須是struct s

​ return y:返回結構變量y的內容,函數的返回值必須被聲明為struct s類型

指針的基本操作

x 是一個類型為t的比那輛

pt 是一個指向t類型變量的指針變量

v 是一個表達式

​ SO:

&x:生成一個指向x的指針,表達式的類型為指向t的指針

pt=&x:使得指針pt指向x,表達式的類型為指向t類型變量的指針

pt=NULL:將指針pt設置為空指針(也可以用0表示空)

pt==NULL:判斷pt是否為空指針

*pt:取得指針pt指向的值,表達式的類型為t

*pt=v:將表達式v的值保存在pt所指向的位置中,表達式的類型為t

image-20210607143428110

image-20210607143747124

多行注釋不能嵌套使用

在復合語句內可以定義局部變量,該局部變量將覆蓋 在該復合語句外定義的 同名變量。這些局部變量的作用域限制在定義它們的 復合語句的內部

break語句只能用於for、while、do或者switch語句內部,在遇到break語句之后,這些語句將立即結束執行,計算機將接着執行這些語句后面的語句

continue語句只能用於循環語句內部。當遇到continue語句之后,循環體中continue語句后面的語句將被跳過,計算機將接着開始執行下一次循環

return的第一種常見形式:return;

執行return語句將使得程序的執行流程立刻回到調用者。這種形式的return語句只能用在那些無返回值的函數中

return的第二種常見形式:return 表達式;
這個語句將 表達式的值 ** 作為 函數返回值 返回給調用者,如果計算機執行到函數的最后一條語句,但是還沒有遇到return語句,執行流程仍將返回調用者**,就好像已經執行了return語句一樣,在這種情況下,函數將不返回值

else子句總和最后一個沒有else子句的if語句配對

while (表達式):表達式的求值在循環體執行前進行

switch語句:如果沒有default語句,那么計算機將接着執行下面case語句中的語句

字符數組的初始化是一個特例,C允許使用字符串常量來初始化字符數組

char today [] ="Monday";
//定義了一個字符數組today,該數組的初始化值為:'M','o','n','d','a','y'和'\0'

定義指針變量的一般形式為:

類型名	*指針變量名

類型名 指定 指針變量所指向的 數據類型,必須是有效的數據類型

int *pt
//聲明了一個指向int類型變量的指針pt

struct point *pt;
//聲明了一個指向point結構類型的指針pt


//指向數組的指針  被聲明為  指向  該數組所容納元素類型的  指針
//比如上面聲明的指針pt也可以用來指向一個整數數組
char *pt [100];

struct point (*fnPrt) (int);
//聲明了一個指向返回值類型為 struct point 的函數的指針,該函數接受一個  整數參數

void* 是通用的指針類型,任何類型的指針都可以保存到void* 類型中,並且隨后將其從 void* 類型的指針中取出而不改變原來的值,除此以外,C不允許不同類型指針之前的轉換

(5)結構變量定義及初始化

結構的一般聲明形式如下:

struct 結構名 {
    類型名	結構成員名1;
    類型名 結構成員名2;
    ...
    類型名 結構成員名n;
} 變量列表;

結構中包含所有聲明的成員變量。每個成員變量聲明由一個類型名加上一個或者多個成員變量名組成

如果要聲明結構變量,可以在結構定義的時候,在結束的分號之前加上一個或者多個成員變量的名字,也可以在定義結構之后使用如下形式的語句聲明結構變量

struct	結構名	變量列表;
/*如果在定義結構類型的時候沒有指定名字的話,就不能使用上述形式。在這種情況下,必須在定義結構類型的時候聲明該類型的所有變量
*/

結構變量初始化可以用一對大括號將結構成員變量的初始值列表包圍起來。在聲明全局結構變量的時候,每一個成員變量的初始化表達式都必須是常量表達式

//C允許使用一個同類型的結構變量初始化另外一個結構變量
struct date tomorrow = today

(6)聯合變量定義和初始化

聯合的一般聲明形式如下所示:

union 聯合名
{
    成員聲明
    成員聲明
    ...
} 變量列表;

/*上面的形式可以用來定義名為“聯合名”的聯合,該聯合包含 所有列出的成員變量。聯合中的所有成員共享同一塊內存空間,C編譯程序保證分配給聯合的內存能夠容納其最大的成員變量*/

如何聲明聯合變量:

  • 可以在聯合定義的時候,在結束的分號之前加上這些變量的名字

  • 也可以在定義聯合之后使用如下形式的語句聲明聯合變量

    union  聯合名  變量列表;
    

(7)枚舉變量定義和初始化

定義枚舉數據類型的一般格式如下:

enum 枚舉名 {枚舉值1,枚舉值2,...} 變量列表;
/*上面的語句形式定義了名為 枚舉名枚舉類型,其枚舉值分別為枚舉值1,、枚舉值2等。
每一個枚舉值應該是一個合法的標識符,或者是一個標識符后面跟上一個等號,再加上一個常量表達式
變量列表本身是可選的,它代表一組該類型的變量(也可以同時初始化)

如果要聲明一個枚舉變量(假定該枚舉類型已經在前面定義過),可以采用如下方式

enum 枚舉名 變量列表;

某個枚舉變量的值只能是定義時列出的枚舉值之一

(8)存儲類型及作用域

存儲類型用於描述編譯程序為變量分配內存的方式,該術語也可以用於描述某個特定函數的使用范圍

C中有4種存儲類型:auto、static、extern和register

聲明時可以省略存儲類型,這時編譯程序將使用默認的存儲類型

作用域用於描述某個特定的標識符在程序中的可見范圍

定義在任何函數或者語句塊碗面的標識符可以在同一個文件中隨后的任意地方被引用

定義在語句塊內的標識符只能在該語句塊內被引用,因此在該語句塊外可以定義同名的標識符

標號和形式參數在整個語句塊中都可以引用

標號名、結構名和結構成員名、聯合與枚舉類型的名字以及變量名和函數名只要求在同類中唯一

(9)函數

當為函數指定存儲類型的時候,只能使用關鍵字 static 或者 extern

聲明為static的函數只能在定義該函數的文件內使用

聲明為extern(如果不指定存儲類型,默認為extern)的函數可以在其他文件中使用

(10)變量

(11)typedef語句

用於給基本數據類型和導出數據類型定義一個新的名字。這個語句本身並不創造新的數據類型,而只是給已經存在的數據類型起一個新的名字,因此編譯程序對使用新名字聲明的變量按照使用原來名字聲明的變量同樣的方式對待

//使用typedef語句的一般形式如下:
//typedef 老的變量名 新的變量類型名
    
typedef struct
{
    float x;
    float y;
} POINT;

/*上面的語句給一個結構類型賦予名字POINT,該結構類型包含兩個名為x和y的浮點數成員變量。隨后可以使用POINT來聲明新的變量,如下所示:*/
POINT origin = {0.0,0.0};

    

8.函數定義、調用和原型

如果在參數列表的括號中指定void,那么函數不接受參數

如果函數的參數類型為一維數組,那么在參數列表中不需要說明該數組的長度

在函數定義的前面可以加上關鍵字inline,該關鍵字只是編譯程序將函數的實際代碼插到適當的位置,而不是取調用函數,這樣可以獲得更快的執行速度

(2)函數調用

函數內部不能修改實際的參數,如果給函數傳遞一個指針參數,函數內部可以對該指針指向的位置進行修改,但是不能對實際的指針進行修改

9.預處理指令

(1)#define指令

//#define指令一般形式:
#define  宏名  宏定義字符串

/*定義了一個名為“宏名”的宏,並將該宏與其名字后的第一個空格后直到該行結束的字符串等價起來。C語言預處理器將用這個字符串替換隨后程序中任何位置出現的宏名*/

//#define指令的另一種形式:
#define 宏名 (參數1, 參數2, ..., 參數n)宏定義字符串
/*定義了一個名為“宏名”的宏,該宏接收一組參數,在隨后的程序中任何出現宏名的地方,預處理器將使用后面的宏定義字符串替換該宏名,並使用實際的參數替換宏定義字符串中的參數*/

(2)#if指令

// #if指令的一種常用形式如下所示
#if 常量表達式
    程序段
#endif
        
/*預處理器將對常量表達式求值,如果結果為非0,那么#if和#endif之間的語句將被處理。否則,預處理器和編譯程序都不會處理這些語句*/

(3)#ifdef指令

//#ifdef語句的一般使用形式如下:
#ifdef 標識符
	程序段
#endif
        
/*如果標識符代表的宏已經被定義過了(可能是通過#define語句,也可能是通過命令行上的-D命令選項),#ifdef和#endif之間的語句將被編譯,否則,這些語句將被忽略。如同#if指令一樣,#ifdef指令后面也可以有#elif指令和#else指令

與#ifdef相對應的還有#ifndef指令,意義正好和#ifdef相反*/

(4)#include 指令

完。

《啊哈C語言》筆記

啊哈C語言

學計算機究竟是學什么呢?答案是邏輯思維和編程思維

//選擇排序
#include <stdio.h>
#include <stdlib.h>
int main()	{
    int a[6], i, t, j;
    for(i=1;i<=5;i++)	{
        scanf("%d",&a[i]);
    for(i=1;i<=4;i++)	{
        for(j=1;i<=5;j++)	{
            if(a[i]>a[j])	{
                t=a[i];a[i]=a[j];a[j]=t;
            }
        }
    }
    }
    
    for(i=1;i<=5;i++)
        printf("%d ", a[i]);
	return 0;
}
//給一個字符變量賦值
char a='x';

%c在C語言中代表字符型格式符
%s在C語言中代表字符串型格式符

用scanf進行字符串讀入時,遇到空格就提前終止了
用gets進行讀入時卻可以讀入一整行

使用puts(a)輸出時,會在末尾自動換到下一行,相當於printf("%s\n", a)
    
兩個字符串的比較可以用函數strcmp( )
strcmp(a, b)就是比較字符串a和字符串b在字典中的順序。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM