目錄
內(nei) 部函數
外部函數
8.3.8 動態內(nei) 存分配與(yu) 指向它的指針變量
第1章 什麽是C語言
C語言的先祖是BCPL語言。它是由美國貝爾實驗室的D.M.Ritchie在1972-1973年間設計出來的計算機編程語言。C語言還是目前使用最為(wei) 廣泛的計算機編程語言之一,由於(yu) 它學習(xi) 起來簡單、使用方便、功能強大和容易上手等特點,普遍被廣大程序設計者使用。
第2章 算法
算法需要有以下特性:(1)有窮性:有限的操作步驟;(2)確定性:每個(ge) 步驟都是確定的,不能含糊;(3)有零個(ge) 或多個(ge) 輸入:從(cong) 外界獲取必要輸入信息;(4)有一個(ge) 或多個(ge) 輸出:一個(ge) 沒輸出的算法沒有意義(yi) ;(5)有效性:每個(ge) 步驟都能有效執行;
第3章 最簡單的C程序設計
3.1 數據類型
3.1.1 常量
常量包括以下幾種:整形常量、實型常量、字符常量、字符串常量和符號常量。整形常量如100等;實型常量有兩(liang) 種形式,一種是十進製小數形式如10.21,另一種是指數形式如12.34e3(代表12.34×10*3);字符常量包括普通字符如‘a’,和轉義(yi) 字符‘\n’。字符串常量是使用雙撇號把若幹個(ge) 字符括起來,如“boy123”。符號常量是指使用#define指令,指定一個(ge) 符號代表一個(ge) 常量,如#define P1 3.12,這時P1代表3.12。
3.1.2 變量
變量代表一個(ge) 有名字的、具有特定屬性的一個(ge) 存儲(chu) 單元,存儲(chu) 單元裏的值是可以改變的,所以變量的值可以改變。在使用變量時必須先定義(yi) ,後使用。在有些情況下我們(men) 不希望變量的值被改變,這時可以將變量定義(yi) 為(wei) 常變量。使用const可以將變量定為(wei) 常變量,如const int P1=12,之後P1的值將不會(hui) 被改變。
3.1.3 數據類型
在定義(yi) 變量和函數時,需要指定相應的數據類型,數據類型包括:整型類型、浮點類型、枚舉(ju) 類型(enum)、空類型(void)和派生類型。
整型類型:注意整型中,區分有無符號的整型,無符號需要加上unsigned,如unsigned int a;
類型 | 字節 |
基本整形(int) | 2或4 |
短整型(short) | 2 |
長整型(long int) | 4 |
雙長整型(long long int) | 8 |
字符型(char) | 1 |
布爾型(bool)值true 和 false | 1 |
浮點類型:
類型 | 字節 |
單精度浮點型(float) (有效6位) | 4 |
雙精度浮點型(double) (有效8位) | 8 |
長雙精度浮點型(long double) 有效(16位) | 16 |
複數浮點型 (不常用) |
派生類型:
指針類型(*) | 數組類型([ ]) | 結構體類型(struct) | 共用體類型(union) | 函數類型 |
數據以補碼的形式存儲(chu) 再存儲(chu) 空間中。
補碼求法:正·數的補碼就是其二進製碼如5的補碼是0000 0000 0000 0101,負數的補碼是其正數的二進製碼取反,再加1,如-5,它的正數是5,5的二進製碼是0000 0000 0000 0101,取反為(wei) 1111 1111 1111 1010,再加1,得-5的補碼是1111 1111 1111 1011。
3.2 運算符和表達式
經常混肴的運算符有:/(除法運算符)、%(求餘(yu) 運算符)、++和--(自增和自減運算符),例如++i(--i),它們(men) 在使用i之前,先使i的值加(減)1;i++(i--),它們(men) 在使用i之後,使i的值加(減)1。
強製類型轉換符:使用強製類型轉換符可以使一個(ge) 表達式轉換成所需的類型,一般形式如:(類型名)(表達式),例如(double)a,(float)(5%3)將5%3的值轉換為(wei) float類型。
需要注意的運算符:= 賦值運算符、== 等於(yu) 運算符、?:條件運算符、(sizeof)求字節數運算符、,->成員運算符、<<左移運算符、複合的賦值運算符,例如j×=y+8,等價(jia) 於(yu) j=j×(y+8)等。
左移運算符(<<)
將一個(ge) 運算對象的各二進製位全部左移若幹位(左邊的二進製位丟(diu) 棄,右邊補0)。
例:a = a << 2 將a的二進製位左移2位,右補0,
左移1位後a = a * 2;
若左移時舍棄的高位不包含1,則每左移一位,相當於(yu) 該數乘以2。
右移運算符(>>)
將一個(ge) 數的各二進製位全部右移若幹位,正數左補0,負數左補1,右邊丟(diu) 棄。
操作數每右移一位,相當於(yu) 該數除以2。
例如:a = a >> 2 將a的二進製位右移2位,
左補0 or 補1 得看被移數是正還是負。
注意: 當一個(ge) 占多字節的整型數據,給一個(ge) 占少字節的整型變量或字符變量賦值時,隻能將它的低字節原封不動的送到被賦值變量,例如:a占4個(ge) 字節,b占2個(ge) 字節,當b=a時,隻能把a的2位低字節賦給b。
3.3 數據的輸入輸出
3.3.1數據的輸入
通過標準的輸入函數完成,係統函數庫中標準的輸入函數有:scanf(格式輸入)、gets(輸入字符串)、getchar(輸入字符)。
scanf函數的一般形式:scanf(格式控製,地址列表),如scanf(“a=%f,b=%f”,&a,&b);
gets函數的一般形式:gets(地址),如int a[10],gets(a),從(cong) 鍵盤輸入字符串,送到數組a中。
getchar函數的一般形式:a=getchar(),從(cong) 鍵盤輸入一個(ge) 字符,送到字符變量a。
3.3.2數據的輸出
標準輸出函數有:printf(格式輸出)、puts(輸出字符串)、putchar(輸出字符)
printf函數的一般格式:printf(格式控製,輸出列表),如printf(“a=%d,ch[]=%s”,a,ch);a是變量名,ch是數組ch[]的數組名。
puts函數的一般格式:puts(地址),如int a[10],puts(a),這地址是指要輸出數組的地址。
putchar函數的一般格式:putchar(字符),如char c=‘a’,putchar(c),輸出字符變量c的值。
printf 的格式字符(scanf的格式基本一樣)
d,i | o | x,X | u | c | s | f | e,E | g,G |
十進製輸出整數 | 八進製 | 十六進製 | 無符號十進製輸出整數 | 字符輸出 | 字符串輸出 | 小數形式輸出單、雙精度數 | 指數形式輸出實數 | 選用%f或%e中輸出寬度較短的一種格式 |
printf 和scanf 的格式附加字符
printf | printf | printf | scanf | scanf |
l | m(代表一個正整數) | n(代表一個正整數) | l | h |
用於長整型整數,可加於d,0,x,u前麵 | 數據最小寬度 | 對實數時,表示輸出n個小數,字符串時,表示截取字符個數 | 用於輸入長整型數據,如%ld,%lx,%lo | 用於輸入短整型數據,如%hd,%ho,%hx |
第4章 選擇結構程序設計
4.1 if條件選擇
if語句的一般形式是:if(表達式)語句1,如if(a+b>10) printf("yes");表達的意思是,如果a+b的值大於(yu) 10,則執行printf函數輸出yes。通過判斷if表達式的真假,來判斷是否執行語句1。
if語句一般與(yu) else語句一起使用,以實現更多功能,例如
if(表達式) 語句1;
else 語句;
例如 if(a>b) a=b;
else b=a;
這裏表示如果a>b,則a=b,否則b=a;
if和else還可以嵌套使用,例如
if(number>50) a=1; else if(number>40) a=2; else if(number>30) a=3; else if(number>20) a=4; else a=5;
注意 : 關(guan) 係運算符的優(you) 先級低於(yu) 算術運算符
關(guan) 係運算符的優(you) 先級高於(yu) 賦值運算符
! | && | || |
邏輯非 | 邏輯與 | 邏輯或 |
!(邏輯非),如果a為(wei) 真,則!a為(wei) 假,如果a為(wei) 假,則!a為(wei) 真。
優(you) 先級:!(非)>&&(與(yu) )>||(或)
優(you) 先級:!(非)>算術運算符>關(guan) 係運算符>&&(與(yu) )>||(或)>賦值運算符
4.2 switch多分枝選擇
switch語句是多分枝選擇語句,switch的一般形式如下:
switch(表達式) { case 常量1 :語句1 case 常量2 :語句2 case 常量n :語句n default :語句n+1 }
注意:switch後麵括號內(nei) 的“表達式”,其值的類型應為(wei) 整數類型(字符型)
在執行switch語句時,根據switch表達式的值找到相應的入口標號,然後從(cong) 相應的case中執行語句,在執行完入口標號的case語句後,程序會(hui) 繼續往下執行下麵的case標號的語句,直到switch程序結束,所以一般情況下需要在case子句後麵,應用break語句,使程序跳出switch結構。如果沒有與(yu) switch表達式相匹配的case常量,流程會(hui) 轉去執行default標號後麵的語句。
switch語句使用例子:
#include<stdio.h> int mian() { char ch; int a=0; ch=getchar(); switch(ch) { case 'a' : printf("a");break; case 'b' : printf("b");break; case 'c' : printf("c");break; default : printf("d"); } return 0; }
第5章 循環結構程序設計
5.1 for循環結構
for循環的一般形式: for(表達式1;表達式2;表達式3) 語句,例如
for(i=1;i<10;i++)
sum=sum+i;
其中表達式1可以省略,如for(;i<10;i++),但分號不能省。但是當表達式2省略時,表示無限循環。
for循環實現冒泡算法:
void xunhuan(int r[],int n) { int i,j,temp=0; for(i=1;i<n;i++) for(j=1;j<=n-i;j++) if(r[j]>r[j+1]) { temp=r[j]; r[j]=r[j+1]; r[j+1]=temp; } }
5.2 while循環結構
while實現循環有兩(liang) 種方式,一種是使用do.....while結構,另一種是使用while結構。
do.....while語句的一般形式為(wei) :
do
{語句}
while(表達式);
例如:
#include<stdio.h> int main() { int i=1,sum=0; do { sum=sum+i; i++; }while(i<=100); printf("sum=%d\n",sum); return 0; }
while語句的一般形式為(wei) :
while(表達式)語句;
例如,使用while和for語句實現的冒泡算法
#include<stdio.h> int main() { int i=0,j=0,flag=1,temp=0; int a[5]={10,2,57,7,98}; while((i<4)&&flag) { flag=0 for(j=0;j<4-i;j++) if(a[j]>a[j+1]) { temp=a[j]; a[j]=a[j+1]; a[j+1]=temp; flag=1; } i++; } for{i=0;i<4;i++} printf("%d ",a[i]); return 0; }
注意:break語句和continue語句的區別是,在執行for或while循環語句時,通過break語句可以提前終止循環,跳出循環體(ti) 。而continue語句隻是提前結束本次循環,而不是終止整個(ge) 循環,然後繼續執行下一次循環。
第6章 數組
6.1 一維數組
數組是一組有序數據的集合,數組中的每個(ge) 元素都是同一種類型。通過數組可以存儲(chu) 一組數據或多組數據。定義(yi) 一維數組的一般形式為(wei) :類型符 數組名 [常量表達式];例如int a[10],定義(yi) 了一個(ge) 名為(wei) a的數組,它包含10個(ge) 元素,它第一個(ge) 元素是a[0]。當數組常量表達式為(wei) 空時,數組的長度由初始化列表中元素的個(ge) 數決(jue) 定,例如int a[ ]={1,2},這時a數組的長度為(wei) 2。
數組的常量表達式可以是常量和符號常量(#define P1 3.12,其中P1代表符號常量),但不能是變量。如果數組是在被調用的函數裏麵(不包括主函數)定義(yi) 的數組,其長度可以是變量或非常量表達式。例如:
void fun(int n) { int a[2*n];//合法,n的值從(cong) 實參傳(chuan) 來 . . . }
這種情況稱為(wei) “可變長數組”,允許每次調用fun函數時,n有不同的值,但在執行fun函數時,n的值是不變的。如果指定fun函數中的a數組為(wei) 靜態(static)存儲(chu) 方式,則不能用“可變長數組”,例如:static int a[2*n];這是不合法的,即可變長數組不能定義(yi) 為(wei) 靜態存儲(chu) 方式。
一維數組的數組名代表數組的起始地址。一維數組的初始化可以用“初始化列表”或給數組元素逐個(ge) 賦值,其中“初始化列表”的方式,如:int a[10]={0,1,2,3,4};,把初始化列表的數值,逐個(ge) 賦給a數組的前5個(ge) 元素,係統會(hui) 自動給後麵5個(ge) 元素賦初值為(wei) 0。給數組元素賦值,如a[0]=1;
數組中的元素引用,可以通過數組名加下標的方式,一般形式為(wei) :數組名[下標],下標可以是整型常量、整型的符號常量、整型表達式或者是整型變量,如int i=1; b=a[i]。
數組實現的冒泡算法
#include<stdio.h> int main() { int a[10]; int i,j,t; printf("input 10 numbers :\n"); for(i=0;i<10;i++) scanf("%d",&a[i]); printf("\n"); for(i=0;i<9:i++) for(j=0;j<9-i;j++) if(a[j]>a[j+1]) {t=a[j];a[j]=a[j+1];a[j+1]=t;} printf("the sorted numbers :\n"); for(j=0;j<10;j++) { printf("%d",a[j]); } printf("\n"); return 0; }
6.2 二維數組
二維數組定義(yi) 的一般形式:類型符 數組名[常量表達式1][常量表達式2];
例如:float a[3][4],定義(yi) 一個(ge) 3×4(3行4列)的數組a,常量表達式1表示數組的行數,常量表達式2表示數組的列數。
二維數組可以看成一個(ge) 特殊的一維數組,它的元素又是一個(ge) 一維數組,例如,把a看為(wei) 一維數組,它有3個(ge) 元素:a[0],a[1],a[2],每個(ge) 元素又是一個(ge) 包含4個(ge) 元素的一維數組,如a[0]包含:a[0][0],a[0][1],a[0][2],a[0][3]。a[0]、a[1]、a[2]可以看作三個(ge) 一維數組名。二維數組元素的存放是按行存放的,先放第一行,再放第二行。在內(nei) 存中二維數組的各元素是連續存放的,不是二維的,是線性的。
二維數組初始化和引用數組元素
二維數組的初始化形式:
1、給二維數組部分元素賦初值:如
int a[3][4]={ {1,2},[5],{9}}; 表示給第一行的1、2列分別賦值1和2,給第二、三行的第1列,分別賦值5和9,其餘(yu) 元素值自動為(wei) 0;
2、把所有數據寫(xie) 在一個(ge) 花括號中,按數組元素在內(nei) 存中的排列順序對各元素賦初值。
如int a[2][2]={1,2,3,4};
3、分行給二維數組賦初值。
例如:int a[2][2]={ {1,2},{3,4}};
4、如果給數組全部元素賦初值(即提供全部初始化數據),定義(yi) 二維數組時,第1維(行)的長度可以不指定,但第2維(列)的長度不能省略。
如:a[ ][2]={1,2,3,4}; 定義(yi) 一個(ge) 2×2的數組a
二維數組元素的引用:二維數組元素表示形式,數組名[下標1][下標2],下標應是整型表達式,如 a[0][1]=1;b=a[0][1]; 把數組a的第0行,第1列的元素賦值給b,b=1。
6.3 字符數組
字符數組的一般形式與(yu) 一維數組和二維數組的一樣的,隻是數據類型不同,例如,char a[2]={'I','k'};char a[ ]={I','k'};,這兩(liang) 個(ge) 數組的長度都是一樣的。char a[2][2]={ {'b','a'},{'c','d'}}; char a[][2]={ {'b','a'},{'d'}};,當字符數組初值的個(ge) 數小於(yu) 數組長度時,隻將這些字符賦給數組中前麵的元素,其餘(yu) 的元素自動定為(wei) 空字符(即‘\0’)。
在C係統中,使用字符數組存儲(chu) 字符串常量時會(hui) 自動加一個(ge) '\0'作為(wei) 結束符,C語言規定了'\0'作為(wei) 字符串結束標誌,'\0'代表ASCII碼為(wei) 0的字符。例如char c[ ]={"C program"},字符串是存放在一維數組中的,在數組中它占10個(ge) 字節,最後一個(ge) 字節'\0'是由係統自動加上的。
注意:char c[ ]={"Cab"};不等於(yu) char c[3]={‘C’,'a','b'};前式長度為(wei) 4,後式長度為(wei) 3。
字符串的輸入輸出
字符串輸入 char c[10]; 方式1 使用格式輸入函數scanf() scanf("%s",c) ; //c是數組名,代表數組c的地址 方式2 使用gets()輸入字符串函數 gets(c); //c是數組名,代表數組c的地址 字符串輸出 char c[10]; 方式1 使用格式輸出函數printf() printf("%s",c) ; //c是數組名,代表數組c的地址 方式2 使用puts()輸出字符串函數 puts(c); //c是數組名,代表數組c的地址
字符串處理函數,使用時需要加頭文件#include<string.h>
1、gets(字符數組) 輸入字符串函數 gets(c); //c是數組名,代表數組c的地址
2、puts(字符數組) 輸出字符串函數 puts(c); //c是數組名,代表數組c的地址
3、strcat函數——字符串鏈接函數 strcat(字符數組1,字符數組2)
char str1[30]={"ABC"}; char str2[]={"DE"}; printf("%s",strcat(str1,str2)); 輸出:ABCDE (1)字符數組1必須足夠大,以便容納連接後的新字符串。如果字符數組1不夠大,會(hui) 出現問題。 (2)連接前兩(liang) 個(ge) 字符串的後麵都有'\0',連接時將字符串1後麵的'\0'取消,隻在新字符串最後保留'\0'。 (3)連接的新串放在字符數組1中,函數最後返回的是字符數組1的地址。
4、strcpy和strncpy函數——字符串複製函數 strcpy(字符數組1,字符串2)
char str1[10],str2[ ]={"china"}; strcpy(str1,str2);
執行後str1的狀態如下:
c | h | i | n | a | \0 | \0 | \0 | \0 | \0 |
說明:
(1)、字符數組1的長度不應小於(yu) 字符串2。
(2)、字符數組1必須寫(xie) 成數組形式,字符串2可以是字符數組名,也可以是字符串常量。
(3)、如果字符數組1沒被賦初值,它各字節中的內(nei) 容是未知的,複製時將str2中的字符串和其後的‘\0’一起複製到字符數組1中,取代其前6個(ge) 字符,str1的最後4個(ge) 字符並不一定為(wei) '\0',而是其原有的最後4個(ge) 字節的內(nei) 容。
(4)、不能使用賦值語句直接把字符串常量或字符數組給一個(ge) 字符數組。
(5)、可以使用strncpy函數將字符串2中的前n個(ge) 字符複製到字符數組1中,但n不應多於(yu) str1中原有的字符(不包括'\0')。
5、strcmp函數——字符串比較函數 strcmp(字符串1,字符串2)
strcmp(str1,str2); strcmp(str1,"Beijing"); strcmp("China","Korea");
說明
(1)比較規則,兩(liang) 個(ge) 字符串自左至右逐個(ge) 字符相比較(按ASCII碼值大小比較),直至出現不同字符或遇到'\0'為(wei) 止。
(2)全部字符相同,則兩(liang) 個(ge) 字符串相等;
(3)若出現不同的字符,則以第1對不相同的字符的比較結果為(wei) 準。
(4)字符串1=字符串2,則函數值為(wei) 0;字符串1>字符串2,則函數值為(wei) 一個(ge) 正整數;字符串1<字符串2,則函數值為(wei) 一個(ge) 負整數。
6、 strlen函數——測字符串長度的函數 strlen(字符數組)
函數的值為(wei) 字符串中的實際長度(不包括'\0'在內(nei) ),例如
char str[10]={"china"}; printf("%d",strlen(str)); 輸出:5
7、strlwr函數——轉換為(wei) 小寫(xie) 函數 strlwr(字符串)
8、strupr函數——轉換為(wei) 大寫(xie) 函數 strupr(字符串)
第7章 函數的使用
7.1 為什麽使用函數
在編寫(xie) 一個(ge) 程序時,往往不是隻實現一個(ge) 或兩(liang) 個(ge) 功能,當程序功能多且複雜時,如果還是把功能的實現放在主函數裏,會(hui) 使得主程序變得複雜,使思路變得複雜,不利於(yu) 查看和檢查,而且有時一個(ge) 程序需要多人一起完成,如果把係統功能實現放在主程序中,這樣會(hui) 不利於(yu) 團隊的合作。通過函數可以把程序的各功能分別在子函數中實現,然後通過主函數調用各子函數就可以實現係統的功能,在團隊合作中,每個(ge) 人隻需把他需要完成的功能寫(xie) 在子函數中,最後通過主函數調用就能實現係統功能。使用函數可以大大簡化程序功能的實現,有助理清思路,各功能可以互不幹預。
7.2 函數的定義
定義(yi) 無參函數:
類型名 函數名() 或 類型名 函數名(void)
{ {
函數體(ti) 函數體(ti)
} }
定義(yi) 有參函數:
類型名 函數名(形式參數列表)
{
函數體(ti)
}
int max(int x,int y) { int z; //聲明部分 z=x>y?x:y; //執行語句部分 return(z); }
函數體(ti) 包括聲明部分和語句部分。定義(yi) 函數時的類型名是指定函數返回值的類型。
定義(yi) 空函數:類型名 函數名() { } 例如void fun() { }
7.3 函數的調用方式
如果函數在主函數後定義(yi) ,在函數調用前,需要對函數進行聲明,聲明方式是:類型名 函數名();例如:int max(int x,int y);,根據函數在程序中的位置和形式,函數調用分三種形式:
(1)函數調用語句 例如:fun();
(2)函數表達式 例如:c=2*max(a,b);
(3)函數參數 例如:m=max(a,max(b,c));
函數調用時,用到的實參和形參是直傳(chuan) 遞的,即形參的改變不會(hui) 影響到實參。當形參類型是指針、數組、或變量的&引用時,形參的改變才會(hui) 影響到實參。
函數的嵌套調用:
函數的嵌套調用是指,在主函數中調用a函數(子函數),然後在a函數中調用b函數(別的子函數),函數的執行流程是:在主程序中遇到a函數調用,則跳到a函數執行,在a函數遇到b函數調用,下一步跳到b函數執行,在b函數執行完後,程序返回a函數繼續執行,a函數執行完後,程序返回主函數繼續執行,直到整個(ge) 主函數執行完畢。
函數的遞歸調用:
在調用一個(ge) 函數的過程中又出現直接或間接地調用本函數本身,稱為(wei) 函數的遞歸調用。
int age(int n) { int c; if(n==1) c=10; else c=age(n-1)+2; return(c); }
7.4 數組作為函數參數
函數的實參可以是常量、變量或表達式,數組元素當實參與(yu) 變量相當,向形參傳(chuan) 遞數組元素的值,從(cong) 實參傳(chuan) 到形參,單向傳(chuan) 遞(值傳(chuan) 遞)。此外,數組名也可以當實參和形參,傳(chuan) 遞的是數組第一個(ge) 元素的地址(首地址),即實參和形參數組的地址相同,它們(men) 操作同一片存儲(chu) 空間,所以當形參改變時,實參也會(hui) 跟著改變。
7.5 內部函數和外部函數
7.5.1 局部變量
局部變量包括:在函數內(nei) 部定義(yi) 的變量(包括主函數)、在複合語句內(nei) 定義(yi) 的變量、形參也是局部變量。它的作用域是定義(yi) 函數內(nei) 部、定義(yi) 複合語句內(nei) 部,例如在fun函數內(nei) 定義(yi) 的變量,它的作用域是fun函數。在for循環裏定義(yi) 的變量,它的作用域是for循環裏麵。
7.5.2 全局變量
在函數之外定義(yi) 的變量稱為(wei) 外部變量,外部變量是全局變量,它的作用域是從(cong) 定義(yi) 變量的位置開始到本源文件結束,全局變量可以為(wei) 本文件中其他函數所共用。
當定義(yi) 的全局變量和局部變量同名時,在局部變量的作用域內(nei) ,同名的全局變量會(hui) 被同名的局部變量所屏蔽。例如在全局變量a=10,局部變量a=5,在局部變量a的作用域內(nei) ,b=a,則b的值為(wei) 5。
變量的存儲(chu) 方式:動態存儲(chu) 方式與(yu) 靜態存儲(chu) 方式
靜態存儲(chu) 方式:指在程序運行期間由係統分配固定的存儲(chu) 空間的方式。
動態存儲(chu) 方式:指在程序運行期間根據需要進行動態的分配存儲(chu) 空間的方式。
供用戶使用的存儲(chu) 空間分3部分:(1)程序區;(2)靜態存儲(chu) 區;(3)動態存儲(chu) 區。數據分別存儲(chu) 在靜態存儲(chu) 區和動態存儲(chu) 區中,全局變量全部存放在靜態存儲(chu) 區,在程序開始執行時給全局變量分配存儲(chu) 區,程序執行完畢就釋放。在程序執行過程中它們(men) 占據固定的存儲(chu) 單元,而不是動態地進行分配和釋放。
在動態存儲(chu) 區中存放以下數據:
(1)函數形式參數。在調用函數時給形參分配存儲(chu) 空間。
(2)函數中定義(yi) 的沒有用關(guan) 鍵字static聲明的變量,即自動變量(auto變量)。
(3)函數調用時的現場保護和返回地址。
以上這些數據,在函數調用開始時分配動態存儲(chu) 空間,函數結束時釋放這些空間。在程序執行過程中,這種分配和釋放是動態的。
在c語言中,每個(ge) 變量和函數都有兩(liang) 個(ge) 屬性:數據類型和數據的存儲(chu) 類別。在定義(yi) 和聲明變量和函數時,一般應同時指定其數據類型和存儲(chu) 類別,也可以采用默認方式指定(係統自動指定為(wei) 某一種存儲(chu) 類別)。數據類別指的是:數據在內(nei) 存中的存儲(chu) 方式(靜態存儲(chu) 和動態存儲(chu) )。
C的存儲(chu) 類別包括4種,自動的(auto),靜態的(statis)、寄存器的(register)、外部的(extern)。
1、自動變量(auto變量)
函數中的局部變量,如果不專(zhuan) 門聲明為(wei) static(靜態)存儲(chu) 類別,都是動態地分配存儲(chu) 空間的,數據存放在動態存儲(chu) 區中。函數中的形參和函數中定義(yi) 的局部變量(包括複合語句中定義(yi) 的局部變量)都是自動變量(auto變量)。
int fun(int a) { int b=3; //省略auto時,係統隱含指定為(wei) “自動存儲(chu) 類別(auto)”,b為(wei) 自動變量 auto int c=2; //定義(yi) c為(wei) 自動變量 . . }
當定義(yi) 的自動變量不賦初值時,它的值是未知的。
2、靜態局部變量(static)
靜態局部變量屬於(yu) 靜態存儲(chu) 類別,存放在靜態存儲(chu) 區內(nei) 分配的單元。對靜態局部變量賦初值是在編譯時完成的(即隻賦初值一次),在以後函數調用時,不再重新賦初值,而是保留上一次調用函數結束時的值。例如
int a() { static int i=1; printf{"%d",i}; i=i+1; }
第一次調用a函數時,i的初值為(wei) 1,輸出i為(wei) 1;第二次調用a函數輸出的i為(wei) 2,保留上一次函數結束時的值。
當定義(yi) 的靜態局部變量不賦初值時,編譯時自動賦初值為(wei) 0(對數值型變量)或'\0'(對字符變量)
3、寄存器變量(register變量)
寄存器變量存放在CPU中的寄存器中,它不是存放在內(nei) 存中的。由於(yu) 寄存器變量存放在cpu中的寄存器中,所以數據的存取速度遠高於(yu) 存放在內(nei) 存中的數據的存取。使用寄存器變量可以提高執行效率。定義(yi) 寄存器變量的一般形式:register int f;
4、全局變量(外部變量)
全局變量都是存放在靜態存儲(chu) 區中的。它的作用域一般是從(cong) 定義(yi) 全局變量處開始,到本程序文件的末尾。當定義(yi) 外部變量的位置,不是位於(yu) 文件開頭時,在外部變量定義(yi) 處之前的程序是不能引用外部變量的,如果要引用外部變量,需要在引用之前使用關(guan) 鍵字extern對該變量進行“外部變量聲明”
把外部變量的作用域擴展到其他文件的方式,可以在其他文件中使用extern對外部變量進行聲明。例如
文件file1.c #include<stdio.h> int a; //定義(yi) 外部變量 int b; //定義(yi) 外部變量 int main() { . . . } 文件file2.c extern int a; //把文件file1.c中的外部變量a的作用域擴展到file2.c中 extern b; //把文件file1.c中的外部變量b的作用域擴展到file2.c中 int fun() { . . . }
把外部變量的作用域限定在本文件中,隻能被本文件引用,而不能被其他文件引用,可以通過,在定義(yi) 外部變量時加一個(ge) statis聲明來實現。通過statis定義(yi) 的靜態外部變量隻能作用於(yu) 本文件,當其他文件也定義(yi) 了相同名字的外部變量時,它們(men) 之間的互不幹預的。例如
文件file1.c #include<stdio.h> static int a; //定義(yi) 靜態外部變量 int main() { . . . }
5、小結
(1)從(cong) 作用域角度分,有局部變量和全局變量。它們(men) 的存儲(chu) 類別如下:
局部變量 | 自動變量,即動態局部變量(離開函數,值就消失) |
靜態局部變量(離開函數,值仍然保留) | |
寄存器變量(離開函數,值就消失) | |
形式參數可以定義為自動變量或寄存器變量 | |
全局變量 | 靜態外部變量(隻限本文件引用) |
外部變量(即非靜態的外部變量,允許其他文件引用) |
(2)從(cong) 變量生存時間(生存期)來區分,有動態存儲(chu) 和靜態存儲(chu) 兩(liang) 種類型。
動態存儲 | 自動變量(本函數內有效) |
寄存器變量(本函數內有效) | |
形式參數(本函數內有效) | |
靜態存儲 | 靜態局部變量(函數內有效) |
靜態外部變量(本文件內有效) | |
外部變量(用extern聲明後,其他文件可引用) |
(3)從(cong) 變量值存放的位置來區分。
內存中靜態存儲區 | 靜態局部變量 |
靜態外部變量(函數外部靜態變量) | |
外部變量(可為其他文件引用) | |
內存中動態存儲區 | 自動變量和形式參數 |
CPU中寄存器 | 寄存器變量 |
內部函數
如果一個(ge) 函數隻能被本文件中其他函數所調用,它稱為(wei) 內(nei) 部函數(又稱靜態函數,因為(wei) 它使用static聲明的),它的作用域隻局限於(yu) 所在文件。這樣,在不同文件中即使有同名的內(nei) 部函數,也互不幹擾。一般形式:static 類型名 函數名(形參列表),例如static int a(int a){...}
外部函數
如果在定義(yi) 函數時,在函數首部的最左端加關(guan) 鍵字extern,則此函數是外部函數,可供其他文件調用。函數首部可以為(wei) :extern int fun(int a,int b)。C語言規定,如果在定義(yi) 函數時省略extern,則默認為(wei) 外部函數。
第8章 指針
8.1 什麽是指針
指針指的是地址,一個(ge) 變量的地址稱為(wei) 該變量的指針。一個(ge) 變量專(zhuan) 門用來存放另一個(ge) 變量的地址,稱為(wei) 指針變量。指針變量的值是地址。
8.2 指針變量
存放地址的變量是指針變量,它用來指向另一個(ge) 對象(如變量、數組、函數等)。定義(yi) 指針變量的一般形式:類型名 *指針變量名;如int *a;char *b;定義(yi) 了指向整型變量的指針變量a和定義(yi) 了指向字符變量的指針變量b;
指針變量的初始化,可以在定義(yi) 的同時初始化,也可以在定義(yi) 後初始化,例如:
int a=0; int *b=a; //定義(yi) 指針變量時初始化 int *c; c=&a; //定義(yi) 指針變量後初始化,意思是把a的地址賦值給指針變量c,指針變量c指向變量a 錯誤賦值方式:int *d=&a; //錯誤,*d不是指針變量,d才是指針變量, 所以應該改為(wei) int *d=a;
一個(ge) 變量的指針的含義(yi) 包括兩(liang) 方麵:一是以存儲(chu) 單元編號表示的地址,一是它指向的存儲(chu) 單元的數據類型。
8.3 指針的使用方式
8.3.1指針變量的使用
當指針變量指向一個(ge) 變量時,那麽(me) 指針變量就存儲(chu) 著所指向變量的地址,通過對指針變量的使用可以實現它所指向變量的功能。例如
int a=12; int *put=a; //定義(yi) 指向變量a的指針變量put printf("a=%d ",a); printf("a=%d",*put); 輸出指針變量put所指向的變量的值 結果:a=12 a=12
指針變量的使用例子:
輸入兩(liang) 個(ge) 數,按先大後小輸出 #include<stdio.h> int main() { int *p1,*p2,*p,a,b; printf("please enter two integer numbers:"); scanf("%d,%d",&a,&b); p1=&a; p2=&b; if(a<b) {p=p1;p1=p2;p2=p;} printf("a=%d,b=%d\n",a,b); printf("max=%d,min=%d\n",*p1,*p2); return 0; } 結果:please enter two integer numbers:5,9 a=9,b=5 max=9,min=5
8.3.2 指針變量作為(wei) 函數參數
指針變量作為(wei) 函數參數,例如:
#include<stdio.h> int mian() { int fun(int *a); //聲明函數fun int a=8; int *b=a; fun(&a); fun(b); return 0; } int fun(int *a); { printf("%d ",*a); } 運行結果:8 8
當指針變量作為(wei) 形參時,因為(wei) 形參和實參所操作的存儲(chu) 單元是同一個(ge) 的(它們(men) 指向同一個(ge) 存儲(chu) 單元),所以形參值的修改也會(hui) 導致實參值的修改。
8.3.3 通過指針引用數組
所謂數組元素的指針就是數組元素的地址,可以用一個(ge) 指針變量指向一個(ge) 數組元素,例如:
int a[5]={1,2,3,4,5}; int *p; p=&a[0]; 使指針變量p指向a數組的第0號元素。
在引用數組元素時指針的運算。因為(wei) 指針的值是數組元素的地址,當指針p指向數組中的一個(ge) 元素時,則p+1指向同一數組中下一個(ge) 元素,p-1指向同一數組中的上一個(ge) 元素。注意:執行p+1時,並不是將p的值(地址)簡單的加1,而是加上一個(ge) 數組元素所占用的字節數。例如,數組元素是float型,每個(ge) 元素占4個(ge) 字節,則p+1意味著是p的值(地址)加4個(ge) 字節。
通過指針引用數組元素
指針法:如 *(a+i)或*(p+i),其中a是數組名,p是指向數組元素的指針變量,其初值p=a。
通過數組名計算數組元素地址,找出元素的值。例如:
#include<stdio.h> int main() { int a[10]; int i; printf("please enter 10 integer numbers:"); for(i=0;i<10;i++) scanf("%d",&a[i]); for(i=0;i<10;i++) printf("%d ",*(a+i)); //通過數組名和元素序號計算元素地址,再找到該元素 printf("\n"); return 0; } 第10行中(a+i)是a數組中序號為(wei) i的元素的地址,*(a+i)是該元素的值。
用指針變量指向數組元素
#include<stdio.h> int main() { int a[10]; int i,*p; printf("please enter 10 integer numbers:"); for(i=0;i<10;i++) scanf("%d",&a[i]); for(p=a;p<(a+10);p++) printf("%d ",*p); //通過數組名和元素序號計算元素地址,再找到該元素 printf("\n"); return 0; }
注意:*p++,由於(yu) ++和*同優(you) 先級,結合方向為(wei) 自右向左,因此它等價(jia) 於(yu) *(p++),先引用p的值,實現*p的運算,然後再使p自增1(p+1).
數組名作為(wei) 函數參數:
int main() { void fun(int arr[],int n); //聲明fun函數 int array[10]; . . . fun(array,10); return 0; } void fun(int arr[],int n) { . . . }
變量名和數組名作為(wei) 函數參數比較:
實參類型 | 變量名 | 數組名 |
要求形參的類型 | 變量名 | 數組名或指針變量 |
傳遞的信息 | 變量值 | 實參數組首元素的地址 |
通過函數調用能否改變實參的值 | 不能改變實參變量的值 | 能改變實參數組的值 |
通過指針引用多維數組,以int a[3][4]為(wei) 例,a代表首行的首地址,a+1代表序號為(wei) 1的行的首地址,a+1指向a[1](代表a數組的元素,但它又是一維數組),或者說a+1的值是a[1]的首地址。a[0],a[1],a[2]代表一維數組名,因此a[0]代表一維數組a[0]的第0列的地址(即&a[0][0])。
a[0]+3代表a[0][3]的地址(&a[0][3]),“a[0]+3中的a[0]代表數組名(地址)”。
a[0](代表一維數組a的元素,但它又是一維數組)和*(a+0)等價(jia) ,a[ i ]和*(a+i)等價(jia) 。因此a[0]+1和*(a+1)+1等價(jia) ,都是&a[0][1]。進一步分析,a[0][1]的值,使用地址法為(wei) :*(*(a+0)+1)或*(*a+1)和*(a[0]+1)。
二維數組名(如a)指向行的。在指向行的指針前加一個(ge) *,就轉換為(wei) 指向列的指針,例如a+1(行指針),第一行的指針;*(a+1)為(wei) 列指針,第一行第0列的指針。反之在列的指針前加&,就成為(wei) 指向行的指針,例如&a[0],由於(yu) a[0]和*(a+0)等價(jia) ,因此&a[0]和&*a等價(jia) ,它是指向第0行的指針。
指向多維數組元素的指針變量
#include<stdio.h> int main() { int a[3][4]={....}; int *p; for(p=a[0];p<a[0]+12;p++) //a[0]是a[0][0]的地址,指針p指向a[0][0]元素 printf("%4d",*p); //輸出p指的數組元素的指 return 0; }
定義(yi) 指向“由n個(ge) 元素組成的一維數組”的指針變量
#include<stdio.h> int main() { int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23}; int (*p)[4],j,i; p=a; printf("please enter row and colum :"); scanf("%d,%d",&i,&j); printf("[%d,%d]=%d\n",i,j,*(*(p+i)+j)); return 0; } 結果:please enter row and colum:1,2 a[1,2]=13
用指向數組的指針作函數參數
#include<stdio.h> int main() { void a(int *a); void b(int (*b)[4]); int c[3][4]={...}; a(*c); //*c是數組c的第0行第0列的地址 b(c); //c是數組c的第0行的地址 return 0 }
8.3.4 通過指針引用字符串
例子:
#include<stdio.h> int main() { char *string="I love china!"; printf("%s",string); return 0; } 輸出:I love china!
注意:char *string="I love china!"; 等價(jia) 於(yu) char *string; string="I love china!",把字符串第一個(ge) 元素的地址賦值給字符指針變量string。
字符指針作函數參數,例如: void fun(char *p),調用時,實參可以是字符指針變量、字符數組名。
void fun(char a[ ]),調用時,實參可以是字符指針變量、字符數組名。
用字符指針作函數參數時,實參和形參的類型對應關(guan) 係如下
實參 | 形參 | 實參 | 形參 |
字符數組名 | 字符數組名 | 字符指針變量 | 字符指針變量 |
字符數組名 | 字符指針變量 | 字符指針變量 | 字符數組名 |
用字符指針變量指向一個(ge) 格式字符串,可以用它代替printf函數中的格式字符串,例如:
char *format; format="a=%d,b=%f\n"; printf(format,a,b); 它相當於(yu) printf("a=%d,b=%f\n",a,b); 因此隻要改變指針變量format所指的字符串,就可以改變輸入輸出的格式。 這種printf函數稱為(wei) “可變格式輸出函數”
8.3.5 指向函數的指針(函數指針變量)
函數代碼也有存儲(chu) 空間,這段存儲(chu) 空間的起始地址稱為(wei) 這個(ge) 函數的指針。定義(yi) 一個(ge) 指針變量指向函數,存放某一函數的起始地址,因為(wei) 此指針變量指向該函數。這就是指向函數的指針(函數指針變量)。函數指針變量定義(yi) 方式:類型名 (*指針變量名)(函數參數列表);例如:int (*p)(int ,int);,定義(yi) 一個(ge) 指向函數的指針變量p,它可以指向函數的類型為(wei) 整型且有兩(liang) 個(ge) 整型參數的函數。
可以通過指向函數的指針(函數指針變量)調用函數,例如
int max(int a,int b) {...} int (*p)(int ,int); p=max; 這時調用函數:max(1,2);等價(jia) 於(yu) (*p)(1,2);
指向函數的指針變量可以作為(wei) 函數參數。例如
void fun(int (*a)(int)) {...} int flie1(int a) {...} int (*b)(int); b=flie1; //指向flie1函數 fun(b); //指向函數的指針變量作實參 fun(flie1); //函數名作實參,函數名代表該函數存儲(chu) 空間的起始地址
8.3.6返回指針值的函數
定義(yi) 返回指針值的函數的一般形式:類型名 *函數名(參數列表),
例如 int *fun(int n){...},
int *fun(int n) { int *p; n=n+1; p=n; return(p); //返回指針變量p所指的地址 }
8.3.7 指針數組
定義(yi) 一維指針數組的一般形式:類型名 *數組名[數組長度],例如int *p[5],char *name[ ]={"abc","bcd"};
指向指針數據的指針變量的定義(yi) 為(wei) :類型名 **指針名,如char **p,p=name,其中name是指針數組的數組名,*p代表指針數組中存儲(chu) 的指針,**p代表指針數組中存儲(chu) 的指針所指的單元的值。
指針數組作main函數的形參:int main(int argc,char *argv[ ]),其中argc,argv,它們(men) 是程序的命令行參數(這是在係統操作命令下完成),argc是參數的個(ge) 數。命令行的一般形式:命令名 參數1 參數2 ....參數n,其中命令名是可執行文件名,如,可執行文件flie1.exe,現想將兩(liang) 個(ge) 字符串“china”,“beijing”作為(wei) 傳(chuan) 送給mian函數的參數,命令行可寫(xie) 為(wei) flie1 china beijing,這時argc的值為(wei) 3,參數的個(ge) 數為(wei) 3,argv[0]指向flie1字符串。
8.3.8 動態內存分配與指向它的指針變量
內(nei) 存中的動態存儲(chu) 區也稱之為(wei) "棧區"。在C語言中還允許建立內(nei) 存動態分配區域,以存放一些臨(lin) 時用的數據,這些數據不必在程序的聲明部分定義(yi) ,可以隨時開辟和關(guan) 閉,這些數據是臨(lin) 時存放在一個(ge) 特別的自由存儲(chu) 區,稱之為(wei) “堆(heap)區”
對內(nei) 存的動態分配是通過係統提供的庫函數來實現的,主要有malloc,calloc,free,realloc這4個(ge) 函數,在使用這些函數時,需要添加頭文件stdlib.h。
malloc函數,其函數原型為(wei) :void *malloc(unsigned int size),其作用是在內(nei) 存的動態存儲(chu) 區中分配一個(ge) 長度為(wei) size的連續空間,形參size是無符號整型,不允許有負數,該函數的返回值是所分配的第一個(ge) 字節的地址。注意,指針的基類型為(wei) void,即不指向任何類型的數據,隻提供地址。如果該函數未能成功執行(例如內(nei) 存空間不足),則返回空指針(NULL)。
calloc函數,其函數原型為(wei) :void *calloc(unsigned n,unsigned size),其作用是在內(nei) 存的動態存儲(chu) 區中分配n個(ge) 長度為(wei) size的連續空間,這空間一般比較大,足可以保存一個(ge) 數組。可以使用calloc為(wei) 一維數組開辟動態存儲(chu) 空間,n代表數組元素個(ge) 數,每個(ge) 元素的長度為(wei) size。這是動態數組,該函數返回的是所分配域的起始位置的指針,如果分配不成功,則返回NULL。
free函數(無返回值),其函數原型為(wei) :void free(void *p),其作用是釋放指針變量p所指向的動態空間,使這部分空間能重新被其他變量使用。p應是最近一次調用calloc和malloc函數時得到的函數返回值。例如free(p)。
realloc函數,其函數原型為(wei) :void *realloc(void *p,unsigned int size);其作用是改變通過malloc和calloc函數獲得的動態空間的大小,通過realloc函數重新分配。用法說明:用realloc函數將p所指向的動態空間的大小改變為(wei) size,p的值不變。如果重新分配不成功,返回NULL。例如:realloc(p,50);
void 指針類型,定義(yi) 一個(ge) void類型的指針變量,它不指向任何類型的數據,它是空類型或不指向確定類型,例如void *p; p=&a, printf("%d",*p),錯誤,因為(wei) p是無指向,不能指向a。
指針變量可以有空值,即不指向任何變量,例如p=NULL。
#include<stdio.h> #include<stdlib.h> int main() { void check(int *p); int *p1,i; p1=(int *)malloc(5*sizeof(int)); for(i=0;i<5;i++) scanf("%d",p1+i); check(p1); free(p1); return 0 } void check(int *p) { int i; printf("They are fail:"); for(i=0;i<5;i++) if(p[i]<60) printf("%d",p[i]); printf("\n"); } 結果: 67 98 59 78 57 They are fail:59 57
有關(guan) 指針變量的歸納:
變量定義 | 類型表示 | 含義 |
int i; | int | 定義整型變量 |
int *p; | int * | 定義p為指向整型數據的指針變量 |
int a[5 ]; | int [5] | 定義整型數組a,它有5個元素 |
int *p[4]; | int *[4] | 定義指針數組p,它由4個指向整型數據的指針元素組成 |
int (*p)[4]; | int (*)[4] | p為指向包含4個元素的的一維數組的指針變量 |
int f(); | int () | f為返回整型函數值的函數 |
int *p(); | int *() | p為返回一個指針的函數,該指針指向整型數據 |
int (*p)(); | int (*) | p為指向函數的的指針變量,該函數返回一個整型值 |
int **p; | int ** | p是一個指針變量,它指向一個指向整型數據的指針變量 |
void *p | void * | p是一個指針變量,基類型為void(空類型),不指向具體的對象 |
第9章 自定義數據類型
9.1 結構體類型
結構體(ti) 類型,是由用戶根據自己的需要建立的數據類型。聲明一個(ge) 結構體(ti) 類型的一般形式:struct 結構體(ti) 名 {成員列表};例如:
struct student { int num; char name[20]; char sex; int ahe; float score; char addr[30]; struct Date birthday; //該成員的數據類型為(wei) Date結構類型 };
花括號內(nei) 是該結構體(ti) 的成員,成員列表也稱為(wei) “域表”,每個(ge) 成員是結構體(ti) 中的一個(ge) 域。成員名命名規則與(yu) 變量名相同。它的成員可以是另一個(ge) 結構體(ti) 類型。
定義(yi) 結構體(ti) 類型變量
在建立結構體(ti) 類型時,它隻相當於(yu) 一個(ge) 模型,並沒有定義(yi) 變量,其中並無具體(ti) 數據,係統不會(hui) 對其分配存儲(chu) 單元。定義(yi) 結構體(ti) 類型變量的三種方式:
1、先聲明結構體(ti) 類型,再定義(yi) 該類型的變量 例如:struct student student1;
2、在聲明類型的同時定義(yi) 變量:例如
struct student { 成員列表 }student1,student2; //定義(yi) 結構體(ti) 變量student1和student2
3、不指定類型名而直接定義(yi) 結構體(ti) 類型變量
其一般形式為(wei) struct { 成員列表 }變量名列表;
初始化結構體(ti) 變量
#include<stdi.h> int main() { struct student { long int num; char name[20]; char sex; }a={10101,"xiaoming",'m'}; //定義(yi) 結構體(ti) 變量a並初始化 return 0; } struct student a={10101,"xiaoming",'m'}; //定義(yi) 結構體(ti) 變量a並初始化 struct student a={.name="xiaoming"}; //定義(yi) 結構體(ti) 變量a,並對其成員a初始化,其他未被 指定初始化的數值型成員被係統初始化為(wei) 0,字符型成員被係統初始化為(wei) '\0',指針型成員被係統初始化 為(wei) NULL。
引用結構體(ti) 變量的成員:結構體(ti) 變量名.成員名 例如studen1.num=10101;
如果成員本身又是結構體(ti) 類型,則要使用若幹個(ge) 成員運算符,一級一級地找到最低的一級的成員。例如,student1.birthday.month(結構體(ti) 變量student1中的成員birthday中的成員month)
同類的結構體(ti) 變量可以互相賦值,如student1=student2;
可以引用結構體(ti) 變量成員的地址,也可以引用結構體(ti) 變量的地址。例如
scanf("%d",&student1.num); printf("%o",&student1); (輸出結構體(ti) 變量student1的首地址)
結構體(ti) 數組
定義(yi) 結構體(ti) 數組和定義(yi) 一般數組的方式類似,它的一般形式為(wei) :結構體(ti) 類型 數組名[數組長度],或struct 結構體(ti) 名 { 成員列表 } 數組名[數組長度];,它的每個(ge) 成員都是結構體(ti) 類型。
對結構體(ti) 數組初始化的形式是在定義(yi) 數組後麵加上:={ 初值列表 };
例如:struct person leader[3]={ {“Li”,0},{"Zhang",0},{"Sun",0}};
結構體(ti) 指針
指向結構體(ti) 變量的指針,稱為(wei) 結構體(ti) 指針,一個(ge) 結構體(ti) 變量的起始地址就是該結構體(ti) 變量的指針。指向結構體(ti) 變量的指針變量既可指向結構體(ti) 變量,也可指向結構體(ti) 數組中的元素。
如果p指向一個(ge) 結構體(ti) 變量stu,以下3種用法等價(jia) :
(1) stu.成員名(如stu.num)
(2) (*p).成員名(如(*p).num) //*p表示p指向的結構體(ti) 變量
(3) p->成員名(如p->num)
(++p)->num //先使p自加1,然後得到p指向的數組元素中的num成員的值。
結構體(ti) 變量和結構體(ti) 變量的指針作函數參數
(1) 結構體(ti) 變量的成員作實參,用法和普通的變量作實參一樣。(值傳(chuan) 遞)
(2)結構體(ti) 變量作實參。(值傳(chuan) 遞)
(3)用指向結構體(ti) 變量(或數組元素)的指針作實參,將結構體(ti) 變量的地址傳(chuan) 給形參。
用指針處理鏈表
鏈表是一種常見的重要的數據結構。它是動態地進行存儲(chu) 分配的一種結構。鏈表可以根據需要開辟內(nei) 存單元。鏈表有一個(ge) “頭指針”變量head,它存放一個(ge) 地址,該地址指向一個(ge) 元素。鏈表中每一個(ge) 元素稱為(wei) “結點”,每個(ge) 結點都應包含兩(liang) 個(ge) 部分:(1)用戶需要用的實際數據;(2)下一個(ge) 結點的地址。
鏈表中各元素在內(nei) 存中的地址可以不連續,要找某一元素,可以通過它的上一個(ge) 元素中的地址尋找。如果不提供頭指針(head),則整個(ge) 鏈表都無法訪問。通過結構體(ti) 變量建立鏈表最合適。例如:
struct student
{int num;
float score;
struct student *next; //next是指針變量,指向結構體(ti) 變量,鏈表中用來指向下一個(ge) 結點
};
建立簡單的靜態鏈表:
#include<stdio.h> struct student { int num; struct student *next; }; int main() {struct student a,b,c,*head,*p; a.num=1; b.num=2; c.num=3; head=&a; a.next=&b; b.next=&c; c.next=NULL; p=head; do { printf("%d\n",p->num) p=p->next; }while(p!=NULL) } 結果: 1 2 3
所有結點都是在程序中定義(yi) 的,不是臨(lin) 時開辟的,也不能用完後釋放,這種鏈表稱為(wei) “靜態鏈表”。
建立動態鏈表
動態鏈表是指在程序執行過程中從(cong) 無到有地建立起一個(ge) 鏈表,即一個(ge) 一個(ge) 地開辟結點和輸入各結點數據,並建立起前後相鏈的關(guan) 係。
#include<stdio.h> #include<stdlib.h> #define LEN sizeof(struct student) //設置LEN的長度 struct student { long num; float score; struct student *next; }; int n; struct student *creat(void) //定義(yi) 函數,此函數返回一個(ge) 指向鏈表頭的指針 { struct student *head,p1,p2; n=0; p1=p2=(struct student *)malloc(LEN); //開辟動態存儲(chu) 區,把起始地址賦給p1 scanf("%ld,%f",&p1->num,&p1->score); head=NULL; while(p1->num!=0) { n=n+1; if(n==1) head=p1; else p2->next=p1; p2=p1; p1=(struct student *)malloc(LEN); //開辟動態存儲(chu) 區,把起始地址賦給p1 scanf("%ld,%f",&p1->num,&p1->score); } p2-next=NULL; return(head); } int main() { struct student *pt; pt=creat(); //函數返回鏈表第一個(ge) 結點的地址 printf("\nnum:%ld\nscore:%5.f\n",pt->num,pt->score); //輸出第一個(ge) 結點的成員值 return 0; } 結果: 1 65 2 50 3 34 0 0 num:1 score:65
9.2 共用體類型
共用體(ti) 結構,用同一段內(nei) 存單元存放不同類型的變量(存放共用體(ti) 的成員,但同時隻能放一個(ge) ,保留最一個(ge) 存放的變量)。
定義(yi) 共用體(ti) 類型變量的一般形式:
union 共用體(ti) 名 例如 union Data
{成員表列 { int i;
}變量表列 ; char ch;
float f;
}a,b,c;
也可以類型聲明和變量定義(yi) 分開:例如:
union Data {int i; char ch; float f; }; union Data a,b,c;
共用體(ti) 類型數據的特點,同一段內(nei) 存可以幾種不同的類型成員,但每一瞬間隻能存放其中一個(ge) 成員,而不是同時存放幾個(ge) 成員。
union Data {int i; char ch; float f; }; union Data a; a.ch='a'; a.f=100.2; a.i=97; //內(nei) 存中最終存儲(chu) 97 printf("%d",a.i); //輸出97 printf("%c",a.ch); //輸出字符'a',ASCII碼是97 printf("%f",a.f); //輸出實數0.0000
9.3 枚舉(ju) 類型
聲明枚舉(ju) 類型的一般形式為(wei) :enum[枚舉(ju) 名] {枚舉(ju) 元素列表};
定義(yi) 枚舉(ju) 類型變量:例如:(1)先聲明枚舉(ju) 類型enum Weekdat: enum Weekdat {sun,mon,tue,wed,thu,fir,sat};(2)在定義(yi) 變量:enum Weekdat workday; workday變量的值隻能是枚舉(ju) 元素列表中的枚舉(ju) 元素(也稱枚舉(ju) 常量)。枚舉(ju) 元素列表中的枚舉(ju) 元素按順序係統默認它們(men) 的值是0,1,2,3....。即workday=mon;相當於(yu) workday=1;
用typedef聲明新類型名
簡單地用一個(ge) 新的類型名代替原有的類型名
例如: typedef int Integer;指定用Integer代表int類型。這樣 int i;等價(jia) 於(yu) Integer i;
通過typedef可以命名一個(ge) 簡單的類型名代替複雜的類型表示方法,例如
sypedef struct { long int num; char name[20]; char sex; }student1; 聲明了一個(ge) 新類型名student1,代表上麵的結構體(ti) 類型。 定義(yi) 結構體(ti) 變量: student1 xisi;
命名一個(ge) 新的類型名代表數組類型,例如typedef int Num[100];//聲明Num為(wei) 整型數組類型名
Num a;定義(yi) a為(wei) 整型數組名,它有100個(ge) 元素。
命名一個(ge) 新的類型名代表指針類型,例如typedef char *String;//聲明String 為(wei) 字符指針類型
String p,s[10];//定義(yi) p為(wei) 字符指針變量,s為(wei) 字符指針數組。一般typedef聲明新的類型名的開頭用大寫(xie) 。
命名一個(ge) 新的類型名代表指向函數的指針類型,typedef int (*Pointer)(); //聲明Pointer為(wei) 指向函數的指針類型,該函數的返回整型。Pointer p1,p2; //p1,p2為(wei) Pointer類型的指針變量。
第10章 對文件的輸入輸出(主要討論數據文件)
10.1 什麽是文件
文件有不同的類型,在程序設計中,主要用到兩(liang) 種文件:程序文件和數據文件。程序文件:包括源程序文件(後綴.c)、目標文件(後綴.obj)、可執行文件(後綴.exe)等,這些文件的內(nei) 容是程序代碼。數據文件,文件的內(nei) 容不是程序,而是程序運行時讀寫(xie) 的數據,如在程序運行過程中輸出到磁盤(或其他設備)的數據,或在程序運行過程中供讀入的數據,如:一批學生的成績數據。
為(wei) 了簡化用戶對輸入輸出設備的操作,使用戶不必去區分各種輸入輸出設備之間的區別,操作係統把各種設備都統一作為(wei) 文件處理。(例如鍵盤是輸入文件、顯示屏和打印機是輸出文件)。所謂”文件“一般指存儲(chu) 在外部介質上數據的集合(一批數據是以文件的形式存放在外部介質(如磁盤)上的)。
流,輸入輸出是數據傳(chuan) 送的過程,數據如流水一樣從(cong) 一處流向另一處,因此常將輸入輸出形象地稱為(wei) 流,即數據流。
10.2 文件分類
一個(ge) 文件要有一個(ge) 唯一的文件標識:包括(1)文件路徑;(2)文件名主幹;(3)文件後綴。如:D: \CC \temp \file1.dat
文件的分類:根據數據的組織形式,數據文件可分為(wei) :ASCII文件和二進製文件。數據在內(nei) 存中以二進製的形式存儲(chu) 的,如果不加轉換地輸出到外存,就是二進製文件,可以認為(wei) 它就是存儲(chu) 在內(nei) 存的數據的映像,所以也稱為(wei) 映像文件。ASCII文件又稱為(wei) 文本文件(text file),每一個(ge) 字節放一個(ge) 字符的ASCII代碼。
在磁盤上,字符一律以ASCII形式存儲(chu) ,數值型數據既可以用ASCII也可以用二進製存儲(chu) 。
10.3 文件的使用
ANSI C標準采用”文件緩衝(chong) 係統“處理數據文件。所謂文件緩衝(chong) 係統是指係統自動地在內(nei) 存區為(wei) 程序中每個(ge) 正在使用的文件開辟一個(ge) 文件緩衝(chong) 區。文件緩衝(chong) 區作用:程序數據區——輸入(或輸出)文件緩衝(chong) 區——磁盤。
文件類型指針:FILE *文件名,如FILE *f1。它是指向一個(ge) 文件的文件信息區(存放這文件的有關(guan) 信息的地方)。
打開文件,所謂的”打開“,是指為(wei) 文件建立相應的信息區(用來存放有關(guan) 文件的信息)和文件緩衝(chong) 區(用來暫時存放輸入輸出的數據)。
fopen函數,打開文件。函數調用方法:fopen(文件名,使用文件方式),例如fopen("a1","r"),fopen(a2,“r”),FILE *fp=fopen(a3,“w”),a2、a3是存放文件名的數組名。
文件使用方式 | 含義 | 如果指定的文件不存在 |
”r“(隻讀) | 為了輸入數據,打開一個也存在的文本文件 | 出錯 |
"w"(隻寫) | 為了輸出數據,打開一個文本文件 | 建立新文件 |
"a"(追加) | 向文本文件尾添加數據 | 出錯 |
"rb"(隻讀) | 為了輸入數據,打開一個二進製文件 | 出錯 |
"wb"(隻寫) | 為了輸出數據,打開一個二進製文件 | 建立新文件 |
"ab"(追加) | 向二進製文件尾添加數據 | 出錯 |
"r+"(讀寫) | 為了讀和寫,打開一個文本文件 | 出錯 |
"w+"(讀寫) | 為了讀和寫,建立一個新的文本文件 | 建立新文件 |
"a+"(讀寫) | 為了讀和寫,打開一個文本文件 | 出錯 |
"rb+"(讀寫) | 為了讀和寫,打開一個二進製文件 | 出錯 |
"wb+"(讀寫) | 為了讀和寫,建立一個新的二進製文件 | 建立新文件 |
"ab+"(讀寫) | 為了讀和寫,打開一個二進製文件 | 出錯 |
fclose函數,關(guan) 閉文件。fclose函數調用的一般形式:fclose(文件指針),例如:fclose(fp);
對文本文件讀寫(xie) 一個(ge) 字符的函數
函數名 | 調用形式 | 功能 | 返回值 |
fgetc | fgetc(fp) | 從fp指向的文件讀入一個字符 | 成功(返回讀取的字符),失敗(返回-1,即文件結束標誌EOF) |
fputc | fputc(ch,fp) | 把字符ch寫到文件指針變量fp所指向的文件中 | 成功(返回輸出的字符),失敗(返回-1,即文件結束標誌EOF) |
讀寫(xie) 一個(ge) 字符串的函數
函數名 | 調用形式 | 功能 | 返回值 |
fgets | fgets(str,n,fp) | 從fp指向的文件讀入一個長度為(n-1)的字符串,存放到字符數組str中。 | 讀成功(返回地址str),失敗(返回NULL) |
fputs | fputs(str,fp) | 把str所指向的字符串寫到文件指針變量fp所指向的文件中。 | 輸出成功(返回0),失敗(返回非0值) |
用格式化的方式讀取文件:
fprintf(文件指針,格式字符串,輸出列表);例如:fprintf(fp,"%d,%f",i,f);
fsanf(文件指針,格式字符串,輸入列表);scanf(fp,"%d,%f",&i,&f);
用二進製方式向文件讀取一組數據:它們(men) 一般調用形式:讀入:fread(buffer,size,count,fp);輸出:fwrite(buffer,size,count,fp);其中buffer是地址,size:要讀寫(xie) 的字節數,count:要讀寫(xie) 多少個(ge) 數據項(每個(ge) 數據項的長度為(wei) size)。注意,打開文件時要使用二進製形式,如:fopen(”file1“,”wb“)。
文件位置標記
(1)用rewind函數使文件位置標記指向文件開頭。
(2)用fseek函數改變文件位置標記,fseek函數的調用形式為(wei) :fseek(文件指針,位移量,起始點),起始點可以用0、1、2代替,其中0代表文件開始位置,1代表當前位置,2代表文件末尾位置。起始點名字如表:
起始點 | 名字 | 用數字代表 |
文件開始位置 | SEEK_SET | 0 |
文件當前位置 | SEEK_CUR | 1 |
文件末尾位置 | SEEK_END | 2 |
位移量應是long型數據(在數字的末尾加一個(ge) 字母L,就表示long型)
例如:fseek(fp,100L,0)將文件位置標記向前移到離文件開頭100個(ge) 字節處。
文件讀寫(xie) 的出錯檢測
ferror函數,檢查輸入輸出函數出錯的情況,一般調用形式:ferror(fp);出錯時返回非0值,未出錯返回0值。注意,對同一個(ge) 文件每一次調用輸入輸出函數,都會(hui) 產(chan) 生一個(ge) 新的ferror函數值。
clearerr函數,作用是使文件錯誤標誌和文件結束標誌為(wei) 0。一般調用形式:clearerr(fp)。