日韩欧美视频第二区,秋霞成人午夜鲁丝一区二区三区,美女日批视频在线观看,av在线不卡免费

電子開發網

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

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

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

目錄

第1章 什么是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 為什么使用函數

7.2 函數的定義

7.3 函數的調用方式

7.4 數組作為函數參數

7.5 內部函數和外部函數

7.5.1 局部變量

7.5.2 全局變量

內部函數

外部函數

第8章 指針

8.1 什么是指針

8.2 指針變量

8.3 指針的使用方式

8.3.1指針變量的使用

8.3.2 指針變量作為函數參數

8.3.3 通過指針引用數組

8.3.4 通過指針引用字符串

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

8.3.6返回指針值的函數

8.3.7 指針數組

8.3.8 動態內存分配與指向它的指針變量

第9章 自定義數據類型

9.1 結構體類型

9.2 共用體類型

9.3 枚舉類型

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

10.1 什么是文件

10.2 文件分類

10.3 文件的使用


第1章 什么是C語言


C語言的先祖是BCPL語言。它是由美國貝爾實驗室的D.M.Ritchie在1972-1973年間設計出來的計算機編程語言。C語言還是目前使用最為廣泛的計算機編程語言之一,由于它學習起來簡單、使用方便、功能強大和容易上手等特點,普遍被廣大程序設計者使用。



第2章 算法

算法需要有以下特性:(1)有窮性:有限的操作步驟;(2)確定性:每個步驟都是確定的,不能含糊;(3)有零個或多個輸入:從外界獲取必要輸入信息;(4)有一個或多個輸出:一個沒輸出的算法沒有意義;(5)有效性:每個步驟都能有效執行;



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

3.1 數據類型

3.1.1 常量

常量包括以下幾種:整形常量、實型常量、字符常量、字符串常量和符號常量。整形常量如100等;實型常量有兩種形式,一種是十進制小數形式如10.21,另一種是指數形式如12.34e3(代表12.34×10*3);字符常量包括普通字符如‘a’,和轉義字符‘\n’。字符串常量是使用雙撇號把若干個字符括起來,如“boy123”。符號常量是指使用#define指令,指定一個符號代表一個常量,如#define P1 3.12,這時P1代表3.12。

3.1.2 變量

變量代表一個有名字的、具有特定屬性的一個存儲單元,存儲單元里的值是可以改變的,所以變量的值可以改變。在使用變量時必須先定義,后使用。在有些情況下我們不希望變量的值被改變,這時可以將變量定義為常變量。使用const可以將變量定為常變量,如const int P1=12,之后P1的值將不會被改變。

3.1.3 數據類型

在定義變量和函數時,需要指定相應的數據類型,數據類型包括:整型類型、浮點類型、枚舉類型(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) 函數類型

數據以補碼的形式存儲再存儲空間中。

補碼求法:正·數的補碼就是其二進制碼如5的補碼是0000 0000 0000 0101,負數的補碼是其正數的二進制碼取反,再加1,如-5,它的正數是5,5的二進制碼是0000 0000 0000 0101,取反為1111 1111 1111 1010,再加1,得-5的補碼是1111 1111 1111 1011。


3.2 運算符和表達式

經常混肴的運算符有:/(除法運算符)、%(求余運算符)、++和--(自增和自減運算符),例如++i(--i),它們在使用i之前,先使i的值加(減)1;i++(i--),它們在使用i之后,使i的值加(減)1。

強制類型轉換符:使用強制類型轉換符可以使一個表達式轉換成所需的類型,一般形式如:(類型名)(表達式),例如(double)a,(float)(5%3)將5%3的值轉換為float類型。

需要注意的運算符:= 賦值運算符、== 等于運算符、?:條件運算符、(sizeof)求字節數運算符、,->成員運算符、<<左移運算符、復合的賦值運算符,例如j×=y+8,等價于j=j×(y+8)等。

左移運算符(<<)

將一個運算對象的各二進制位全部左移若干位(左邊的二進制位丟棄,右邊補0)。

例:a = a << 2 將a的二進制位左移2位,右補0,

左移1位后a = a * 2;

若左移時舍棄的高位不包含1,則每左移一位,相當于該數乘以2。

右移運算符(>>)

將一個數的各二進制位全部右移若干位,正數左補0,負數左補1,右邊丟棄。

操作數每右移一位,相當于該數除以2。

例如:a = a >> 2 將a的二進制位右移2位,

左補0 or 補1 得看被移數是正還是負。

注意: 當一個占多字節的整型數據,給一個占少字節的整型變量或字符變量賦值時,只能將它的低字節原封不動的送到被賦值變量,例如:a占4個字節,b占2個字節,當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),從鍵盤輸入字符串,送到數組a中。

getchar函數的一般形式:a=getchar(),從鍵盤輸入一個字符,送到字符變量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的值大于10,則執行printf函數輸出yes。通過判斷if表達式的真假,來判斷是否執行語句1。


if語句一般與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;

注意 : 關系運算符的優先級低于算術運算符

關系運算符的優先級高于賦值運算符

&& ||
邏輯非 邏輯與 邏輯或

!(邏輯非),如果a為真,則!a為假,如果a為假,則!a為真。

優先級:!(非)>&&(與)>||(或)

優先級:!(非)>算術運算符>關系運算符>&&(與)>||(或)>賦值運算符



4.2 switch多分枝選擇

switch語句是多分枝選擇語句,switch的一般形式如下:

switch(表達式)
{
    case 常量1 :語句1
    case 常量2 :語句2
    case 常量n :語句n
    default :語句n+1
}

