18luck网站

18luck网站電子設計 | 18luck网站Rss 2.0 會員中心 會員注冊
搜索: 您現在的位置: 18luck网站 >> 編程學習 >> C語言 >> 正文

C語言詳細入門教學_c語言教程_C語言入門教程

作者:佚名    文章來源:網絡    點擊數:    更新時間:2023/10/10

目錄

第1章 什麽(me) 是C語言

第2章 算法

第3章 最簡單的C程序設計

3.1 數據類型

3.1.1 常量

3.1.2 變量

3.1.3 數據類型

3.2 運算符和表達式

3.3 數據的輸入輸出

3.3.1數據的輸入

3.3.2數據的輸出

第4章 選擇結構程序設計

4.1 if條件選擇

4.2 switch多分枝選擇

第5章 循環結構程序設計

5.1 for循環結構

5.2 while循環結構

第6章 數組

6.1 一維數組

6.2 二維數組

6.3 字符數組

第7章 函數的使用

7.1 為(wei) 什麽(me) 使用函數

7.2 函數的定義(yi)

7.3 函數的調用方式

7.4 數組作為(wei) 函數參數

7.5 內(nei) 部函數和外部函數

7.5.1 局部變量

7.5.2 全局變量

內(nei) 部函數

外部函數

第8章 指針

8.1 什麽(me) 是指針

8.2 指針變量

8.3 指針的使用方式

8.3.1指針變量的使用

8.3.2 指針變量作為(wei) 函數參數

8.3.3 通過指針引用數組

8.3.4 通過指針引用字符串

8.3.5 指向函數的指針(函數指針變量)

8.3.6返回指針值的函數

8.3.7 指針數組

8.3.8 動態內(nei) 存分配與(yu) 指向它的指針變量

第9章 自定義(yi) 數據類型

9.1 結構體(ti) 類型

9.2 共用體(ti) 類型

9.3 枚舉(ju) 類型

第10章 對文件的輸入輸出(主要討論數據文件)

10.1 什麽(me) 是文件

10.2 文件分類

10.3 文件的使用


第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)。

Tags:C語言,編程入門,入門教程  
責任編輯:admin
請文明參與討論,禁止漫罵攻擊。 昵稱:注冊  登錄
[ 查看全部 ] 網友評論
關於我們 - 聯係我們 - 廣告服務 - 友情鏈接 - 網站地圖 - 版權聲明 - 在線幫助 - 文章列表
返回頂部
刷新頁麵
下到頁底
晶體管查詢