目錄
一、數據類型和表達式
1.基本類型C語言中二進製數、八進製數和十六進製數的表示:
- 二進製:二進製由 0 和 1 兩個數字組成,使用時必須以0b或0B(不區分大小寫)開頭。例如:0b101、0B001
注意:標準的C語言並不支持二進製寫法,有些編譯器自己進行了擴展,才會支持二進製數字- 八進製:八進製由 0~7 八個數字組成,使用時必須以0開頭(注意是數字 0,不是字母 o),例如:015(十進製的13)、0177777(十進製的65535)
- 十六進製:十六進製由數字 0~9、字母 A~F 或 a~f(不區分大小寫)組成,使用時必須以0x或0X(不區分大小寫)開頭,例如:0X2A(十進製的43)、0xffff(十進製的65535)
- 整型(int)
- 字符型(char)
- 實型(浮點型)
- 單精度型(float)
- 雙精度型(double)
- 枚舉類型
下麵是詳細的類型說明:
類型 | 類型說明符 | 字節 | 數字範圍 |
---|---|---|---|
字符型 | char | 1 | C字符集 |
基本整型 | int | 4 | -32768~32767 |
短整型 | short int | 2 | -32768~32767 |
長整型 | long int | 4 | -214783648~-214783647 |
無符號整型 | unsigned int | 4 | 0~65535 |
無符號長整型 | unsigned long | 4 | 0~4294967295 |
單精度實型 | float | 4 | 10-38~1038 |
雙精度實型 | double | 8 | 10-308~10-308 |
1)數組類型
數組:按序排列的同類數據元素的集合
- 一維數組:類型說明符 數組名[數組長度];
- 二維/多維數組:類型說明符 數組名[行數][列數]; 多維數組以此類推
- 字符數組:char 數組名[數組長度];C語言沒有字符串類型,字符串通常用字符數組表示
數組定義(yi) :類型說明符 數組名[長度];
數組引用:
一維數組數組名[索引]; 二維數組數組名[行索引][列索引];
注:索引都是從(cong) 0開始
數組賦值:
1.在定義(yi) 的時候賦初值:int a[10]={1,2,3,4,5,6,7,8,9,10};或int a[]={1,2,3,4,5,6,7,8,9,10};
2.先定義(yi) ,再賦值:int a[10];a = {1,2,3,4,5,6,7,8,9,10};
字符數組賦值:
1.char Hello[] = {'H','e','l','l','o'};
2.char Hello[] = "Hello";
注:字符數組第二種賦值方式比第一種方式多占一個(ge) 字符,因為(wei) 第二種方式會(hui) 在字符數組中結尾添加一個(ge) \0作為(wei) 字符串結束符提示:數組賦值時,如果給定值數量小於(yu) 數組長度,係統默認填充0
示例:
#include <stdio.h> int main() { //=====================一維數組=============== int a[5] = { 1, 2}; // a={1,2,0,0,0} int b[] = { 1, 2, 3, 4, 5};// b={1,2,3,4,5} int c[10];// 沒有賦初始值係統會(hui) 自動賦值一個(ge) 無意義(yi) 的數字,可以自行printf輸出查看 printf("a第二個(ge) 元素:%d\nb第一個(ge) 元素:%d\n", a[1], b[0]); //=====================二維數組=============== int aa[2][3] = { 1, 2, 3, 4, 5, 6};// C語言是按行編址,所以可以這樣賦值 int bb[2][3] = { { 1, 2, 3}, { 4, 5, 6} }; //aa和bb這兩(liang) 個(ge) 數組是相同的 printf("aa第1行第1列元素:%d\n", aa[0][0]); printf("bb第1行第2列元素:%d\n", bb[0][1]); //=====================字符串=============== char name[8] = { 'x', 'i', 'a', 'o', 'm', 'i', 'n', 'g'}; char name2[] = "xiaohong"; printf("第一個(ge) 名字:%s第二個(ge) 名字:%s", name, name2); return 0; }
2)結構體(ti) 類型
3)共用體(ti) 類型
3.常量C語言中常量的定義(yi) 有兩(liang) 種方式,假如我們(men) 要定義(yi) 一個(ge) int類型的常量TEMP,值為(wei) 1:
- 預定義命令: #define TEMP = 1
- const關鍵字:const int TEMP = 1
1)算術運算表達式:
- 加:+
- 減:-
- 乘:*
- 除:/
- 取餘:%
- 自增:++
- 自減:--
注意:自增和自減跟賦值運算結合的時候如果運算符在左邊,會(hui) 先進行自增或自減運算,請看下麵例子:
void test1(){ int a = 1; int b = ++a; //結果是b=2 } void test2(){ int a = 1; int b = a++; //結果是b=1 }
2)關(guan) 係運算表達式:
- 等於(yu) :==
- 大於(yu) :>
- 大於(yu) 等於(yu) :>=
- 小於(yu) :<
- 小於(yu) 等於(yu) :<=
- 不等於(yu) :!=
3)邏輯運算符:
C語言中非0為(wei) 真
- 與:&&
- 或:||
- 非:!
4)位運算符:
- 位與:&
對每一位進行邏輯與運算,0表示假,1表示真:0011 & 1111 = 0011 - 位或:|
對每一位進行邏輯或運算,0表示假,1表示真:0011 | 1111 =1111 - 位非:~
對每一位進行邏輯非運算,0表示假,1表示真:~1111 =0000 - 位異或:^
對每一位進行邏輯異或運算,0表示假,1表示真:0011 ^ 1111 =1100 - 左移:<<
高位溢出丟棄,低位不足補0:01100100 << 2 = 10010000 - 右移:>>
- 正數:高位補0,低位溢出舍去:01111111 >> 4 = 00000111
- 負數:高位補1,低位溢出舍去:11111111 >> 4 = 11111111
二、C語言的語句
1.表達式語句定義(yi) :由表達式和分號組成的語句:x + y = z;
2.函數調用語句定義(yi) :函數名、實際參數和分號組成:函數名(參數);
3.控製語句1)條件判斷語句:
- if語句:單條件判斷語句
// 用法 if (條件表達式){ // 條件滿足 要執行的語句 }
- if…else…語句:條件分支語句
// 用法 if (條件表達式){ // 條件滿足 要執行的語句 }else{ // 條件不滿足 要執行的語句 }
- if…else if…else…語句:多條件分支語句
// 用法 if (條件表達式1){ // 滿足條件表達式1 要執行的語句; }else if (條件表達式2) { // 滿足條件表達式2 要執行的語句; }else if (條件表達式3) { // 滿足條件表達式3 要執行的語句; } ... else if (條件表達式n) { // 滿足條件表達式n 要執行的語句; }else{ // 所有條件表達式都不滿足 要執行的語句; }
- switch語句:開關(guan) 語句,一般配合case關(guan) 鍵字使用
switch(表達式) { case 常量1: // 如果表達式的值等於(yu) 常量1,執行下麵的語句1 語句1 ; break; case 常量2: // 如果表達式的值等於(yu) 常量2,執行下麵的語句2 語句2; break; ... case 常量n: // 如果表達式的值等於(yu) 常量n,執行下麵的語句n 語句n; break; default: // 默認執行的語句,如果沒有通過上麵的開關(guan) 語句退出,就會(hui) 執行下麵的語句n+1 語句n+1; //break; // default可以省略break;因為(wei) 它本身就是最後執行,執行完就會(hui) 退出開關(guan) 語句。 }
注:switch語句如果沒有break會(hui) 一直向下執行直到結束。
2)循環執行語句:
- for語句
結構:
for (表達式1;表達式2;表達式3){
語句;
}
循環邏輯:
step1:先執行表達式1
step2:然後執行表達式2,
step3:如果step2結果為(wei) 真,執行語句,否則退出循環
step4:如果step3沒有退出循環,則執行表達式3
step5:重複執行step2-step4直至循環退出
//用法 for (循環變量賦初值;循環條件;循環變量增量){ 執行語句; }
- while語句
條件循環語句,當滿足循環條件的情況下循環執行
//用法 while (循環條件){ 執行語句; }
- do while語句
與(yu) while循環的區別:do…while會(hui) 先執行一遍循環體(ti) 裏麵的語句,再進行條件判斷,也就是說,do…while至少會(hui) 執行一次循環體(ti) 中的語句
//用法 do{ 執行語句; }while (循環條件);
3)轉向語句:
- continue:continue語句一般用於(yu) 循環結構中,作用是跳過當次循環,當循環語句執行到continue時,不會(hui) 繼續向下執行,會(hui) 跳過當次循環,直接執行下一次循環。
- break:中斷語句,一般用於(yu) 循環結構中,作用是終止循環,當執行到break語句時,會(hui) 立即退出循環。
- return:跳出函數語句,用於(yu) 跳出函數並返回一個(ge) 值。
- goto:強製轉向語句(不推薦使用)
//用法 int main(){ int a=1; int b=5; loop: if (a<b){ printf("%d\n",a); a++; goto loop; } return 0; }
4.複合語句輸出結果:
1
2
3
4
說明:goto語句一般用於(yu) 跟if語句結合形成循環結構,需要先定義(yi) 一個(ge) 標誌符(loop),表示goto轉向到哪個(ge) 地方。
定義(yi) :將多個(ge) 語句用大括號括起來組成一個(ge) 複合語句
{ int a = 1; a++; int b = a + 1; } 5.空語句
定義(yi) :隻有分號組成的語句稱為(wei) 空語句
1)海倫(lun) 公式
根據三角形的三條邊求出麵積:S= p ( a − p ) ( b − p ) ( c − p ) \sqrt{p(a-p)(b-p)(c-p)} p(a−p)(b−p)(c−p)
S:麵積 p:周長的1/2 a,b,c:三角形的三條邊長
#include "stdio.h" #include "math.h" int main(){ float a; float b; float c; float area; float p; printf("請輸入構成三角形的三條邊的長度:"); scanf("%f,%f,%f", &a, &b, &c); p = (a+b+c)/2; area = sqrt(p*(a-p)*(b-p)*(c-p)); printf("三角形麵積是:%f",area); return 0; }
2)一元二次方程
#include <stdio.h> #include "math.h" int main() { float a,b,c; float p,x1,x2; printf("請輸入一元二次方程的3個(ge) 係數a,b,c:ax^2+bx+c=0(a≠0)\n"); scanf("%f,%f,%f",&a,&b,&c); p = sqrt(b*b-4*a*c); x1 = (-b+p)/(2*a); x2 = (-b-p)/(2*a); printf("方程的解為(wei) :x1=%f,x2=%f",x1,x2); return 0; }
三、函數
1.函數的概念函數是實現了某種功能的代碼塊
- 庫函數:由C係統提供,用戶無須定義,也不必在程序中作類型說明,隻需在程序前包含有該函數原型的頭文件即可在程序中直接調用。
- 用戶定義函數:由用戶按需要寫的函數。對於用戶自定義函數,不僅要在程序中定義函數本身,而且在主調函數模塊中還必須對該被調函數進行類型說明,然後才能使用。
- 無參函數:
類型標識符 函數名() { 聲明部分; 語句; }
- 有參函數:
類型標識符 函數名(形參1,形參2,形參3...形參n) { 聲明部分; 語句; }
- 示例:下麵定義(yi) 了兩(liang) 個(ge) 函數,第一個(ge) HelloWorld是無參函數,功能是輸出一個(ge) "Hello World!"字符串,第二個(ge) FindMax是有參函數,接收兩(liang) 個(ge) int類型的參數,返回兩(liang) 個(ge) 數中最大的那個(ge) 數
//void HelloWorld(); //int FindMax(int a,int b); //上麵是對函數進行聲明,函數的調用必須先定義(yi) ,否則編譯不通過,如果定義(yi) 在調用函數之後,需要先聲明 void HelloWorld() { printf("Hello World!"); } int FindMax(int a, int b) { int max; max = a >= b ? a : b; return max; } int main(){ HelloWorld(); int a = 5; int b = 10; int c; c = FindMax(a, b); printf("\n最大數為(wei) :%d\n", c); return 0; }3.函數的參數
- 形參:形參出現在函數定義(yi) 中,在整個(ge) 函數體(ti) 內(nei) 都可以使用,離開該函數則不能使用。
- 實參:實參在主調函數中,是調用函數時傳(chuan) 遞的參數。
- 參數傳(chuan) 遞:函數的參數由主調函數的實參傳(chuan) 遞給被調函數的形參,因此實參與(yu) 形參的順序、類型必須保持一致。
函數返回值是一個(ge) 類型與(yu) 函數聲明中定義(yi) 的返回類型相同的值,如果函數聲明中沒有定義(yi) 返回類型,則默認為(wei) int 類型。
例如,下麵是一個(ge) 簡單的 C 函數,它返回一個(ge) 整數值:
int max(int a, int b) { if (a > b) { return a; } else { return b; } }
在這個(ge) 例子中,函數 max() 定義(yi) 了兩(liang) 個(ge) int 類型的參數 a 和 b,並在函數體(ti) 內(nei) 部判斷它們(men) 的大小關(guan) 係。如果 a 大於(yu) b,則函數返回 a 的值;否則,函數返回 b 的值。
另外,如果函數聲明中定義(yi) 了 void 類型的返回值,則表示函數不會(hui) 返回任何值。在這種情況下,函數體(ti) 內(nei) 部不能使用 return 語句返回值。例如:
void print_hello() { printf("Hello, world!\n"); }
在這個(ge) 例子中,函數 print_hello() 不需要返回任何值,因此聲明中定義(yi) 的返回類型為(wei) void。
5.函數的調用6.全局變量與局部變量
- 調用的一般形式為:函數名(實參);
- 被調用函數的聲明和函數原型:在主調函數中調用某函數之前應對該被調函數進行說明(聲明),這與使用變量之前要先進行變量說明是一樣的。在主調函數中對被調函數作說明的目的是使編譯係統知道被調函數返回值的類型,以便在主調函數中按此種類型對返回值作相應的處理。
其一般形式為: 類型說明符 被調函數名(類型 形參,類型 形參...);或 類型說明符 被調函數名(類型,類型...);
作用域:表示一個(ge) 變量起作用的範圍,例如:
{ int a = 1; //a的作用域就是這個(ge) 代碼塊,在代碼塊外部就無法訪問變量a }
1)全局變量
- 定義(yi) :全局變量也稱為(wei) 外部變量,它是在函數外部定義(yi) 的變量。它不屬於(yu) 哪一個(ge) 函數,它屬於(yu) 一個(ge) 源程序文件。其作用域是整個(ge) 源程序。
- 使用:在全局變量定義(yi) 之前的函數中使用全局變量,需要使用關(guan) 鍵字extern做全局變量說明,聲明某個(ge) 變量是全局變量,然後才能使用;在全局變量定義(yi) 之後的函數中使用全局變量,可以省略extern關(guan) 鍵字,不做全局變量說明也可以使用。
int a = 5; // 此處a為(wei) 全局變量 int main(void){ int extern a; // 全局變量說明,聲明a是一個(ge) 全局變量,此處在a定義(yi) 之後,可以省略該說明 printf("%d", a); //輸出結果為(wei) 5 }
2)局部變量
- 定義(yi) :局部變量也稱為(wei) 內(nei) 部變量。局部變量是函數內(nei) 部定義(yi) 的變量,作用域僅(jin) 限於(yu) 函數內(nei) 部,局部變量隻能在函數內(nei) 部使用,函數外部無法訪問。
int main(void){ int a = 5; // 這是一個(ge) 局部變量,a的作用域範圍是main函數內(nei) ,在函數外無法使用 print("%d", a); a++; } print("%d", a);//全局作用域內(nei) 找不到變量a,編譯不通過7.靜態變量與(yu) 寄存器變量
1)靜態變量
- 定義(yi) :靜態變量是在函數調用結束後不消失而保留原值的變量,如果在一個(ge) 函數調用結束後,希望它保留某個(ge) 變量的值,就把這個(ge) 變量用static關(guan) 鍵字聲明為(wei) 靜態變量。
// 定義(yi) 一個(ge) 自增函數,初始化局部靜態變量a為(wei) 0,每調用一次,a自增1 int Add() { static int a = 0; a++; return a; } int main(){ print("%d", Add());// 輸出結果為(wei) 1 print("%d", Add());// 輸出結果為(wei) 2 return 0; }
2)寄存器變量
- 定義(yi) :寄存器變量是放在CPU寄存器中的變量,CPU寄存器可以理解為(wei) CPU的內(nei) 存空間,就像是電腦的內(nei) 存一樣,在寄存器中運算速度非常快。使用register關(guan) 鍵字聲明。
- 注意:
- 隻有局部自動變量(非靜態變量)和形參可以作為(wei) 寄存器變量
- 一個(ge) 計算機係統中的寄存器數目有限,不能定義(yi) 任意多個(ge) 寄存器變量
- 局部靜態變量不能定義(yi) 為(wei) 寄存器變量
#include "stdio.h" // 這是一個(ge) 計算n的階乘的函數,將局部變量i和f聲明為(wei) 寄存器變量 int fac(int n) { register int i, f = 1; for (i = 1; i <= n; i++) { f = f * i; } return f; } int main() { int i; for (i = 0; i <= 5; i++) { printf("%d!=%d\n", i, fac(i)); } return 0; }8.預處理命令
預處理是指在進行編譯的第一遍掃描(詞法掃描和語法分析)之前所作的工作。預處理是C語言的一個(ge) 重要功能,它由預處理程序負責完成。
C語言提供了多種預處理功能,如宏定義(yi) 、文件包含、條件編譯等。
1)宏定義
C語言可以使用#define定義(yi) 宏(類似常量),程序在編譯處理時會(hui) 把源程序中所有的宏名替換成宏定義(yi) 的結果。
宏定義(yi) 是由源程序中的宏定義(yi) 命令完成的。宏代換是由預處理程序自動完成的。
- 無參宏定義:#define 標識符 字符串 (“字符串”可以是常數、表達式、格式串等)
所有出現在源程序中的宏名都會(hui) 替換成宏定義(yi) 的字符串
例如:#include <stdio.h> #define PI 3.1415926 #define M (a+a) int main(void) { double a = 1.0; double b; b = 2*M + PI; // 等同於(yu) 2*(a+a) + 3.1415926 printf("%f", b); return 0; }
- 帶參宏定義(yi) :#define 宏名(形參1,形參2,形參3,...形參n) 字符串 (“字符串”可以是常數、表達式、格式串等)
類似於(yu) 定義(yi) 一個(ge) 匿名函數
>#include <stdio.h> #define S(x,y) x*y // S表示矩形麵積,x,y分別表示長寬 int main(void) { double a = 3.0,b = 4.0; double s; s = S(a,b); // 等同於(yu) a*b printf("%f", s); return 0; }
2)文件包含
文件包含命令的功能是把指定的文件插入該命令行位置取代該命令行,從(cong) 而把指定的文件和當前的源程序文件連成一個(ge) 源文件。
文件包含的形式為(wei) :#include "文件名"或#include <文件名>
上麵兩(liang) 種形式的區別:使用尖括號表示在包含文件目錄中去查找(包含目錄是由用戶在設置環境時設置的),而不在源文件目錄去查找;使用雙引號則表示首先在當前的源文件目錄中查找,若未找到才到包含目錄中去查找。
3)條件編譯
預處理程序提供了條件編譯的功能。可以按不同的條件去編譯不同的程序部分,因而產(chan) 生不同的目標代碼文件。
條件編譯有以下三種形式:
- 第一種:如果標識符已被 #define命令定義過則對程序段 1 進行編譯;否則對程序段 2 進行編譯。
#ifdef 標識符 程序段 1 #else 程序段 2 #endif
- 第二種:如果標識符未被#define命令定義(yi) 過則對程序段 1 進行編譯,否則對程序段 2 進行編譯。
#ifndef 標識符 程序段 1 #else 程序段 2 #endif
- 第三種:常量表達式的值為(wei) 真(非 0),則對程序段 1 進行編譯,否則對程序段 2 進行編譯。
#if 常量表達式 程序段 1 #else 程序段 2 #endif
四、指針
1.變量的指針&指針變量指針是指存儲(chu) 單元的地址,例如定義(yi) 一個(ge) 變量int a = 1;指向變量a的指針就是a在內(nei) 存中的地址。
- 變量的指針:就是變量的內存地址。
- 指針變量:存放變量地址的變量。
解釋:比如有個變量a,變量a的地址是p,p就是變量a的指針。現在我們再假設一個變量b,然後把p賦值給變量b,那麽變量b就是一個指針變量(字麵意思,存放指針的變量)。
1)指針變量的定義
類型說明符* 變量名;
類型說明符表示這個(ge) 指針指向的變量類型,換句話說這個(ge) 指針變量的值必須是一個(ge) 什麽(me) 類型的變量的地址
例如:
int* p1; //定義(yi) 一個(ge) int類型的指針變量,指向的變量類型也必須是int char* p2; //定義(yi) 一個(ge) char類型的指針變量,指向的變量類型也必須是char double* p3; //定義(yi) 一個(ge) double類型的指針變量,指向的變量類型也必須是double 2)指針的操作
- &:取地址運算符
&變量名表示取變量的地址,就是獲取變量的指針
int a = 123; int* p = &a; //取變量a的地址賦值給指針變量p
- *:指針運算符(或稱“間接訪問” 運算符)
*指針變量表示取指向的變量的值
int a = 123; int* p = &a; //取變量a的地址賦值給指針變量p printf("%d",*p); //輸出123,*p表示取a的值
1)數組的指針
數組的指針是指數組的首地址。
名詞解釋:一個(ge) 數組是由連續的一塊內(nei) 存單元組成的。數組名就是這塊連續內(nei) 存單元的首地址,也是數組中第一個(ge) 元素的地址。int array[] = { 1,2,3,4,5,6}; int* pA = array; // 數組名就是數組的指針 int* pB = &array[0]; // 數組的第一個(ge) 元素的地址就是數組的指針
指針pA和指針pB是相等的
2)指針數組
3.字符串的指針一個(ge) 數組的元素值為(wei) 指針則是指針數組。
定義(yi) 方式:類型說明符* 數組名[數組長度](跟普通數組定義(yi) 方式相同,唯一區別是*)int main() { int a=1,b=2,c=3,d=4,e=5; int* Int[5] = { &a,&b,&c,&d,&e}; // 這是一個(ge) 整型指針數組 // 字符串在C語言中是字符數組,所以一個(ge) 字符串相當於(yu) 一個(ge) 字符數組,字符串本身就等於(yu) 字符數組的指針(首地址) const char* String[] = { "Test1","Test2","Test3","Test4","Test5"}; // 這是一個(ge) 字符型的指針數組 for (int i = 0; i < 5; ++i) { printf("%p\n",String[i]); // 這裏輸出的就是每個(ge) 字符串的指針 } return 0;
C語言中是沒有字符串類型的,C語言中的字符串都是用字符數組進行存儲(chu)
字符串的指針就是字符數組的指針,也就是字符數組的首地址
C語言字符串的兩(liang) 種定義(yi) 形式:
- 數組形式:char string[] = {'H','e','l','l','o','\0'};或char string[] = "Hello";
- 指針形式:char* string = "Hello";(等價於{'H','e','l','l','o','\0'})
1)函數的指針
在C語言中,一個(ge) 函數總是占用一段連續的內(nei) 存區,而函數名就是該函數所占內(nei) 存區的首地址(函數指針)。
- 函數指針的定義:類型說明符 (*指針變量名)(實參類型);
int (*p)(); // 定義(yi) 一個(ge) 函數指針p int Function(){ printf("test"); } p = Function; // 將Function函數的入口地址賦值給函數指針變量p
注意:函數指針的定義(yi) 區別於(yu) 變量指針
- 函數指針的調用:(*指針變量名) (實參表);
int FindMax(int a, int b){ return a > b ? a : b; } int main() { int (*p)(int, int) = FindMax; int max = p(5,10); printf("%d",max); return 0; }
2)指針型函數
函數類型是指針的函數就是指針型函數(函數類型是指函數返回值的類型)
定義(yi) :類型說明符* 函數名(參數){ 執行語句; return 對應類型的指針; }
例:下麵定義(yi) 了指針型函數,作用是隨機生成一個(ge) 數組,返回數組的指針
#include <stdio.h> #include <stdlib.h> #include <time.h> int* GetNumber(){ static int array[10]; srand((unsigned)time(NULL)); for (int i = 0; i < 10; ++i) { array[i] = rand(); printf("%d\n",array[i]); } return array; } int main() { int* p = GetNumber(); printf("===================================\n"); for (int i = 0; i < 10; ++i) { printf("%d\n",p[i]); } 5.指向指針的指針
指向指針的指針,就是字麵意思,假如有個(ge) 變量a,變量a的指針用p1表示,將p1賦值給一個(ge) 變量b,變量b的指針用p2表示,現在將p2賦值給一個(ge) 變量c,變量c就是指向指針的指針。
int a = 2333; int* b = &a; int** c = &b;
要訪問指向指針的指針的值,要使用**,如上麵的指針c,訪問方式為(wei) **c
五、結構體和共用體
1.結構體結構體(ti) 跟一些麵向對象的語言(Python、C#、Java)中的類概念相似,就是一組數據由多個(ge) 成員數據組成,成員數據可以是基本類型或者構造類型,在使用結構體(ti) 之前必須先進行定義(yi) 。
結構體(ti) 是由多個(ge) 不同數據類型的成員組成的數據類型。結構體(ti) 中的每個(ge) 成員可以有不同的數據類型和命名。使用結構體(ti) 可以將多個(ge) 不同數據類型的信息組合成一個(ge) 單一的邏輯單元,從(cong) 而方便地進行操作。
1)結構體的定義
- 定義結構體關鍵字:struct
- 定義形式:struct 結構名 {成員數據};
// 下麵定義(yi) 了一個(ge) 名為(wei) Person的結構體(ti) ,Person包含有一個(ge) 人的姓名、年齡、性別、身高、住址信息 struct Person{ char* name; int age; char sex; double height; char address[200]; };
2)結構體(ti) 的用法
- 結構體(ti) 成員變量的表示方法:結構名.變量名或(*結構指針).變量名/(*結構指針)->變量名
struct Person{ char* name; int age; char sex; double height; char address[200]; }; int main() { struct Person man; // 結構體(ti) 變量實例化 struct Person woman; // 結構體(ti) 變量實例化 struct Person* pW = &woman; // 實例化一個(ge) 結構體(ti) 指針變量 man.name; // 結構體(ti) 變量直接表示 man.sex; (*pW).name; // 結構體(ti) 指針變量表示 pW->sex; // 結構體(ti) 指針變量表示 return 0; }
- 結構體(ti) 變量的賦值:直接給成員變量賦值,注意數組類型不能直接賦值。
#include <stdio.h> #include <string.h> // 下麵定義(yi) 了一個(ge) 名為(wei) Person的結構體(ti) ,Person包含有一個(ge) 人的姓名、年齡、性別、身高、住址信息 struct Person{ char* name; int age; char sex; float height; char address[200]; }; int main() { struct Person man; struct Person woman; struct Person* pW = &woman; man.name = "小明"; // 結構體(ti) 變量賦值 man.sex = 'M'; man.age = 18; man.height = 1.78f; strcpy(man.address,"四川省成都市"); (*pW).name = "小紅"; // 結構體(ti) 變量賦值 (*pW).sex = 'W'; pW->age = 19; pW->height = 1.68f; strcpy(pW->address,"四川省綿陽市"); // 數組類型不能直接賦值 printf("姓名:%s\n年齡:%d\n性別:%c\n身高:%.2fm\n地址:%s\n",man.name,man.age,man.sex,man.height,man.address); printf("==============================================================================================\n"); printf("姓名:%s\n年齡:%d\n性別:%c\n身高:%.2fm\n地址:%s\n",woman.name,woman.age,woman.sex,(*pW).height,pW->address); return 0; }2.共用體(ti) (聯合體(ti) )
共用體(ti) 是一種特殊的結構體(ti) ,其所有成員共享相同的內(nei) 存空間。共用體(ti) 中的每個(ge) 成員可以有不同的數據類型,但是它們(men) 共享相同的內(nei) 存空間,因此隻能同時存在一個(ge) 成員的值。共用體(ti) 的主要用途是在不同的數據類型之間進行類型轉換或節省內(nei) 存空間。
1)共用體的定義
- 定義結構體關鍵字:union
- 定義形式:union 共用體名 {成員數據};
#include <stdio.h> #include <string.h> union data { int i; float f; char str[20]; }; int main() { union data mydata; // 實例化一個(ge) 共用體(ti) 變量 mydata.i = 10; printf("mydata.i = %d\n", mydata.i); mydata.f = 3.14f; printf("mydata.f = %f\n", mydata.f); strcpy(mydata.str, "Hello"); printf("mydata.str = %s\n", mydata.str); return 0; }
在這個(ge) 例子中,我們(men) 定義(yi) 了一個(ge) 名為(wei) data的共用體(ti) ,包含一個(ge) 整型變量i、一個(ge) 浮點型變量f和一個(ge) 字符數組str。在main函數中,我們(men) 定義(yi) 了一個(ge) mydata的共用體(ti) 變量,可以用來存儲(chu) int、float或char類型的數據。
由於(yu) 所有成員變量共享同一塊內(nei) 存空間,因此在設置mydata.f和mydata.str時,mydata.i的值被覆蓋了。這也是共用體(ti) 的一個(ge) 特點:在任意時刻,隻能有一個(ge) 成員變量是有效的。
2)共用體的用法
主要用途:在不同的數據類型之間進行類型轉換或節省內(nei) 存空間。
#include <stdio.h> #include <string.h> union data { int i; float f; char* s; char c; }; int main() { union data temp; // 定義(yi) 一個(ge) 共用體(ti) temp temp.i = 10; printf("temp = %d\n",temp.i); printf("data中i的內(nei) 存地址:%p\n",&temp.i); printf("data中f的內(nei) 存地址:%p\n",&temp.f); printf("data中s的內(nei) 存地址:%p\n",&temp.s); printf("data中c的內(nei) 存地址:%p\n",&temp.c); // 可以看出共用體(ti) 的所有成員指向的是同一塊內(nei) 存空間 printf("=========================================================\n"); temp.s = "測試"; printf("temp = %s\n",temp.s); printf("data中i的內(nei) 存地址:%p\n",&temp.i); printf("data中f的內(nei) 存地址:%p\n",&temp.f); printf("data中s的內(nei) 存地址:%p\n",&temp.s); printf("data中c的內(nei) 存地址:%p\n",&temp.c); printf("=========================================================\n"); temp.f = 3.14159f; printf("temp = %f\n",temp.f); printf("data中i的內(nei) 存地址:%p\n",&temp.i); printf("data中f的內(nei) 存地址:%p\n",&temp.f); printf("data中s的內(nei) 存地址:%p\n",&temp.s); printf("data中c的內(nei) 存地址:%p\n",&temp.c); printf("=========================================================\n"); //通過上麵的例子,如果把temp看做一個(ge) 沒有定義(yi) 類型的變量,那麽(me) 他就是個(ge) 可變類型的變量 return 0; } 3.枚舉(ju)
枚舉(ju) (Enumeration)是一種自定義(yi) 的數據類型,它允許定義(yi) 一組命名的常量。枚舉(ju) 類型的變量隻能賦值為(wei) 枚舉(ju) 列表中的一個(ge) 值,這些值被稱為(wei) 枚舉(ju) 常量。枚舉(ju) 類型是一種非常方便的方式來組織和描述常量。
1)枚舉的定義
- 定義枚舉關鍵字:enum
- 定義枚舉的形式:enum 枚舉名稱 {枚舉常量列表};(枚舉常量的值被認為是int類型或者unsigned int類型,默認枚舉變量值從0開始遞增)
enum color { RED, GREEN, BLUE }; /*上麵定義(yi) 了一個(ge) 三種顏色的枚舉(ju) ,三種枚舉(ju) 默認值為(wei) RED=0,GREEN=1,BLUE=2*/ // 下麵定義(yi) 一個(ge) 性別的枚舉(ju) ,並給枚舉(ju) 值進行自定義(yi) enum sex { MAN = 1, WOMAN = 2 }
2)枚舉(ju) 的用法
枚舉(ju) 常用來定義(yi) 一組常量選項
#include <stdio.h> #include <string.h> enum week { Mon, Tue, Wed, Thu, Fri, Sat, Sun }; int main() { enum week today; today = Mon; switch (today) { case Mon: printf("今天是周一"); break; case Tue: printf("今天是周二"); break; case Wed: printf("今天是周三"); break; case Thu: printf("今天是周四"); break; case Fri: printf("今天是周五"); break; case Sat: printf("今天是周六"); break; case Sun: printf("今天是周日"); break; } return 0; } 4.動態內(nei) 存分配
C語言常用的內(nei) 存管理函數有四個(ge) :malloc、calloc、realloc、free
其中申請空間的函數是malloc、calloc;重新調整空間大小的函數是realloc;釋放空間的函數是free
1)malloc
作用:用於(yu) 在堆上分配指定大小的內(nei) 存空間,內(nei) 容隨機,函數原型:void* malloc(size_t size);
參數:
- size:分配空間的大小(字節)
返回值:分配的內(nei) 存空間的首地址,分配失敗返回NULL空指針
注意:返回值類型為(wei) void*,使用時需要轉換成對應類型
下麵是一個(ge) 例子:分配一塊空間存儲(chu) 指定個(ge) 數的數字,並對數字求和
#include <stdio.h> #include <stdlib.h> int main() { int* ptr; // 定義(yi) 一個(ge) 指針變量 int n, sum = 0; // 初始化元素個(ge) 數與(yu) 元素總和 printf("輸入要保存的元素個(ge) 數: "); scanf("%d", &n); ptr = (int*) malloc(n * sizeof(int)); // 分配一塊足夠存儲(chu) n個(ge) int類型數字的內(nei) 存空間,將指針強製轉換為(wei) int類型 if(ptr == NULL) { printf("內(nei) 存空間分配失敗!\n"); exit(1); } printf("輸入保存的元素:\n"); for(int i = 0; i < n; i++) { scanf("%d", &ptr[i]); sum += ptr[i]; } printf("所有元素累加總和為(wei) :%d\n", sum); free(ptr);// 釋放內(nei) 存空間ptr return 0; }
2)calloc
作用:用於(yu) 在堆上分配指定數量和大小的內(nei) 存空間,內(nei) 容初始化為(wei) 0。
其函數原型為(wei) :void* calloc(size_t num, size_t size);
參數:
- num:分配空間塊數(需要分配多少塊空間)
- size:每塊空間的大小(字節)
返回值:分配的內(nei) 存空間的首地址,分配失敗返回NULL空指針
注意:返回值類型為(wei) void*,使用時需要轉換成對應類型
同樣使用上麵的例子:
#include <stdio.h> #include <stdlib.h> int main() { int* ptr; // 定義(yi) 一個(ge) 指針變量 int n, sum = 0; // 初始化元素個(ge) 數與(yu) 元素總和 printf("輸入要保存的元素個(ge) 數: "); scanf("%d", &n); ptr = (int*) calloc(n, sizeof(int)); // 分配n塊足夠存儲(chu) 1個(ge) int類型數字的內(nei) 存空間,將指針強製轉換為(wei) int類型 if(ptr == NULL) { printf("內(nei) 存空間分配失敗!\n"); exit(1); } printf("輸入保存的元素:\n"); for(int i = 0; i < n; i++) { scanf("%d", &ptr[i]); sum += ptr[i]; } printf("所有元素累加總和為(wei) :%d\n", sum); free(ptr);// 釋放內(nei) 存空間ptr return 0; }
3)realloc
作用:用於(yu) 重新分配已分配內(nei) 存的大小。其函數原型為(wei) :void* realloc(void* ptr, size_t size);
參數:
- ptr:原內存空間地址
- size:重新分配內存空間大小
返回值:分配的內(nei) 存空間的首地址,分配失敗返回NULL空指針
注意:返回值類型為(wei) void*,使用時需要轉換成對應類型說明:realloc重新分配是在原地址的基礎上進行調整,如果是擴大空間大小,當新的空間大小超過了原空間所能擴展的範圍(比如a空間占了4個(ge) 字節,現在要把a空間擴展到8個(ge) 字節,而在這一塊連續的內(nei) 存中,第7個(ge) 字節已經被分配出去了,那麽(me) 這塊空間最大隻能是6個(ge) 字節了),係統會(hui) 重新找一塊足夠大的空間來作為(wei) 新空間,然後將原本空間中的數據拷貝過來,釋放原本的空間,也就是指針會(hui) 進行改變,值不會(hui) 發生變化;如果是縮小空間大小,就會(hui) 釋放原空間調整之後的內(nei) 存空間。
同樣使用上麵例子做修改:
#include <stdio.h> #include <stdlib.h> int main() { int* ptr; // 定義(yi) 一個(ge) 指針變量 int n,m, sum = 0; // 初始化元素個(ge) 數與(yu) 元素總和 printf("輸入要保存的元素個(ge) 數: "); scanf("%d", &n); ptr = (int*) calloc(n, sizeof(int)); // 分配n塊足夠存儲(chu) 1個(ge) int類型數字的內(nei) 存空間,將指針強製轉換為(wei) int類型 if(ptr == NULL) { printf("內(nei) 存空間分配失敗!\n"); exit(1); } printf("輸入保存的元素:\n"); for(int i = 0; i < n; i++) { scanf("%d", &ptr[i]); sum += ptr[i]; } m = n+2; ptr = (int*)realloc(ptr,m*sizeof(int)); // 重新分配一塊足夠存儲(chu) m個(ge) int類型數字的內(nei) 存空間 printf("輸入新增的元素:\n"); for(int i = n; i < m; i++) { scanf("%d", &ptr[i]); sum += ptr[i]; } printf("所有元素累加總和為(wei) :%d\n", sum); free(ptr);// 釋放內(nei) 存空間ptr return 0; }
4)free
5.位域作用:用於(yu) 釋放已分配的內(nei) 存空間。其函數原型為(wei) :void free(void* ptr);
參數:
- ptr:需要釋放的空間地址
返回值:沒有返回值
C語言允許在一個(ge) 結構體(ti) 中以位(Bit)為(wei) 單位來指定其成員長度,這種以位為(wei) 單位的結構體(ti) 成員稱為(wei) “位段”或者“位域”。位域隻能是int、unsigned int、signed int類型。int默認是有符號整型(signed)。
位域的主要目的:節省內(nei) 存空間,比如開關(guan) 控製隻需要0和1,那麽(me) 隻需要1位就能表示二進製0和1,一個(ge) 字節有8位,使用位域就可以隻是用一個(ge) 字節中的其中1位。
- 基本定義:
struct 位域名稱 { 位域列表; }
- 示例:下麵定義(yi) 了一個(ge) 日期的結構體(ti) ,包含成員變量年、月、日,年我們(men) 用四位數字表示,最多隻需要14位,月我們(men) 隻需要四位就能表示1-12月,我們(men) 隻需要用6位便能完全表示1-31日。
#include <stdio.h> struct Date{ unsigned int year; unsigned int month; unsigned int day; }; struct Date2{ unsigned int year : 14; unsigned int month : 4; unsigned int day : 6; }; int main() { printf("Date占用字節數:%llu\n", sizeof(struct Date)); printf("Date2占用字節數:%llu\n", sizeof(struct Date2)); return 0; }
輸出結果:
Date占用字節數:12 Date2占用字節數:4
從(cong) 以上結果便能看出,使用位域可以節省內(nei) 存空間。
注意:位域的位數不能超過其依附的基本類型的最大位數,例如一個(ge) unsigned int類型的成員,他有4個(ge) 字節,一個(ge) 字節是8位,它最大隻能存儲(chu) 32位,位域的位數就不能超過32(不同的編譯器基本類型占用空間大小不一致)
六、文件操作
1.文件的概念2.文件指針文件是一個(ge) 有序數據集,數據集的名稱叫文件名。文件分為(wei) 兩(liang) 種,一種是普通文件,比如txt文件、C語言的源程序文件、頭文件等等存在於(yu) 磁盤上的;另一種是設備文件,比如鼠標、鍵盤、顯示器等等外部設備,都認為(wei) 是一個(ge) 文件。
3.操作文件的函數C語言使用一個(ge) 指針變量指向一個(ge) 文件,通過操作指針來操作文件。
文件指針的定義(yi) :FILE *變量名;FILE實際上是係統定義(yi) 的一個(ge) 結構體(ti) ,該結構體(ti) 中含有文件名、文件狀態、文件當前位置等信息(編寫(xie) 程序時不用關(guan) 心FILE結構體(ti) 細節)
文件位置指針: 文件位置指針表示的是文件中所處位置的指針(頭部、當前位置、末尾等),注意跟文件指針區別開,文件指針指向的是整個(ge) 文件
1)打開與關閉
- fopen:打開一個文件,成功返回文件的指針,失敗返回空指針NULL
- 函數原型:FILE* fopen(const char *path,const char *mode)
- path:文件路徑
- mode:打開的模式
mode主要由以下6個字符組合而成:- r:可讀(文件位置指針在文件頭部,文件必須存在)
- w:可寫(文件位置指針在文件頭部,文件存在則清空內容,不存在就創建)
- a:追加寫入(文件位置指針在文件尾部,文件必須存在)
- b:二進製方式打開
- +:可讀寫
- t:文本模式(默認,可省略)
- 下麵列出常用模式:
選項 說明 r 隻讀打開一個文本文件,隻允許讀數據 w 隻寫打開一個文本文件,隻允許寫數據 a 追加寫入打開一個文本文件,在文件末尾寫數據 rb 以二進製方式打開一個文件,隻允許讀數據 wb 以二進製方式打開一個文件,隻允許寫數據
- 函數原型:FILE* fopen(const char *path,const char *mode)
- fclose:關閉一個文件,成功返回0,失敗返回非0
通常對文件操作如下:
#include "stdio.h" #include "stdlib.h" FILE *fp = fopen("文件名", "打開模式"); if (fp == NULL) { printf("文件打開失敗!"); exit(1); } /* 要執行的文件操作 */ fclose(fp);
2)文件讀寫(xie)
文件結束符:EOF
文件寫(xie) 入的函數需要以寫(xie) 或者讀寫(xie) 模式打開文件,文件讀取的函數需要以讀或者讀取的模式打開文件,讀取或寫(xie) 入操作之後,位置指針都會(hui) 向後移動到讀取或寫(xie) 入位置的末尾
- fgetc:從文件讀取一個字符
- 函數原型:int fgetc(FILE *file);
- file:目標文件的指針
- 返回值:返回int類型的ASCII碼,位置指針向後移動一個字節
- 使用方法:fgetc(文件指針);
- 函數原型:int fgetc(FILE *file);
- fputc:向文件中寫入一個字符
- 函數原型:int fputc(int c, FILE *file);
- c:要寫入的字符(char或者int類型ASCII碼)
- file:目標文件的指針
- 返回值:成功返回寫入的字符,位置指針向後移動一個字節;失敗返回EOF
- 使用方法:fputc('a', 文件指針);
- 函數原型:int fputc(int c, FILE *file);
- fgets:從文件讀取一個字符串到字符數組中
- 函數原型:char* fgets(char *Buffer, int MaxCount, FILE *file );
- Buffer:字符數組的指針
- MaxCount:最大讀取字符數
- file:目標文件的指針
- 說明:
- MaxCount是一個正整數,表示從文件中讀出的字符串不超過 MaxCount-1個字符。在讀入的最後一個字符後加上串結束標誌\0。
- 在讀出MaxCount-1個字符之前,如遇到了換行符或EOF,則讀出結束。
- 返回值:字符數組的首地址
- 使用方法:fgets(數組首地址, 字符串最大長度, 文件指針);
- 函數原型:char* fgets(char *Buffer, int MaxCount, FILE *file );
- fputs:將一個字符串寫入到文件中,不包含’\0’
- 函數原型:int fputs(const char *str, FILE *file);
- str:要寫入的字符數組(字符串)的指針
- file:目標文件的指針
- 返回值:成功返回非負整數;失敗返回EOF(符號常量,其值為-1)
- 使用方法:fputs(字符串, 文件指針);
- 函數原型:int fputs(const char *str, FILE *file);
- fread:從文件中讀取一組固定大小的數據到內存空間
- 函數原型:size_t fread(void *Buffer, size_t size, size_t count, FILE *file);
- Buffer:內存空間首地址(用來存放數據的內存空間指針)
- size:數據塊的大小
- count:數據塊的數量
- file:目標文件的指針
- 返回值:返回成功讀取的對象個數(若出現錯誤或到達文件末尾,則可能小於count)
- 使用方法:fread(內存空間地址, 數據塊大小, 數據塊數量, 文件指針);
- 函數原型:size_t fread(void *Buffer, size_t size, size_t count, FILE *file);
- fwrite:寫入一組固定大小的數據到文件中
- 函數原型:size_t fwrite(const void *Buffer, size_t size, size_t count, FILE *file);
- Buffer:要存入的數據的首地址
- size:數據塊的大小
- count:數據塊的數量
- file:目標文件的指針
- 返回值:返回成功寫入的對象個數(若出現錯誤或到達文件末尾,則可能小於count)
- 使用方法:fwrite(數據地址, 數據塊大小, 數據塊數量, 文件指針);
- 函數原型:size_t fwrite(const void *Buffer, size_t size, size_t count, FILE *file);
- fscanf:從文件中獲取指定格式的數據,跟scanf類似,輸入對象換成了普通文件
- 函數原型:int fscanf(FILE *file, const char *str, [arg...]);
- file:目標文件的指針
- str:格式化字符串
- [arg…]:一個或多個接收數據的地址
- 說明:fscanf遇到空格和換行時結束
- 返回值:成功返回讀入的參數的個數,失敗返回EOF
- 使用方法:fscanf(文件指針, 格式化字符串, 目標地址);
- 函數原型:int fscanf(FILE *file, const char *str, [arg...]);
- fprintf:格式化輸出數據到文件,跟printf類似,輸出對象換成了普通文件
- 函數原型:int fprint(FILE *file, const char *str, [arg...]);
- file:目標文件的指針
- str:格式化字符串
- [arg…]:一個或多個數據
- 說明:fprintf會根據參數str字符串來轉換並格式化數據,然後將結果輸出到參數file指定的文件中,直到出現字符串結束(\0)為止。
- 返回值:成功返回輸出的數據的個數,失敗返回EOF
- 使用方法:fprintf(文件指針, 格式化字符串, 目標數據);
- 函數原型:int fprint(FILE *file, const char *str, [arg...]);
3)文件定位
- rewind:將文件的位置指針移動到文件頭部
- 函數原型:void rewind(FILE *file);
- file:目標文件的指針
- 使用方法:rewind(文件指針);
- 函數原型:void rewind(FILE *file);
- fseek:將文件的位置指針從規定的起始點移動到某個位置
- 函數原型:int fseek(FILE *file, long offset, int start);
- file:目標文件的指針
- offset:偏移量,從起始點移動多少字節,必須是long型數據
- start:起始點,規定三個起始點:文件首、當前位置、文件尾
起始點 標識符 數字表示 文件頭部 SEEK_SET 0 當前位置 SEEK_CUR 1 文件尾部 SEEK_END 2
- 使用方法:fseek(文件指針, 偏移量, 起始點);
- 函數原型:int fseek(FILE *file, long offset, int start);
4)文件檢測
-
feof:判斷文件位置指針是否處於(yu) 文件結束位置
- 函數原型:int feof(FILE *file);
- file:目標文件的指針
- 返回值:文件指針處於結束位置返回非0,否則返回0
- 函數原型:int feof(FILE *file);
-
ferror:檢查文件在用各種輸入輸出函數進行讀寫(xie) 時是否出錯
- 函數原型:int ferror(FILE *file);
- file:目標文件的指針
- 返回值:未出錯返回0,出錯返回非0
- 函數原型:int ferror(FILE *file);
-
clearerr:清除出錯標誌和文件結束標誌,使它們(men) 為(wei) 0值
- 函數原型:void clearerr(FILE *file);
- file:目標文件的指針
- 函數原型:void clearerr(FILE *file);
5)文件操作示例
#include "stdio.h" #include "stdlib.h" struct Student { char name[20]; int age; float score; }; int main() { FILE *fp = fopen("test.txt", "w+"); // 以讀寫模式打開一個文件 if (fp == NULL) { printf("文件打開失敗!"); exit(1); } fputc('a', fp); // 向文件寫入一個字符'a' rewind(fp); // 將文件位置指針放到文件頭部,因為我們剛剛向文件寫入了一個字符'a',所以現在文件位置指針指向的文件尾部 char ch = (char)fgetc(fp); // 從文件讀取一個字符,現在文件中隻有一個'a',讀取的字符就是'a' printf("%c\n",ch); printf("結束位置:%d\n", feof(fp)); // 看看位置指針是不是在結束位置 fseek(fp,1L,0); // 將文件位置指針手動置於字符'a'後麵,讀取時也會把指針後移,但是寫入的時候失敗了,原因暫時未找到! fputs("this is fputs test", fp); // 向文件中寫入字符串,現在文件中的內容應該是"athis is fputs test" printf("寫入出錯:%d\n",ferror(fp)); // 查看寫入是否出錯 rewind(fp); // 位置指針放回文件頭部 char string[255]; // 定義一個字符數組用來存放字符串 fgets(string, 255, fp); // 讀取文件中的字符串到字符數組string中,遇到換行或文件末尾就結束 printf("%s\n",string); // 輸出:athis is fputs test rewind(fp); // 位置指針放回文件頭部 fprintf(fp,"%s %d %f","test", 1, 0.6f); // 現在文件內容是"test 1 0.600000test",因為現在寫入的把前麵的"athis is fputs "覆蓋了 rewind(fp); char str[255]; int a; float b; fscanf(fp,"%s %d %f",str,&a,&b); printf("str的值:%s\na的值:%d\nb的值:%f",str,a,b); /* str的值:test a的值:1 b的值:0.600000 */ fclose(fp); struct Student boys[3]; // 定義一個結構體數組 struct Student boy2[2]; struct Student *pb; // 定義一個結構體指針 pb = boys; // 指向結構體中第一個成員(數組首地址) FILE *fp1 = fopen("test1.txt", "wb+"); // 以二進製讀寫模式打開一個文件 if (fp1 == NULL) { printf("文件打開失敗!"); exit(1); } for (int i=0;i<3;i++){ scanf("%s %d %f",pb->name,&pb->age,&pb->score); // 這裏循環輸入學生的信息 pb++; // 指針向後移動,指向下一個boys數組的成員 } long size = sizeof(struct Student); // 獲取結構的大小 fwrite(boys, size,3,fp1); // 向文件中寫入3個Student結構 rewind(fp1); fseek(fp1,size,SEEK_SET); // 位置指針移動到第二個學生的地址 fread(boy2,size,2,fp1); // 讀取2個Student大小的數據 for (int i=0; i < 2;i++) { printf("%s %d %f\n",boy2[i].name,boy2[i].age,boy2[i].score); } fclose(fp1); // 關閉文件 return 0; }