注意:switch后面括號內的“表達式”,其值的類型應為整數類型(字符型)

在執行switch語句時,根據switch表達式的值找到相應的入口標號,然后從相應的case中執行語句,在執行完入口標號的case語句后,程序會繼續往下執行下面的case標號的語句,直到switch程序結束,所以一般情況下需要在case子句后面,應用break語句,使程序跳出switch結構。如果沒有與switch表達式相匹配的case常量,流程會轉去執行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實現循環有兩種方式,一種是使用do.....while結構,另一種是使用while結構。


do.....while語句的一般形式為:

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語句的一般形式為:

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語句可以提前終止循環,跳出循環體。而continue語句只是提前結束本次循環,而不是終止整個循環,然后繼續執行下一次循環。



第6章 數組

6.1 一維數組

數組是一組有序數據的集合,數組中的每個元素都是同一種類型。通過數組可以存儲一組數據或多組數據。定義一維數組的一般形式為:類型符 數組名 [常量表達式];例如int a[10],定義了一個名為a的數組,它包含10個元素,它第一個元素是a[0]。當數組常量表達式為空時,數組的長度由初始化列表中元素的個數決定,例如int a[ ]={1,2},這時a數組的長度為2。


數組的常量表達式可以是常量和符號常量(#define P1 3.12,其中P1代表符號常量),但不能是變量。如果數組是在被調用的函數里面(不包括主函數)定義的數組,其長度可以是變量或非常量表達式。例如:

void fun(int n)
{
  int a[2*n];//合法,n的值從實參傳來
.
.
.
}

這種情況稱為“可變長數組”,允許每次調用fun函數時,n有不同的值,但在執行fun函數時,n的值是不變的。如果指定fun函數中的a數組為靜態(static)存儲方式,則不能用“可變長數組”,例如:static int a[2*n];這是不合法的,即可變長數組不能定義為靜態存儲方式。


一維數組的數組名代表數組的起始地址。一維數組的初始化可以用“初始化列表”或給數組元素逐個賦值,其中“初始化列表”的方式,如:int a[10]={0,1,2,3,4};,把初始化列表的數值,逐個賦給a數組的前5個元素,系統會自動給后面5個元素賦初值為0。給數組元素賦值,如a[0]=1;

數組中的元素引用,可以通過數組名加下標的方式,一般形式為:數組名[下標],下標可以是整型常量、整型的符號常量、整型表達式或者是整型變量,如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 二維數組


二維數組定義的一般形式:類型符 數組名[常量表達式1][常量表達式2];

例如:float a[3][4],定義一個3×4(3行4列)的數組a,常量表達式1表示數組的行數,常量表達式2表示數組的列數。


二維數組可以看成一個特殊的一維數組,它的元素又是一個一維數組,例如,把a看為一維數組,它有3個元素:a[0],a[1],a[2],每個元素又是一個包含4個元素的一維數組,如a[0]包含:a[0][0],a[0][1],a[0][2],a[0][3]。a[0]、a[1]、a[2]可以看作三個一維數組名。二維數組元素的存放是按行存放的,先放第一行,再放第二行。在內存中二維數組的各元素是連續存放的,不是二維的,是線性的。


二維數組初始化和引用數組元素

二維數組的初始化形式:

1、給二維數組部分元素賦初值:如

int a[3][4]={ {1,2},[5],{9}}; 表示給第一行的1、2列分別賦值1和2,給第二、三行的第1列,分別賦值5和9,其余元素值自動為0;

2、把所有數據寫在一個花括號中,按數組元素在內存中的排列順序對各元素賦初值。

如int a[2][2]={1,2,3,4};

3、分行給二維數組賦初值。

例如:int a[2][2]={ {1,2},{3,4}};

4、如果給數組全部元素賦初值(即提供全部初始化數據),定義二維數組時,第1維(行)的長度可以不指定,但第2維(列)的長度不能省略。

如:a[ ][2]={1,2,3,4}; 定義一個2×2的數組a

二維數組元素的引用:二維數組元素表示形式,數組名[下標1][下標2],下標應是整型表達式,如 a[0][1]=1;b=a[0][1]; 把數組a的第0行,第1列的元素賦值給b,b=1。



6.3 字符數組

字符數組的一般形式與一維數組和二維數組的一樣的,只是數據類型不同,例如,char a[2]={'I','k'};char a[ ]={I','k'};,這兩個數組的長度都是一樣的。char a[2][2]={ {'b','a'},{'c','d'}}; char a[][2]={ {'b','a'},{'d'}};,當字符數組初值的個數小于數組長度時,只將這些字符賦給數組中前面的元素,其余的元素自動定為空字符(即‘\0’)。

在C系統中,使用字符數組存儲字符串常量時會自動加一個'\0'作為結束符,C語言規定了'\0'作為字符串結束標志,'\0'代表ASCII碼為0的字符。例如char c[ ]={"C program"},字符串是存放在一維數組中的,在數組中它占10個字節,最后一個字節'\0'是由系統自動加上的。

注意:char c[ ]={"Cab"};不等于char c[3]={‘C’,'a','b'};前式長度為4,后式長度為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不夠大,會出現問題。
(2)連接前兩個字符串的后面都有'\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的長度不應小于字符串2。

(2)、字符數組1必須寫成數組形式,字符串2可以是字符數組名,也可以是字符串常量。

(3)、如果字符數組1沒被賦初值,它各字節中的內容是未知的,復制時將str2中的字符串和其后的‘\0’一起復制到字符數組1中,取代其前6個字符,str1的最后4個字符并不一定為'\0',而是其原有的最后4個字節的內容。

(4)、不能使用賦值語句直接把字符串常量或字符數組給一個字符數組。

(5)、可以使用strncpy函數將字符串2中的前n個字符復制到字符數組1中,但n不應多于str1中原有的字符(不包括'\0')。


5、strcmp函數——字符串比較函數 strcmp(字符串1,字符串2)

strcmp(str1,str2);
strcmp(str1,"Beijing");
strcmp("China","Korea");

說明

(1)比較規則,兩個字符串自左至右逐個字符相比較(按ASCII碼值大小比較),直至出現不同字符或遇到'\0'為止。

(2)全部字符相同,則兩個字符串相等;

(3)若出現不同的字符,則以第1對不相同的字符的比較結果為準。

(4)字符串1=字符串2,則函數值為0;字符串1>字符串2,則函數值為一個正整數;字符串1<字符串2,則函數值為一個負整數。


6、 strlen函數——測字符串長度的函數 strlen(字符數組)

函數的值為字符串中的實際長度(不包括'\0'在內),例如

char str[10]={"china"};
printf("%d",strlen(str));
輸出:5

7、strlwr函數——轉換為小寫函數 strlwr(字符串)

8、strupr函數——轉換為大寫函數 strupr(字符串)



第7章 函數的使用

7.1 為什么使用函數

在編寫一個程序時,往往不是只實現一個或兩個功能,當程序功能多且復雜時,如果還是把功能的實現放在主函數里,會使得主程序變得復雜,使思路變得復雜,不利于查看和檢查,而且有時一個程序需要多人一起完成,如果把系統功能實現放在主程序中,這樣會不利于團隊的合作。通過函數可以把程序的各功能分別在子函數中實現,然后通過主函數調用各子函數就可以實現系統的功能,在團隊合作中,每個人只需把他需要完成的功能寫在子函數中,最后通過主函數調用就能實現系統功能。使用函數可以大大簡化程序功能的實現,有助理清思路,各功能可以互不干預。

7.2 函數的定義

定義無參函數:

類型名 函數名() 或 類型名 函數名(void)

{ {

函數體 函數體

} }


定義有參函數:

類型名 函數名(形式參數列表)

{

函數體

}

int max(int x,int y)
{
int z;         //聲明部分
z=x>y?x:y;     //執行語句部分
return(z);
}

函數體包括聲明部分語句部分。定義函數時的類型名是指定函數返回值的類型

定義空函數:類型名 函數名() { } 例如void fun() { }



7.3 函數的調用方式

如果函數在主函數后定義,在函數調用前,需要對函數進行聲明,聲明方式是:類型名 函數名();例如:int max(int x,int y);,根據函數在程序中的位置和形式,函數調用分三種形式:

(1)函數調用語句 例如:fun();

(2)函數表達式 例如:c=2*max(a,b);

(3)函數參數 例如:m=max(a,max(b,c));

函數調用時,用到的實參和形參是直傳遞的,即形參的改變不會影響到實參。當形參類型是指針、數組、或變量的&引用時,形參的改變才會影響到實參。


函數的嵌套調用:

函數的嵌套調用是指,在主函數中調用a函數(子函數),然后在a函數中調用b函數(別的子函數),函數的執行流程是:在主程序中遇到a函數調用,則跳到a函數執行,在a函數遇到b函數調用,下一步跳到b函數執行,在b函數執行完后,程序返回a函數繼續執行,a函數執行完后,程序返回主函數繼續執行,直到整個主函數執行完畢。


函數的遞歸調用:

在調用一個函數的過程中又出現直接或間接地調用本函數本身,稱為函數的遞歸調用。

int age(int n)
 {
    int c;
    if(n==1) c=10;
    else  c=age(n-1)+2;
    return(c);
 }


7.4 數組作為函數參數

函數的實參可以是常量、變量或表達式,數組元素當實參與變量相當,向形參傳遞數組元素的值,從實參傳到形參,單向傳遞(值傳遞)。此外,數組名也可以當實參和形參,傳遞的是數組第一個元素的地址(首地址),即實參和形參數組的地址相同,它們操作同一片存儲空間,所以當形參改變時,實參也會跟著改變。



7.5 內部函數和外部函數

7.5.1 局部變量

局部變量包括:在函數內部定義的變量(包括主函數)、在復合語句內定義的變量、形參也是局部變量。它的作用域是定義函數內部、定義復合語句內部,例如在fun函數內定義的變量,它的作用域是fun函數。在for循環里定義的變量,它的作用域是for循環里面。

7.5.2 全局變量

在函數之外定義的變量稱為外部變量,外部變量是全局變量,它的作用域是從定義變量的位置開始到本源文件結束,全局變量可以為本文件中其他函數所共用。

當定義的全局變量和局部變量同名時,在局部變量的作用域內,同名的全局變量會被同名的局部變量所屏蔽。例如在全局變量a=10,局部變量a=5,在局部變量a的作用域內,b=a,則b的值為5。


變量的存儲方式:動態存儲方式與靜態存儲方式

靜態存儲方式:指在程序運行期間由系統分配固定的存儲空間的方式。

動態存儲方式:指在程序運行期間根據需要進行動態的分配存儲空間的方式。

供用戶使用的存儲空間分3部分:(1)程序區;(2)靜態存儲區;(3)動態存儲區。數據分別存儲在靜態存儲區和動態存儲區中,全局變量全部存放在靜態存儲區,在程序開始執行時給全局變量分配存儲區,程序執行完畢就釋放。在程序執行過程中它們占據固定的存儲單元,而不是動態地進行分配和釋放。

動態存儲區中存放以下數據:

(1)函數形式參數。在調用函數時給形參分配存儲空間。

(2)函數中定義的沒有用關鍵字static聲明的變量,即自動變量(auto變量)。

(3)函數調用時的現場保護和返回地址。

以上這些數據,在函數調用開始時分配動態存儲空間,函數結束時釋放這些空間。在程序執行過程中,這種分配和釋放是動態的。


在c語言中,每個變量和函數都有兩個屬性:數據類型和數據的存儲類別。在定義和聲明變量和函數時,一般應同時指定其數據類型和存儲類別,也可以采用默認方式指定(系統自動指定為某一種存儲類別)。數據類別指的是:數據在內存中的存儲方式(靜態存儲和動態存儲)。

C的存儲類別包括4種,自動的(auto),靜態的(statis)、寄存器的(register)、外部的(extern)。


1、自動變量(auto變量)

函數中的局部變量,如果不專門聲明為static(靜態)存儲類別,都是動態地分配存儲空間的,數據存放在動態存儲區中。函數中的形參和函數中定義的局部變量(包括復合語句中定義的局部變量)都是自動變量(auto變量)。

int fun(int a)
{
int b=3;        //省略auto時,系統隱含指定為“自動存儲類別(auto)”,b為自動變量
auto int c=2;       //定義c為自動變量
.
.
}

當定義的自動變量不賦初值時,它的值是未知的。


2、靜態局部變量(static)

靜態局部變量屬于靜態存儲類別,存放在靜態存儲區內分配的單元。對靜態局部變量賦初值是在編譯時完成的(即只賦初值一次),在以后函數調用時,不再重新賦初值,而是保留上一次調用函數結束時的值。例如

int a()
{
static int i=1;
printf{"%d",i};
i=i+1;
}

第一次調用a函數時,i的初值為1,輸出i為1;第二次調用a函數輸出的i為2,保留上一次函數結束時的值。

當定義的靜態局部變量不賦初值時,編譯時自動賦初值為0(對數值型變量)或'\0'(對字符變量)


3、寄存器變量(register變量)

寄存器變量存放在CPU中的寄存器中,它不是存放在內存中的。由于寄存器變量存放在cpu中的寄存器中,所以數據的存取速度遠高于存放在內存中的數據的存取。使用寄存器變量可以提高執行效率。定義寄存器變量的一般形式:register int f;


4、全局變量(外部變量)

全局變量都是存放在靜態存儲區中的。它的作用域一般是從定義全局變量處開始,到本程序文件的末尾。當定義外部變量的位置,不是位于文件開頭時,在外部變量定義處之前的程序是不能引用外部變量的,如果要引用外部變量,需要在引用之前使用關鍵字extern對該變量進行“外部變量聲明”

把外部變量的作用域擴展到其他文件的方式,可以在其他文件中使用extern對外部變量進行聲明。例如

文件file1.c
#include<stdio.h>
int a;    //定義外部變量
int b;    //定義外部變量
int main()
{
    .
    .
    .
}
文件file2.c
extern int a;    //把文件file1.c中的外部變量a的作用域擴展到file2.c中
extern b;    //把文件file1.c中的外部變量b的作用域擴展到file2.c中
int fun()
{
    .
    .
    .
}

把外部變量的作用域限定在本文件中,只能被本文件引用,而不能被其他文件引用,可以通過,在定義外部變量時加一個statis聲明來實現。通過statis定義的靜態外部變量只能作用于本文件,當其他文件也定義了相同名字的外部變量時,它們之間的互不干預的。例如

文件file1.c
#include<stdio.h>
static int a;    //定義靜態外部變量
int main()
{
    .
    .
    .
}

5、小結

(1)從作用域角度分,有局部變量和全局變量。它們的存儲類別如下:

局部變量 自動變量,即動態局部變量(離開函數,值就消失)
靜態局部變量(離開函數,值仍然保留)
寄存器變量(離開函數,值就消失)
形式參數可以定義為自動變量或寄存器變量
全局變量 靜態外部變量(只限本文件引用)
外部變量(即非靜態的外部變量,允許其他文件引用)

(2)從變量生存時間(生存期)來區分,有動態存儲和靜態存儲兩種類型。

動態存儲 自動變量(本函數內有效)
寄存器變量(本函數內有效)
形式參數(本函數內有效)
靜態存儲 靜態局部變量(函數內有效)
靜態外部變量(本文件內有效)
外部變量(用extern聲明后,其他文件可引用)

(3)從變量值存放的位置來區分。

內存中靜態存儲區 靜態局部變量
靜態外部變量(函數外部靜態變量)
外部變量(可為其他文件引用)
內存中動態存儲區 自動變量和形式參數
CPU中寄存器 寄存器變量


內部函數

如果一個函數只能被本文件中其他函數所調用,它稱為內部函數(又稱靜態函數,因為它使用static聲明的),它的作用域只局限于所在文件。這樣,在不同文件中即使有同名的內部函數,也互不干擾。一般形式:static 類型名 函數名(形參列表),例如static int a(int a){...}



外部函數

如果在定義函數時,在函數首部的最左端加關鍵字extern,則此函數是外部函數,可供其他文件調用。函數首部可以為:extern int fun(int a,int b)。C語言規定,如果在定義函數時省略extern,則默認為外部函數。



第8章 指針

8.1 什么是指針

指針指的是地址,一個變量的地址稱為該變量的指針。一個變量專門用來存放另一個變量的地址,稱為指針變量。指針變量的值是地址。

8.2 指針變量

存放地址的變量是指針變量,它用來指向另一個對象(如變量、數組、函數等)。定義指針變量的一般形式:類型名 *指針變量名;如int *a;char *b;定義了指向整型變量的指針變量a和定義了指向字符變量的指針變量b;

指針變量的初始化,可以在定義的同時初始化,也可以在定義后初始化,例如:

int a=0;
int *b=a;    //定義指針變量時初始化
int *c;
c=&a;        //定義指針變量后初始化,意思是把a的地址賦值給指針變量c,指針變量c指向變量a
錯誤賦值方式:int *d=&a;  //錯誤,*d不是指針變量,d才是指針變量,
所以應該改為int *d=a;

一個變量的指針的含義包括兩方面:一是以存儲單元編號表示的地址,一是它指向的存儲單元的數據類型。



8.3 指針的使用方式

8.3.1指針變量的使用

當指針變量指向一個變量時,那么指針變量就存儲著所指向變量的地址,通過對指針變量的使用可以實現它所指向變量的功能。例如

int a=12;
int *put=a;    //定義指向變量a的指針變量put
printf("a=%d ",a);
printf("a=%d",*put);    輸出指針變量put所指向的變量的值
結果:a=12 a=12

指針變量的使用例子:

輸入兩個數,按先大后小輸出
#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 指針變量作為函數參數

指針變量作為函數參數,例如:

#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

當指針變量作為形參時,因為形參和實參所操作的存儲單元是同一個的(它們指向同一個存儲單元),所以形參值的修改也會導致實參值的修改。


8.3.3 通過指針引用數組

所謂數組元素的指針就是數組元素的地址,可以用一個指針變量指向一個數組元素,例如:

int a[5]={1,2,3,4,5}; int *p; p=&a[0]; 使指針變量p指向a數組的第0號元素。

在引用數組元素時指針的運算。因為指針的值是數組元素的地址,當指針p指向數組中的一個元素時,則p+1指向同一數組中下一個元素,p-1指向同一數組中的上一個元素。注意:執行p+1時,并不是將p的值(地址)簡單的加1,而是加上一個數組元素所占用的字節數。例如,數組元素是float型,每個元素占4個字節,則p+1意味著是p的值(地址)加4個字節。


通過指針引用數組元素

指針法:如 *(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數組中序號為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++,由于++和*同優先級,結合方向為自右向左,因此它等價于*(p++),先引用p的值,實現*p的運算,然后再使p自增1(p+1).


數組名作為函數參數:

int main()
{
void fun(int arr[],int n);    //聲明fun函數
int array[10];
    .
    .
    .
fun(array,10);
return 0;
}
void fun(int arr[],int n)
{
    .
    .
    .
}

變量名和數組名作為函數參數比較:

實參類型 變量名 數組名
要求形參的類型 變量名 數組名或指針變量
傳遞的信息 變量值 實參數組首元素的地址
通過函數調用能否改變實參的值 不能改變實參變量的值 能改變實參數組的值

通過指針引用多維數組,以int a[3][4]為例,a代表首行的首地址,a+1代表序號為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)等價,a[ i ]和*(a+i)等價。因此a[0]+1和*(a+1)+1等價,都是&a[0][1]。進一步分析,a[0][1]的值,使用地址法為:*(*(a+0)+1)或*(*a+1)和*(a[0]+1)。

二維數組名(如a)指向行的。在指向行的指針前加一個*,就轉換為指向列的指針,例如a+1(行指針),第一行的指針;*(a+1)為列指針,第一行第0列的指針。反之在列的指針前加&,就成為指向行的指針,例如&a[0],由于a[0]和*(a+0)等價,因此&a[0]和&*a等價,它是指向第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;
}


定義指向“由n個元素組成的一維數組”的指針變量

#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!"; 等價于char *string; string="I love china!",把字符串第一個元素的地址賦值給字符指針變量string。

字符指針作函數參數,例如: void fun(char *p),調用時,實參可以是字符指針變量、字符數組名。

void fun(char a[ ]),調用時,實參可以是字符指針變量、字符數組名。

用字符指針作函數參數時,實參和形參的類型對應關系如下

實參 形參 實參 形參
字符數組名 字符數組名 字符指針變量 字符指針變量
字符數組名 字符指針變量 字符指針變量 字符數組名

用字符指針變量指向一個格式字符串,可以用它代替printf函數中的格式字符串,例如:

char *format;
format="a=%d,b=%f\n";
printf(format,a,b);
它相當于 printf("a=%d,b=%f\n",a,b);
因此只要改變指針變量format所指的字符串,就可以改變輸入輸出的格式。
這種printf函數稱為“可變格式輸出函數”

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

函數代碼也有存儲空間,這段存儲空間的起始地址稱為這個函數的指針。定義一個指針變量指向函數,存放某一函數的起始地址,因為此指針變量指向該函數。這就是指向函數的指針(函數指針變量)。函數指針變量定義方式:類型名 (*指針變量名)(函數參數列表);例如:int (*p)(int ,int);,定義一個指向函數的指針變量p,它可以指向函數的類型為整型且有兩個整型參數的函數。

可以通過指向函數的指針(函數指針變量)調用函數,例如

int max(int a,int b)
{...}
int (*p)(int ,int);
p=max;
這時調用函數:max(1,2);等價于(*p)(1,2);

指向函數的指針變量可以作為函數參數。例如

void fun(int (*a)(int))  {...}
int flie1(int a) {...}
int (*b)(int);
b=flie1;    //指向flie1函數
fun(b);    //指向函數的指針變量作實參
fun(flie1);        //函數名作實參,函數名代表該函數存儲空間的起始地址

8.3.6返回指針值的函數

定義返回指針值的函數的一般形式:類型名 *函數名(參數列表),

例如 int *fun(int n){...},

int *fun(int n)
{
    int *p;
    n=n+1;
    p=n;
    return(p);    //返回指針變量p所指的地址
}

8.3.7 指針數組

定義一維指針數組的一般形式:類型名 *數組名[數組長度],例如int *p[5],char *name[ ]={"abc","bcd"};

指向指針數據的指針變量的定義為 :類型名 **指針名,如char **p,p=name,其中name是指針數組的數組名,*p代表指針數組中存儲的指針,**p代表指針數組中存儲的指針所指的單元的值。

指針數組作main函數的形參:int main(int argc,char *argv[ ]),其中argc,argv,它們是程序的命令行參數(這是在系統操作命令下完成),argc是參數的個數。命令行的一般形式:命令名 參數1 參數2 ....參數n,其中命令名是可執行文件名,如,可執行文件flie1.exe,現想將兩個字符串“china”,“beijing”作為傳送給mian函數的參數,命令行可寫為 flie1 china beijing,這時argc的值為3,參數的個數為3,argv[0]指向flie1字符串。


8.3.8 動態內存分配與指向它的指針變量

內存中的動態存儲區也稱之為"棧區"。在C語言中還允許建立內存動態分配區域,以存放一些臨時用的數據,這些數據不必在程序的聲明部分定義,可以隨時開辟和關閉,這些數據是臨時存放在一個特別的自由存儲區,稱之為“堆(heap)區”

對內存的動態分配是通過系統提供的庫函數來實現的,主要有malloc,calloc,free,realloc這4個函數,在使用這些函數時,需要添加頭文件stdlib.h。

malloc函數,其函數原型為:void *malloc(unsigned int size),其作用是在內存的動態存儲區中分配一個長度為size的連續空間,形參size是無符號整型,不允許有負數,該函數的返回值是所分配的第一個字節的地址。注意,指針的基類型為void,即不指向任何類型的數據,只提供地址。如果該函數未能成功執行(例如內存空間不足),則返回空指針(NULL)。

calloc函數,其函數原型為:void *calloc(unsigned n,unsigned size),其作用是在內存的動態存儲區中分配n個長度為size的連續空間,這空間一般比較大,足可以保存一個數組。可以使用calloc為一維數組開辟動態存儲空間,n代表數組元素個數,每個元素的長度為size。這是動態數組,該函數返回的是所分配域的起始位置的指針,如果分配不成功,則返回NULL。

free函數(無返回值),其函數原型為:void free(void *p),其作用是釋放指針變量p所指向的動態空間,使這部分空間能重新被其他變量使用。p應是最近一次調用calloc和malloc函數時得到的函數返回值。例如free(p)。

realloc函數,其函數原型為:void *realloc(void *p,unsigned int size);其作用是改變通過malloc和calloc函數獲得的動態空間的大小,通過realloc函數重新分配。用法說明:用realloc函數將p所指向的動態空間的大小改變為size,p的值不變。如果重新分配不成功,返回NULL。例如:realloc(p,50);

void 指針類型,定義一個void類型的指針變量,它不指向任何類型的數據,它是空類型或不指向確定類型,例如void *p; p=&a, printf("%d",*p),錯誤,因為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

有關指針變量的歸納:

變量定義 類型表示 含義
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 結構體類型

結構體類型,是由用戶根據自己的需要建立的數據類型。聲明一個結構體類型的一般形式:struct 結構體名 {成員列表};例如:

struct student
{
  int num;
  char name[20];
  char sex;
  int ahe;
  float score;
  char addr[30];
  struct Date birthday;    //該成員的數據類型為Date結構類型
};

花括號內是該結構體的成員,成員列表也稱為“域表”,每個成員是結構體中的一個域。成員名命名規則與變量名相同。它的成員可以是另一個結構體類型。


定義結構體類型變量

在建立結構體類型時,它只相當于一個模型,并沒有定義變量,其中并無具體數據,系統不會對其分配存儲單元。定義結構體類型變量的三種方式:

1、先聲明結構體類型再定義該類型的變量 例如:struct student student1;

2、在聲明類型的同時定義變量:例如

struct student
{
    成員列表
}student1,student2;    //定義結構體變量student1和student2

3、不指定類型名而直接定義結構體類型變量

其一般形式為
struct
{
成員列表
}變量名列表;

初始化結構體變量

#include<stdi.h>
int main()
{
  struct student
  {
    long int num;
    char name[20];
    char sex;
  }a={10101,"xiaoming",'m'};    //定義結構體變量a并初始化
  return 0;
}
struct student a={10101,"xiaoming",'m'};    //定義結構體變量a并初始化
struct student a={.name="xiaoming"};    //定義結構體變量a,并對其成員a初始化,其他未被
指定初始化的數值型成員被系統初始化為0,字符型成員被系統初始化為'\0',指針型成員被系統初始化
為NULL。

引用結構體變量的成員:結構體變量名.成員名 例如studen1.num=10101;

如果成員本身又是結構體類型,則要使用若干個成員運算符,一級一級地找到最低的一級的成員。例如,student1.birthday.month(結構體變量student1中的成員birthday中的成員month)

同類的結構體變量可以互相賦值,如student1=student2;

可以引用結構體變量成員的地址,也可以引用結構體變量的地址。例如

scanf("%d",&student1.num); printf("%o",&student1); (輸出結構體變量student1的首地址)


結構體數組

定義結構體數組和定義一般數組的方式類似,它的一般形式為:結構體類型 數組名[數組長度],或struct 結構體名 { 成員列表 } 數組名[數組長度];,它的每個成員都是結構體類型。

對結構體數組初始化的形式是在定義數組后面加上:={ 初值列表 };

例如:struct person leader[3]={ {“Li”,0},{"Zhang",0},{"Sun",0}};


結構體指針

指向結構體變量的指針,稱為結構體指針,一個結構體變量的起始地址就是該結構體變量的指針。指向結構體變量的指針變量既可指向結構體變量,也可指向結構體數組中的元素。

如果p指向一個結構體變量stu,以下3種用法等價:

(1) stu.成員名(如stu.num)

(2) (*p).成員名(如(*p).num) //*p表示p指向的結構體變量

(3) p->成員名(如p->num)

(++p)->num //先使p自加1,然后得到p指向的數組元素中的num成員的值。


結構體變量和結構體變量的指針作函數參數

(1) 結構體變量的成員作實參,用法和普通的變量作實參一樣。(值傳遞)

(2)結構體變量作實參。(值傳遞)

(3)用指向結構體變量(或數組元素)的指針作實參,將結構體變量的地址傳給形參。


用指針處理鏈表

鏈表是一種常見的重要的數據結構。它是動態地進行存儲分配的一種結構。鏈表可以根據需要開辟內存單元。鏈表有一個“頭指針”變量head,它存放一個地址,該地址指向一個元素。鏈表中每一個元素稱為“結點”,每個結點都應包含兩個部分:(1)用戶需要用的實際數據;(2)下一個結點的地址。

在這里插入圖片描述


鏈表中各元素在內存中的地址可以不連續,要找某一元素,可以通過它的上一個元素中的地址尋找。如果不提供頭指針(head),則整個鏈表都無法訪問。通過結構體變量建立鏈表最合適。例如:

struct student

{int num;

float score;

struct student *next; //next是指針變量,指向結構體變量,鏈表中用來指向下一個結點

};


建立簡單的靜態鏈表:

#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

所有結點都是在程序中定義的,不是臨時開辟的,也不能用完后釋放,這種鏈表稱為“靜態鏈表”。


建立動態鏈表

動態鏈表是指在程序執行過程中從無到有地建立起一個鏈表,即一個一個地開辟結點和輸入各結點數據,并建立起前后相鏈的關系。

#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)    //定義函數,此函數返回一個指向鏈表頭的指針
{
struct student *head,p1,p2;
n=0;
p1=p2=(struct student *)malloc(LEN);    //開辟動態存儲區,把起始地址賦給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);    //開辟動態存儲區,把起始地址賦給p1
scanf("%ld,%f",&p1->num,&p1->score);
}
p2-next=NULL;
return(head);
}
int main()
{
 struct student *pt;
pt=creat();    //函數返回鏈表第一個結點的地址
printf("\nnum:%ld\nscore:%5.f\n",pt->num,pt->score);    //輸出第一個結點的成員值
return 0;
}
結果:
1 65
2 50
3 34
0 0
num:1
score:65


9.2 共用體類型

共用體結構,用同一段內存單元存放不同類型的變量(存放共用體的成員,但同時只能放一個,保留最一個存放的變量)。

定義共用體類型變量的一般形式:
union 共用體名 例如 union Data

{成員表列 { int i;

}變量表列 ; char ch;

float f;

}a,b,c;

也可以類型聲明和變量定義分開:例如:

union Data
{int i;
char ch;
float f;
};
union Data a,b,c;

共用體類型數據的特點,同一段內存可以幾種不同的類型成員,但每一瞬間只能存放其中一個成員,而不是同時存放幾個成員。

union Data
{int i;
char ch;
float f;
};
union Data a;
a.ch='a';
a.f=100.2;
a.i=97;  //內存中最終存儲97
printf("%d",a.i);    //輸出97
printf("%c",a.ch);    //輸出字符'a',ASCII碼是97
printf("%f",a.f);    //輸出實數0.0000


9.3 枚舉類型

聲明枚舉類型的一般形式為:enum[枚舉名] {枚舉元素列表};

定義枚舉類型變量:例如:(1)先聲明枚舉類型enum Weekdat: enum Weekdat {sun,mon,tue,wed,thu,fir,sat};(2)在定義變量:enum Weekdat workday; workday變量的值只能是枚舉元素列表中的枚舉元素(也稱枚舉常量)。枚舉元素列表中的枚舉元素按順序系統默認它們的值是0,1,2,3....。workday=mon;相當于workday=1;


用typedef聲明新類型名

簡單地用一個新的類型名代替原有的類型名

例如: typedef int Integer;指定用Integer代表int類型。這樣 int i;等價于 Integer i;

通過typedef可以命名一個簡單的類型名代替復雜的類型表示方法,例如

sypedef struct
  {
    long int num;
    char name[20];
    char sex;
  }student1;
聲明了一個新類型名student1,代表上面的結構體類型。
定義結構體變量: student1 xisi;

命名一個新的類型名代表數組類型,例如typedef int Num[100];//聲明Num為整型數組類型名

Num a;定義a為整型數組名,它有100個元素。

命名一個新的類型名代表指針類型,例如typedef char *String;//聲明String 為字符指針類型

String p,s[10];//定義p為字符指針變量,s為字符指針數組。一般typedef聲明新的類型名的開頭用大寫。

命名一個新的類型名代表指向函數的指針類型,typedef int (*Pointer)(); //聲明Pointer為指向函數的指針類型,該函數的返回整型。Pointer p1,p2; //p1,p2為Pointer類型的指針變量。



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

10.1 什么是文件

文件有不同的類型,在程序設計中,主要用到兩種文件:程序文件和數據文件。程序文件:包括源程序文件(后綴.c)、目標文件(后綴.obj)、可執行文件(后綴.exe)等,這些文件的內容是程序代碼。數據文件,文件的內容不是程序,而是程序運行時讀寫的數據,如在程序運行過程中輸出到磁盤(或其他設備)的數據,或在程序運行過程中供讀入的數據,如:一批學生的成績數據。

為了簡化用戶對輸入輸出設備的操作,使用戶不必去區分各種輸入輸出設備之間的區別,操作系統把各種設備都統一作為文件處理。(例如鍵盤是輸入文件、顯示屏和打印機是輸出文件)。所謂”文件“一般指存儲在外部介質上數據的集合(一批數據是以文件的形式存放在外部介質(如磁盤)上的)。

,輸入輸出是數據傳送的過程,數據如流水一樣從一處流向另一處,因此常將輸入輸出形象地稱為流,即數據流


10.2 文件分類

一個文件要有一個唯一的文件標識:包括(1)文件路徑;(2)文件名主干;(3)文件后綴。如:D: \CC \temp \file1.dat

文件的分類:根據數據的組織形式,數據文件可分為:ASCII文件和二進制文件。數據在內存中以二進制的形式存儲的,如果不加轉換地輸出到外存,就是二進制文件,可以認為它就是存儲在內存的數據的映像,所以也稱為映像文件。ASCII文件又稱為文本文件(text file),每一個字節放一個字符的ASCII代碼。

在磁盤上,字符一律以ASCII形式存儲,數值型數據既可以用ASCII也可以用二進制存儲。


10.3 文件的使用

ANSI C標準采用”文件緩沖系統“處理數據文件。所謂文件緩沖系統是指系統自動地在內存區為程序中每個正在使用的文件開辟一個文件緩沖區。文件緩沖區作用:程序數據區——輸入(或輸出)文件緩沖區——磁盤。

文件類型指針:FILE *文件名,如FILE *f1。它是指向一個文件的文件信息區(存放這文件的有關信息的地方)。

打開文件,所謂的”打開“,是指為文件建立相應的信息區(用來存放有關文件的信息)和文件緩沖區(用來暫時存放輸入輸出的數據)。

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函數,關閉文件。fclose函數調用的一般形式:fclose(文件指針),例如:fclose(fp);

對文本文件讀寫一個字符的函數

函數名 調用形式 功能 返回值
fgetc fgetc(fp) 從fp指向的文件讀入一個字符 成功(返回讀取的字符),失敗(返回-1,即文件結束標志EOF)
fputc fputc(ch,fp) 把字符ch寫到文件指針變量fp所指向的文件中 成功(返回輸出的字符),失敗(返回-1,即文件結束標志EOF)

讀寫一個字符串的函數

函數名 調用形式 功能 返回值
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);

用二進制方式向文件讀取一組數據:它們一般調用形式:讀入:fread(buffer,size,count,fp);輸出:fwrite(buffer,size,count,fp);其中buffer是地址,size:要讀寫的字節數,count:要讀寫多少個數據項(每個數據項的長度為size)。注意,打開文件時要使用二進制形式,如:fopen(”file1“,”wb“)。


文件位置標記

(1)用rewind函數使文件位置標記指向文件開頭。

(2)用fseek函數改變文件位置標記,fseek函數的調用形式為:fseek(文件指針,位移量,起始點),起始點可以用0、1、2代替,其中0代表文件開始位置,1代表當前位置,2代表文件末尾位置。起始點名字如表:

起始點 名字 用數字代表
文件開始位置 SEEK_SET 0
文件當前位置 SEEK_CUR 1
文件末尾位置 SEEK_END 2

位移量應是long型數據(在數字的末尾加一個字母L,就表示long型)

例如:fseek(fp,100L,0)將文件位置標記向前移到離文件開頭100個字節處。


文件讀寫的出錯檢測

ferror函數,檢查輸入輸出函數出錯的情況,一般調用形式:ferror(fp);出錯時返回非0值,未出錯返回0值。注意,對同一個文件每一次調用輸入輸出函數,都會產生一個新的ferror函數值。

clearerr函數,作用是使文件錯誤標志和文件結束標志為0。一般調用形式:clearerr(fp)。

Tags:C語言,編程入門,入門教程  
責任編輯:admin
請文明參與討論,禁止漫罵攻擊。 昵稱:注冊  登錄
[ 查看全部 ] 網友評論
關于我們 - 聯系我們 - 廣告服務 - 友情鏈接 - 網站地圖 - 版權聲明 - 在線幫助 - 文章列表
返回頂部
刷新頁面
下到頁底
晶體管查詢
主站蜘蛛池模板: 昭通市| 墨江| 夏津县| 宁化县| 高雄县| 安平县| 隆尧县| 寻甸| 蕲春县| 静乐县| 永仁县| 莱芜市| 霸州市| 天柱县| 芮城县| 陆川县| 刚察县| 资溪县| 张家界市| 金塔县| 化州市| 康保县| 香河县| 固安县| 浑源县| 保亭| 博乐市| 泸州市| 甘德县| 罗定市| 临湘市| 揭西县| 明光市| 吉林省| 金坛市| 吉木萨尔县| 铜山县| 南郑县| 烟台市| 楚雄市| 奉新县|