介紹
C語言是一門面向過程
闷祥、抽象化
的通用程序設(shè)計語言,廣泛應(yīng)用于底層開發(fā)。C語言能以簡易的方式編譯账忘、處理低級存儲器芋肠。C語言是僅產(chǎn)生少量的機器語言以及不需要任何運行環(huán)境支持便能運行的高效率程序設(shè)計語言拨脉。
1972 年逊移,為了移植與開發(fā) UNIX
操作系統(tǒng)严蓖,丹尼斯·里奇在貝爾電話實驗室設(shè)計開發(fā)了 C 語言逻翁。
hello,world
第一個C語言程序:hello,World
// stdio.h 是一個頭文件 (標(biāo)準(zhǔn)輸入輸出頭文件) , #include 是一個預(yù)處理命令饥努,用來引入頭文件
#include <stdio.h>
// 所有的 C 語言程序都需要包含 main() 函數(shù)。 代碼從 main() 函數(shù)開始執(zhí)行八回。
int main()
{
// printf() 用于格式化輸出到屏幕酷愧。
printf("hello,world!\n");
// return 0; 語句用于表示退出程序。
return 0;
}
一. C語言編譯過程
C代碼編譯成可執(zhí)行程序經(jīng)過4步:
- 預(yù)處理:宏定義展開缠诅、頭文件展開溶浴、條件編譯等,同時將代碼中的注釋刪除管引,這里并不會檢查語法
- 編譯:檢查語法士败,將預(yù)處理后文件編譯生成匯編文件
- 匯編:將匯編文件生成目標(biāo)文件(二進(jìn)制文件)
- 鏈接:C語言寫的程序是需要依賴各種庫的,所以編譯之后還需要把庫鏈接到最終的可執(zhí)行程序中去
gcc編譯過程
1) 分步編譯
- 預(yù)處理:gcc -E hello.c -o hello.i
- 編 譯:gcc -S hello.i -o hello.s
- 匯 編:gcc -c hello.s -o hello.o
- 鏈 接:gcc hello.o -o hello_elf
選項 | 含義 | 文件后綴 | 含義 |
---|---|---|---|
-E | 只進(jìn)行預(yù)處理 | .c | C 語言文件 |
-S(大寫) | 只進(jìn)行預(yù)處理和編譯 | .i | 預(yù)處理后的 C 語言文件 |
-c(小寫) | 只進(jìn)行預(yù)處理褥伴、編譯和匯編 | .s | 編譯后的匯編文件 |
-o file | 指定生成的輸出文件名為 file | .o | 編譯后的目標(biāo)文件 |
2) 一步編譯
- gcc hello.c -o demo(會經(jīng)過:預(yù)處理谅将、編譯、匯編重慢、鏈接的過程)
二. 數(shù)據(jù)類型
數(shù)據(jù)類型的作用:編譯器預(yù)算對象(變量)分配的內(nèi)存空間大小饥臂。
常量:
- 在程序運行過程中,其值不能被改變的量
- 常量一般出現(xiàn)在表達(dá)式或賦值語句中
變量:
- 在程序運行過程中似踱,其值可以改變
- 變量在使用前必須先定義擅笔,定義變量前必須有相應(yīng)的數(shù)據(jù)類型
標(biāo)識符命名規(guī)則:
- 標(biāo)識符不能是關(guān)鍵字
- 標(biāo)識符只能由字母、數(shù)字屯援、下劃線組成
- 第一個字符必須為字母或下劃線
- 標(biāo)識符中字母區(qū)分大小寫
計算機內(nèi)存數(shù)值存儲方式
原碼
一個數(shù)的原碼(原始的二進(jìn)制碼)有如下特點:
- 最高位做為符號位猛们,0表示正,為1表示負(fù)
- 其它數(shù)值部分就是數(shù)值本身絕對值的二進(jìn)制數(shù)
- 負(fù)數(shù)的原碼是在其絕對值的基礎(chǔ)上,最高位變?yōu)?
下面數(shù)值以1字節(jié)的大小描述:
十進(jìn)制數(shù) | 原碼 |
---|---|
+15 | 0000 1111 |
-15 | 1000 1111 |
+0 | 0000 0000 |
-0 | 1000 0000 |
反碼
- 對于正數(shù)狞洋,反碼與原碼相同
- 對于負(fù)數(shù)弯淘,符號位不變,其它部分取反(1變0,0變1)
十進(jìn)制數(shù) | 原碼 |
---|---|
+15 | 0000 1111 |
-15 | 1111 0000 |
+0 | 0000 0000 |
-0 | 1111 1111 |
補碼
在計算機系統(tǒng)中吉懊,數(shù)值一律用補碼來存儲庐橙。
- 對于正數(shù)假勿,原碼、反碼态鳖、補碼相同
- 對于負(fù)數(shù)转培,其補碼為它的反碼加1
- 補碼符號位不動,其他位求反浆竭,最后整個數(shù)加1浸须,得到原碼
十進(jìn)制數(shù) | 原碼 |
---|---|
+15 | 0000 1111 |
-15 | 1111 0001 |
+0 | 0000 0000 |
-0 | 0000 0000 |
在計算機系統(tǒng)中,數(shù)值一律用補碼來存儲邦泄,主要原因是:
- 統(tǒng)一了零的編碼
- 將符號位和其它位統(tǒng)一處理
- 將減法運算轉(zhuǎn)變?yōu)榧臃ㄟ\算
- 兩個用補碼表示的數(shù)相加時删窒,如果最高位(符號位)有進(jìn)位,則進(jìn)位被舍棄
sizeof關(guān)鍵字
- sizeof不是函數(shù)顺囊,所以不需要包含任何頭文件肌索,它的功能是計算一個數(shù)據(jù)類型的大小,單位為字節(jié)
- sizeof的返回值為size_t
- size_t類型在32位操作系統(tǒng)下是unsigned int特碳,是一個無符號的整數(shù)
int a;
int b = sizeof(a);//sizeof得到指定值占用內(nèi)存的大小诚亚,單位:字節(jié)
printf("b = %d\n", b);
三. 運算符與表達(dá)式
常用運算符分類
運算符類型 | 作用 |
---|---|
算術(shù)運算符 | 用于處理四則運算 |
賦值運算符 | 用于將表達(dá)式的值賦給變量 |
比較運算符 | 用于表達(dá)式的比較,并返回一個真值或假值 |
邏輯運算符 | 用于根據(jù)表達(dá)式的值返回真值或假值 |
位運算符 | 用于處理數(shù)據(jù)的位運算 |
sizeof運算符 | 用于求字節(jié)數(shù)長度 |
算術(shù)運算符
運算符 | 術(shù)語 | 示例 | 結(jié)果 |
---|---|---|---|
+ | 正號 | +3 | 3 |
- | 負(fù)號 | -3 | -3 |
+ | 加 | 10 + 5 | 15 |
- | 減 | 10 - 5 | 5 |
* | 乘 | 10 * 5 | 50 |
/ | 除 | 10 / 5 | 2 |
% | 取模(取余) | 10 % 3 | 1 |
++ | 前自增 | a=2; b=++a; | a=3; b=3; |
++ | 后自增 | a=2; b=a++; | a=3; b=2; |
-- | 前自減 | a=2; b=--a; | a=1; b=1; |
-- | 后自減 | a=2; b=a--; | a=1; b=2; |
賦值運算符
運算符 | 術(shù)語 | 示例 | 結(jié)果 |
---|---|---|---|
= | 賦值 | a=2; b=3; | a=2; b=3; |
+= | 加等于 | a=0; a+=2; | a=2; |
-= | 減等于 | a=5; a-=3; | a=2; |
*= | 乘等于 | a=2; a*=2; | a=4; |
/= | 除等于 | a=4; a/=2; | a=2; |
%= | 模等于 | a=3; a%2; | a=1; |
比較運算符
運算符 | 術(shù)語 | 示例 | 結(jié)果 |
---|---|---|---|
== | 相等于 | 4 == 3 | 0 |
!= | 不等于 | 4 != 3 | 1 |
< | 小于 | 4 < 3 | 0 |
> | 大于 4 > 3 | 1 | |
<= | 小于等于 | 4 <= 3 | 0 |
>= | 大于等于 | 4 >= 1 | 1 |
邏輯運算符
運算符 | 術(shù)語 | 示例 | 結(jié)果 |
---|---|---|---|
! | 非 | !a | 如果a為假午乓,則!a為真亡电;如果a為真,則!a為假硅瞧。 |
&& | 與 | a && b | 如果a和b都為真,則結(jié)果為真恕汇,否則為假腕唧。 |
或 | a || b | 如果a和b有一個為真,則結(jié)果為真瘾英,二者都為假時枣接,結(jié)果為假。 |
類型轉(zhuǎn)換
數(shù)據(jù)有不同的類型缺谴,不同類型數(shù)據(jù)之間進(jìn)行混合運算時必然涉及到類型的轉(zhuǎn)換問題但惶。
轉(zhuǎn)換的方法有兩種:
- 自動轉(zhuǎn)換(隱式轉(zhuǎn)換):遵循一定的規(guī)則,由編譯系統(tǒng)自動完成。
- 強制類型轉(zhuǎn)換:把表達(dá)式的運算結(jié)果強制轉(zhuǎn)換成所需的數(shù)據(jù)類型湿蛔。
類型轉(zhuǎn)換的原則:占用內(nèi)存字節(jié)數(shù)少(值域小)的類型膀曾,向占用內(nèi)存字節(jié)數(shù)多(值域大)的類型轉(zhuǎn)換,以保證精度不降低阳啥。
隱式轉(zhuǎn)換
int num = 5;
printf("s1=%d\n", num / 2);
printf("s2=%lf\n", num / 2.0);
強制轉(zhuǎn)換
float x = 0;
int i = 0;
x = 3.6f;
i = x; //x為實型, i為整型添谊,直接賦值會有警告
i = (int)x; //使用強制類型轉(zhuǎn)換
printf("x=%f, i=%d\n", x, i);
四.程序流程結(jié)構(gòu)
C語言支持最基本的三種程序運行結(jié)構(gòu):順序結(jié)構(gòu)、選擇結(jié)構(gòu)察迟、循環(huán)結(jié)構(gòu)
斩狱。
- 順序結(jié)構(gòu):程序按順序執(zhí)行耳高,不發(fā)生跳轉(zhuǎn)。
- 選擇結(jié)構(gòu):依據(jù)是否滿足條件所踊,有選擇的執(zhí)行相應(yīng)功能泌枪。
- 循環(huán)結(jié)構(gòu):依據(jù)條件是否滿足,循環(huán)多次執(zhí)行某段代碼秕岛。
選擇結(jié)構(gòu)
1. if…else語句
int main()
{
int a = 1;
int b = 2;
if (a > b)
{
printf("%d\n", a);
}
else
{
printf("%d\n", b);
}
return 0;
}
2. 三目運算符
int a = 10;
int b = 20;
int c;
c = ( a > b ? a : b );
3. switch語句
int main()
{
char c;
c = getchar();
switch (c) //參數(shù)只能是整型變量
{
case '1':
printf("OK\n");
break;//switch遇到break就中斷了
case '2':
printf("not OK\n");
break;
default://如果上面的條件都不滿足碌燕,那么執(zhí)行default
printf("are u ok?\n");
}
return 0;
}
循環(huán)結(jié)構(gòu)
1. while語句
int main()
{
int a = 20;
while (a > 10)
{
scanf("%d", &a);
printf("a = %d\n", a);
}
return 0;
}
2. do…while語句
int main()
{
int a = 1;
do
{
a++;
printf("a = %d\n", a);
} while (a < 10);
return 0;
}
3. for語句
int main()
{
int I;
int sum = 0;
for (i = 0; i <= 100; I++)
{
sum += I;
}
printf("sum = %d\n", sum);
return 0;
}
跳轉(zhuǎn)語句break、continue瓣蛀、goto
1. break語句
- 當(dāng)它出現(xiàn)在switch條件語句中時陆蟆,作用是終止某個case并跳出switch結(jié)構(gòu)。
- 當(dāng)它出現(xiàn)在循環(huán)語句中惋增,作用是跳出當(dāng)前內(nèi)循環(huán)語句叠殷,執(zhí)行后面的代碼。
- 當(dāng)它出現(xiàn)在嵌套循環(huán)語句中诈皿,跳出最近的內(nèi)循環(huán)語句林束,執(zhí)行后面的代碼。
int main()
{
int i = 0;
while (1)
{
I++;
printf("i = %d\n", i);
if (i == 10)
{
break; //跳出while循環(huán)
}
}
int flag = 0;
int m = 0;
int n = 0;
for (m = 0; m < 10; m++)
{
for (n = 0; n < 10; n++)
{
if (n == 5)
{
flag = 1;
break; //跳出for (n = 0; n < 10; n++)
}
}
if (flag == 1)
{
break; //跳出for (m = 0; m < 10; m++)
}
}
return 0;
}
2. continue語句
在循環(huán)語句中稽亏,如果希望立即終止本次循環(huán)壶冒,并執(zhí)行下一次循環(huán),此時就需要使用continue語句
int main()
{
int sum = 0; //定義變量sum
for (int i = 1; i <= 100; I++)
{
if (i % 2 == 0) //如果i是一個偶數(shù)截歉,執(zhí)行if語句中的代碼
{
continue; //結(jié)束本次循環(huán)
}
sum += i; //實現(xiàn)sum和I的累加
}
printf("sum = %d\n", sum);
return 0;
}
goto語句(無條件跳轉(zhuǎn)胖腾,盡量少用)
int main()
{
goto End; //無條件跳轉(zhuǎn)到End的標(biāo)識
printf("aaaaaaaaa\n");
End:
printf("bbbbbbbb\n");
return 0;
}
五.數(shù)組和字符串
1. 數(shù)組
在程序設(shè)計中,為了方便處理數(shù)據(jù)把具有相同類型的若干變量按有序形式組織起來——稱為數(shù)組瘪松。
數(shù)組就是在內(nèi)存中連續(xù)的相同類型的變量空間咸作。同一個數(shù)組所有的成員都是相同的數(shù)據(jù)類型,同時所有的成員在內(nèi)存中的地址是連續(xù)的宵睦。
數(shù)組屬于構(gòu)造數(shù)據(jù)類型:
- 一個數(shù)組可以分解為多個數(shù)組元素:這些數(shù)組元素可以是基本數(shù)據(jù)類型或構(gòu)造類型记罚。
- 按數(shù)組元素類型的不同,數(shù)組可分為:數(shù)值數(shù)組壳嚎、字符數(shù)組桐智、指針數(shù)組、結(jié)構(gòu)數(shù)組等類別烟馅。
1.1 一維數(shù)組
數(shù)組的初始化
在定義數(shù)組的同時進(jìn)行賦值说庭,稱為初始化。全局?jǐn)?shù)組若不初始化郑趁,編譯器將其初始化為零口渔。局部數(shù)組若不初始化,內(nèi)容為隨機值穿撮。
int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };//定義一個數(shù)組缺脉,同時初始化所有成員變量
int a[10] = { 1, 2, 3 };//初始化前三個成員痪欲,后面所有元素都設(shè)置為0
int a[10] = { 0 };//所有的成員都設(shè)置為0
//[]中不定義元素個數(shù),定義時必須初始化
int a[] = { 1, 2, 3, 4, 5 };//定義了一個數(shù)組攻礼,有5個成員
數(shù)組名
數(shù)組名是一個地址的常量业踢,代表數(shù)組中首元素的地址。
int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };//定義一個數(shù)組礁扮,同時初始化所有成員變量
printf("a = %p\n", a);
printf("&a[0] = %p\n", &a[0]);
1.2. 二維數(shù)組
二維數(shù)組定義的一般形式是:類型說明符 數(shù)組名[常量表達(dá)式1][常量表達(dá)式2]
二維數(shù)組的初始化
//分段賦值 int a[3][4] = {{ 1, 2, 3, 4 },{ 5, 6, 7, 8, },{ 9, 10, 11, 12 }};
int a[3][4] =
{
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8, },
{ 9, 10, 11, 12 }
};
//連續(xù)賦值
int a[3][4] = { 1, 2, 3, 4 , 5, 6, 7, 8, 9, 10, 11, 12 };
//可以只給部分元素賦初值知举,未初始化則為0
int a[3][4] = { 1, 2, 3, 4 };
//所有的成員都設(shè)置為0
int a[3][4] = {0};
//[]中不定義元素個數(shù),定義時必須初始化
int a[][4] = { 1, 2, 3, 4, 5, 6, 7, 8};
1.3 多維數(shù)組
多維數(shù)組的定義與二維數(shù)組類似太伊,其語法格式具體如下:
數(shù)組類型修飾符 數(shù)組名 [n1][n2]…[nn];
2. 字符數(shù)組與字符串
字符數(shù)組與字符串區(qū)別
- C語言中沒有字符串這種數(shù)據(jù)類型雇锡,可以通過char的數(shù)組來替代;
- 字符串一定是一個char的數(shù)組僚焦,但char的數(shù)組未必是字符串锰提;
- 數(shù)字0(和字符‘\0’等價)結(jié)尾的char數(shù)組就是一個字符串,但如果char數(shù)組沒有以數(shù)字0結(jié)尾芳悲,那么就不是一個字符串立肘,只是普通字符數(shù)組,所以字符串是一種特殊的char的數(shù)組名扛。
字符串的初始化
// C語言沒有字符串類型谅年,通過字符數(shù)組模擬
// C語言字符串,以字符‘\0’, 數(shù)字0
int main()
{
//不指定長度, 沒有0結(jié)束符肮韧,有多少個元素就有多長
char buf[] = { 'a', 'b', 'c' };
printf("buf = %s\n", buf); //亂碼
//指定長度融蹂,后面沒有賦值的元素,自動補0
char buf2[100] = { 'a', 'b', 'c' };
printf("buf2 = %s\n", buf2);
//所有元素賦值為0
char buf3[100] = { 0 };
printf("buf3 = %s\n", buf3);
//數(shù)組越界
char buf4[2] = { '1', '2', '3' };
printf("buf4 = %s\n", buf4);
char buf5[50] = { '1', 'a', 'b', '0', '7' };
printf("buf5 = %s\n", buf5);
char buf6[50] = { '1', 'a', 'b', 0, '7' };
printf("buf6 = %s\n", buf6);
char buf7[50] = { '1', 'a', 'b', '\0', '7' };
printf("buf7 = %s\n", buf7);
//使用字符串初始化弄企,編譯器自動在后面補0超燃,常用
char buf8[] = "agjdslgjlsdjg";
printf("buf8 = %s\n", buf8);
//'\0'后面最好不要連著數(shù)字,有可能幾個數(shù)字連起來剛好是一個轉(zhuǎn)義字符
//'\ddd'八進(jìn)制字義字符桩蓉,'\xdd'十六進(jìn)制轉(zhuǎn)移字符
// \012相當(dāng)于\n
char str[] = "\012abc";
printf("str == %s\n", str);
return 0;
}
六. 函數(shù)
概述
C 程序是由函數(shù)組成的,我們寫的代碼都是由主函數(shù) main()開始執(zhí)行的劳闹。函數(shù)是 C 程序的基本模塊院究,是用于完成特定任務(wù)的程序代碼單元。
從函數(shù)定義的角度看本涕,函數(shù)可分為系統(tǒng)函數(shù)和用戶定義函數(shù)兩種:
- 系統(tǒng)函數(shù)业汰,即庫函數(shù):這是由編譯系統(tǒng)提供的,用戶不必自己定義這些函數(shù)菩颖,可以直接使用它們样漆,如我們常用的打印函數(shù)printf()。
- 用戶定義函數(shù):用以解決用戶的專門需要晦闰。
函數(shù)的作用
- 函數(shù)的使用可以省去重復(fù)代碼的編寫放祟,降低代碼重復(fù)率
- 函數(shù)可以讓程序更加模塊化鳍怨,從而有利于程序的閱讀,修改和完善
函數(shù)的定義
函數(shù)定義的一般形式:
返回類型 函數(shù)名(形式參數(shù)列表)
{
數(shù)據(jù)定義部分;
執(zhí)行語句部分;
}
int max(int a, int b){
return a>b?a:b;
}
int main(){
int a = 10;
int b = 100;
int c = max(a, b);
printf("%d",c);
}
函數(shù)的形參和實參
- 形參出現(xiàn)在函數(shù)定義中跪妥,在整個函數(shù)體內(nèi)都可以使用鞋喇,離開該函數(shù)則不能使用。
- 實參出現(xiàn)在主調(diào)函數(shù)中眉撵,進(jìn)入被調(diào)函數(shù)后侦香,實參也不能使用。
- 實參變量對形參變量的數(shù)據(jù)傳遞是“值傳遞”纽疟,即單向傳遞罐韩,只由實參傳給形參,而不能由形參傳回來給實參污朽。
- 在調(diào)用函數(shù)時散吵,編譯系統(tǒng)臨時給形參分配存儲單元。調(diào)用結(jié)束后膘壶,形參單元被釋放错蝴。
- 實參單元與形參單元是不同的單元。調(diào)用結(jié)束后颓芭,形參單元被釋放顷锰,函數(shù)調(diào)用結(jié)束返回主調(diào)函數(shù)后則不能再使用該形參變量。實參單元仍保留并維持原值亡问。因此官紫,在執(zhí)行一個被調(diào)用函數(shù)時,形參的值如果發(fā)生改變州藕,并不會改變主調(diào)函數(shù)中實參的值束世。
函數(shù)的聲明
- 如果使用用戶自己定義的函數(shù),而該函數(shù)與調(diào)用它的函數(shù)(即主調(diào)函數(shù))不在同一文件中床玻,或者函數(shù)定義的位置在主調(diào)函數(shù)之后毁涉,則必須在調(diào)用此函數(shù)之前對被調(diào)用的函數(shù)作聲明。
- 所謂函數(shù)聲明锈死,就是在函數(shù)尚在未定義的情況下贫堰,事先將該函數(shù)的有關(guān)信息通知編譯系統(tǒng),相當(dāng)于告訴編譯器待牵,函數(shù)在后面定義其屏,以便使編譯能正常進(jìn)行。
- 注意:一個函數(shù)只能被定義一次缨该,但可以聲明多次偎行。
int max(int x, int y); // 函數(shù)的聲明,分號不能省略
// int max(int, int); // 另一種方式
int main()
{
int a = 10, b = 25, num_max = 0;
num_max = max(a, b); // 函數(shù)的調(diào)用
printf("num_max = %d\n", num_max);
return 0;
}
// 函數(shù)的定義
int max(int x, int y)
{
return x > y ? x : y;
}
main函數(shù)與exit函數(shù)
在main函數(shù)中調(diào)用exit和return結(jié)果是一樣的,但在子函數(shù)中調(diào)用return只是代表子函數(shù)終止了蛤袒,在子函數(shù)中調(diào)用exit熄云,那么程序終止。
void fun()
{
printf("fun\n");
//return;
exit(0);
}
int main()
{
fun();
while (1);
return 0;
}
多文件(分文件)編程
1.分文件編程
- 把函數(shù)聲明放在頭文件xxx.h中汗盘,在主函數(shù)中包含相應(yīng)頭文件
- 在頭文件對應(yīng)的xxx.c中實現(xiàn)xxx.h聲明的函數(shù)
2.防止頭文件重復(fù)包含
當(dāng)一個項目比較大時皱碘,往往都是分文件,這時候有可能不小心把同一個頭文件 include 多次隐孽,或者頭文件嵌套包含
為了避免同一個文件被include多次癌椿,C/C++中有兩種方式,一種是 #ifndef 方式菱阵,一種是 #pragma once 方式踢俄。
方法一:
#ifndef __SOMEFILE_H__
#define __SOMEFILE_H__
// 聲明語句
#endif
方法二:
#pragma once
// 聲明語句
七. 指針
1. 指針變量的定義和使用
- 指針也是一種數(shù)據(jù)類型,指針變量也是一種變量
- 指針變量指向誰晴及,就把誰的地址賦值給指針變量
- “ * ”操作符操作的是指針變量指向的內(nèi)存空間
int a = 10;
int *p = &a;
2.通過指針間接修改變量的值
int a = 0;
int b = 11;
int *p = &a;
*p = 100;
printf("a = %d, *p = %d\n", a, *p);
p = &b;
*p = 22;
printf("b = %d, *p = %d\n", b, *p);
3.指針大小
- 使用sizeof()測量指針的大小都办,得到的總是:4或8
- sizeof()測的是指針變量指向存儲地址的大小
- 在32位平臺,所有的指針(地址)都是32位(4字節(jié))
- 在64位平臺虑稼,所有的指針(地址)都是64位(8字節(jié))
4.野指針和空指針
指針變量也是變量琳钉,是變量就可以任意賦值,但是任意數(shù)值賦值給指針變量沒有意義蛛倦,因為這樣的指針就成了野指針歌懒,此指針指向的區(qū)域是未知(操作系統(tǒng)不允許操作此指針指向的內(nèi)存區(qū)域)。所以溯壶,野指針不會直接引發(fā)錯誤及皂,操作野指針指向的內(nèi)存區(qū)域才會出問題。
int a = 100;
int *p;
p = a; //把a的值賦值給指針變量p且改,p為野指針验烧, ok,不會有問題又跛,但沒有意義
p = 0x12345678; //給指針變量p賦值碍拆,p為野指針, ok慨蓝,不會有問題感混,但沒有意義
*p = 1000; //操作野指針指向未知區(qū)域,內(nèi)存出問題菌仁,err
但是浩习,野指針和有效指針變量保存的都是數(shù)值静暂,為了標(biāo)志此指針變量沒有指向任何變量(空閑可用)济丘,C語言中,可以把NULL賦值給此指針,這樣就標(biāo)志此指針為空指針摹迷,沒有任何指針疟赊。
int *p = NULL;
NULL是一個值為0的宏常量:
#define NULL ((void *)0)
5.萬能指針void *
void *指針可以指向任意變量的內(nèi)存空間
void *p = NULL;
int a = 10;
p = (void *)&a; //指向變量時,最好轉(zhuǎn)換為void *
//使用指針變量指向的內(nèi)存時峡碉,轉(zhuǎn)換為int *
*( (int *)p ) = 11;
printf("a = %d\n", a);
6.const修飾的指針變量
int a = 100;
int b = 200;
//指向常量的指針
//修飾*近哟,指針指向內(nèi)存區(qū)域不能修改,指針指向可以變
const int *p1 = &a; //等價于int const *p1 = &a;
//*p1 = 111; //err
p1 = &b; //ok
//指針常量
//修飾p1鲫寄,指針指向不能變吉执,指針指向的內(nèi)存可以修改
int * const p2 = &a;
//p2 = &b; //err
*p2 = 222; //ok
7.多級指針
- C語言允許有多級指針存在,在實際的程序中一級指針最常用地来,其次是二級指針戳玫。
- 二級指針就是指向一個一級指針變量地址的指針。
int a = 10;
int *p = &a; //一級指針
*p = 100; //*p就是a
int **q = &p;
//*q就是p
//**q就是a
int ***t = &q;
//*t就是q
//**t就是p
//***t就是a
指針小結(jié)
定義 | 說明 |
---|---|
int i | 定義整形變量 |
int *p | 定義一個指向int的指針變量 |
int a[10] | 定義一個有10個元素的數(shù)組未斑,每個元素類型為int |
int p[10] | 定義一個有10個元素的數(shù)組咕宿,每個元素類型為int |
int func() | 定義一個函數(shù),返回值為int型 |
int *func() | 定義一個函數(shù)蜡秽,返回值為int *型 |
int **p | 定義一個指向int的指針的指針府阀,二級指針 |
八.內(nèi)存管理
1.作用域
C語言變量的作用域分為:
- 代碼塊作用域(代碼塊是{}之間的一段代碼)
- 函數(shù)作用域
- 文件作用域
1.1局部變量
局部變量也叫auto自動變量(auto可寫可不寫),一般情況下代碼塊{}內(nèi)部定義的變量都是自動變量芽突,它有如下特點:
- 在一個函數(shù)內(nèi)定義试浙,只在函數(shù)范圍內(nèi)有效
- 在復(fù)合語句中定義,只在復(fù)合語句中有效
- 隨著函數(shù)調(diào)用的結(jié)束或復(fù)合語句的結(jié)束局部變量的聲明聲明周期也結(jié)束
- 如果沒有賦初值诉瓦,內(nèi)容為隨機
void test()
{
//auto寫不寫是一樣的
//auto只能出現(xiàn)在{}內(nèi)部
auto int b = 10;
}
1.2靜態(tài)(static)局部變量
- static局部變量的作用域也是在定義的函數(shù)內(nèi)有效
- static局部變量的生命周期和程序運行周期一樣川队,同時staitc局部變量的值只初始化一次,但可以賦值多次
- static局部變量若未賦以初值睬澡,則由系統(tǒng)自動賦值:數(shù)值型變量自動賦初值0固额,字符型變量賦空字符
void fun()
{
//靜態(tài)局部變量,沒有賦值煞聪,系統(tǒng)賦值為0斗躏,而且只會初始化一次
static int a;
a++;
printf("a = %d\n", a);
}
1.3 全局變量
- 在函數(shù)外定義,可被本文件及其它文件中的函數(shù)所共用昔脯,若其它文件中的函數(shù)調(diào)用此變量,須用extern聲明
- 全局變量的生命周期和程序運行周期一樣
- 不同文件的全局變量不可重名
1.4 靜態(tài)(static)全局變量
- 在函數(shù)外定義,作用范圍被限制在所定義的文件中
- 不同文件靜態(tài)全局變量可以重名,但作用域不沖突
- static全局變量的生命周期和程序運行周期一樣啄糙,同時staitc全局變量的值只初始化一次
1.5 extern全局變量聲明
- extern int a;聲明一個變量,這個變量在別的文件中已經(jīng)定義了云稚,這里只是聲明隧饼,而不是定義。
1.6全局函數(shù)和靜態(tài)函數(shù)
在C語言中函數(shù)默認(rèn)都是全局的静陈,使用關(guān)鍵字static可以將函數(shù)聲明為靜態(tài)燕雁,函數(shù)定義為static就意味著這個函數(shù)只能在定義這個函數(shù)的文件中使用诞丽,在其他文件中不能調(diào)用,即使在其他文件中聲明這個函數(shù)都沒用拐格。
注意:
- 允許在不同的函數(shù)中使用相同的變量名僧免,它們代表不同的對象,分配不同的單元捏浊,互不干擾懂衩。
- 同一源文件中,允許全局變量和局部變量同名,在局部變量的作用域內(nèi)金踪,全局變量不起作用浊洞。
- 所有的函數(shù)默認(rèn)都是全局的,意味著所有的函數(shù)都不能重名胡岔,但如果是staitc函數(shù)沛申,那么作用域是文件級的,所以不同的文件static函數(shù)名是可以相同的姐军。
總結(jié)
類 型 | 作 用 域 | 生 命 周 期 |
---|---|---|
auto變量 | 一對{}內(nèi) | 當(dāng)前函數(shù) |
static局部變量 | 一對{}內(nèi) | 整個程序運行期 |
extern變量 | 整個程序 | 整個程序運行期 |
static全局變量 | 當(dāng)前文件 | 整個程序運行期 |
extern函數(shù) | 整個程序 | 整個程序運行期 |
static函數(shù) | 當(dāng)前文件 | 整個程序運行期 |
register變量 | 一對{}內(nèi) | 當(dāng)前函數(shù) |
2. 內(nèi)存布局
代碼區(qū)(text segment)
加載的是可執(zhí)行文件代碼段铁材,所有的可執(zhí)行代碼都加載到代碼區(qū),這塊內(nèi)存是不可以在運行期間修改的奕锌。
未初始化數(shù)據(jù)區(qū)(BSS)
加載的是可執(zhí)行文件BSS段著觉,位置可以分開亦可以緊靠數(shù)據(jù)段,存儲于數(shù)據(jù)段的數(shù)據(jù)(全局未初始化惊暴,靜態(tài)未初始化數(shù)據(jù))的生存周期為整個程序運行過程饼丘。
全局初始化數(shù)據(jù)區(qū)/靜態(tài)數(shù)據(jù)區(qū)(data segment)
加載的是可執(zhí)行文件數(shù)據(jù)段,存儲于數(shù)據(jù)段(全局初始化辽话,靜態(tài)初始化數(shù)據(jù)肄鸽,文字常量(只讀))的數(shù)據(jù)的生存周期為整個程序運行過程。
棧區(qū)(stack)
棧是一種先進(jìn)后出的內(nèi)存結(jié)構(gòu)油啤,由編譯器自動分配釋放典徘,存放函數(shù)的參數(shù)值、返回值益咬、局部變量等逮诲。在程序運行過程中實時加載和釋放,因此幽告,局部變量的生存周期為申請到釋放該段椕佛校空間。
堆區(qū)(heap)
堆是一個大容器冗锁,它的容量要遠(yuǎn)遠(yuǎn)大于棧齐唆,但沒有棧那樣先進(jìn)后出的順序。用于動態(tài)內(nèi)存分配冻河。堆在內(nèi)存中位于BSS區(qū)和棧區(qū)之間箍邮。一般由程序員分配和釋放抛腕,若程序員不釋放,程序結(jié)束時由操作系統(tǒng)回收媒殉。
2.1 堆區(qū)內(nèi)存分配和釋放
malloc()
void *malloc(size_t size);
功能:在內(nèi)存的動態(tài)存儲區(qū)(堆區(qū))中分配一塊長度為size字節(jié)的連續(xù)區(qū)域,用來存放類型說明符指定的類型摔敛。分配的內(nèi)存空間內(nèi)容不確定廷蓉,一般使用memset初始化。
int main()
{
int count, *array, n;
printf("請輸入要申請數(shù)組的個數(shù):\n");
scanf("%d", &n);
array = (int *)malloc(n * sizeof (int));
if (array == NULL)
{
printf("申請空間失敗!\n");
return -1;
}
//將申請到空間清0
memset(array, 0, sizeof(int)*n);
for (count = 0; count < n; count++) /*給數(shù)組賦值*/
array[count] = count;
for (count = 0; count < n; count++) /*打印數(shù)組元素*/
printf("%d", array[count]);
free(array);
return 0;
}
free()
void free(void *ptr);
功能:釋放ptr所指向的一塊內(nèi)存空間马昙,ptr是一個任意類型的指針變量桃犬,指向被釋放區(qū)域的首地址。對同一內(nèi)存空間多次釋放會出錯行楞。
九.復(fù)合類型
1.結(jié)構(gòu)體
結(jié)構(gòu)體變量的定義和初始化
定義結(jié)構(gòu)體變量的方式:
- 先聲明結(jié)構(gòu)體類型再定義變量名
- 在聲明類型的同時定義變量
- 直接定義結(jié)構(gòu)體類型變量(無類型名)
結(jié)構(gòu)體類型和結(jié)構(gòu)體變量關(guān)系:
- 結(jié)構(gòu)體類型:指定了一個結(jié)構(gòu)體類型攒暇,它相當(dāng)于一個模型,但其中并無具體數(shù)據(jù)子房,系統(tǒng)對之也不分配實際內(nèi)存單元形用。
- 結(jié)構(gòu)體變量:系統(tǒng)根據(jù)結(jié)構(gòu)體類型(內(nèi)部成員狀況)為之分配空間。
//結(jié)構(gòu)體類型的定義
struct stu
{
char name[50];
int age;
};
//先定義類型证杭,再定義變量(常用)
struct stu s1 = { "mike", 18 };
//定義類型同時定義變量
struct stu2
{
char name[50];
int age;
}s2 = { "lily", 22 };
struct
{
char name[50];
int age;
}s3 = { "yuri", 25 };
指向普通結(jié)構(gòu)體變量的指針
struct student {
char name[20];
int age;
};
int main(){
struct student stu = {"leon", 20};
// 結(jié)構(gòu)體指針,通過->操作結(jié)構(gòu)體成員
struct student *p = &stu;
printf("%s\n",p->name);
printf("%d\n",p->age);
}
堆區(qū)結(jié)構(gòu)體變量
struct student {
char name[20];
int age;
};
int main(){
// 在堆空間創(chuàng)建結(jié)構(gòu)
struct student *stu = malloc(sizeof(struct student));
strcpy(stu->name, "leon");
stu->age = 20;
printf("name = %s, age = %d", stu->name, stu->age);
printf("name = %s, age = %d", (*stu).name, (*stu).age);
free(stu);
stu = NULL;
return 0;
}
2.共用體(聯(lián)合體)
- 聯(lián)合union是一個能在同一個存儲空間存儲不同類型數(shù)據(jù)的類型田度;
- 聯(lián)合體所占的內(nèi)存長度等于其最長成員的長度,也有叫做共用體解愤;
- 同一內(nèi)存段可以用來存放幾種不同類型的成員镇饺,但每一瞬時只有一種起作用;
- 共用體變量中起作用的成員是最后一次存放的成員送讲,在存入一個新的成員后原有的成員的值會被覆蓋奸笤;
- 共用體變量的地址和它的各成員的地址都是同一地址
//共用體也叫聯(lián)合體
union Test
{
unsigned char a;
unsigned int b;
unsigned short c;
};
int main()
{
//定義共用體變量
union Test tmp;
//1、所有成員的首地址是一樣的
printf("%p, %p, %p\n", &(tmp.a), &(tmp.b), &(tmp.c));
//2哼鬓、共用體大小為最大成員類型的大小
printf("%lu\n", sizeof(union Test));
//3监右、一個成員賦值,會影響另外的成員
//左邊是高位异希,右邊是低位
//低位放低地址秸侣,高位放高地址
tmp.b = 0x44332211;
printf("%x\n", tmp.a); //11
printf("%x\n", tmp.c); //2211
tmp.a = 0x00;
printf("short: %x\n", tmp.c); //2200
printf("int: %x\n", tmp.b); //44332200
return 0;
}
3. 枚舉
枚舉:將變量的值一一列舉出來,變量的值只限于列舉出來的值的范圍內(nèi)宠互。
枚舉類型定義:
enum 枚舉名
{
枚舉值表
};
- 在枚舉值表中應(yīng)列出所有可用值味榛,也稱為枚舉元素。
- 枚舉值是常量予跌,不能在程序中用賦值語句再對它賦值搏色。
- 舉元素本身由系統(tǒng)定義了一個表示序號的數(shù)值從0開始順序定義為0,1,2 …
4.typedef
typedef為C語言的關(guān)鍵字撞鹉,作用是為一種數(shù)據(jù)類型(基本類型或自定義數(shù)據(jù)類型)定義一個新名字,不能創(chuàng)建新類型抵蚊。
- 與#define不同航邢,typedef僅限于數(shù)據(jù)類型耕赘,而不是能是表達(dá)式或具體的值
- #define發(fā)生在預(yù)處理,typedef發(fā)生在編譯階段
typedef signed long long ull;
typedef struct Person PP;
struct Person
{
char name[100];
int age;
ull a;
};
int main()
{
ull test = 1000000;
PP p = {"alex", 10, test};
return 0;
}
十. 文件操作
1. 文件指針
在C語言中用一個指針變量指向一個文件膳殷,這個指針稱為文件指針操骡。
typedef struct
{
short level; //緩沖區(qū)"滿"或者"空"的程度
unsigned flags; //文件狀態(tài)標(biāo)志
char fd; //文件描述符
unsigned char hold; //如無緩沖區(qū)不讀取字符
short bsize; //緩沖區(qū)的大小
unsigned char *buffer;//數(shù)據(jù)緩沖區(qū)的位置
unsigned ar; //指針,當(dāng)前的指向
unsigned istemp; //臨時文件赚窃,指示器
short token; //用于有效性的檢查
}FILE;
FILE
是系統(tǒng)使用typedef定義出來的有關(guān)文件信息的一種結(jié)構(gòu)體類型册招,結(jié)構(gòu)中含有文件名、文件狀態(tài)和文件當(dāng)前位置等信息勒极。
聲明FILE結(jié)構(gòu)體類型的信息包含在頭文件“stdio.h”中是掰,一般設(shè)置一個指向FILE類型變量的指針變量,然后通過它來引用這些FILE類型變量辱匿。通過文件指針就可對它所指的文件進(jìn)行各種操作键痛。
C語言中有三個特殊的文件指針由系統(tǒng)默認(rèn)打開,用戶無需定義即可直接使用:
- stdin: 標(biāo)準(zhǔn)輸入匾七,默認(rèn)為當(dāng)前終端(鍵盤)散休,我們使用的scanf、getchar函數(shù)默認(rèn)從此終端獲得數(shù)據(jù)乐尊。
- stdout:標(biāo)準(zhǔn)輸出戚丸,默認(rèn)為當(dāng)前終端(屏幕),我們使用的printf扔嵌、puts函數(shù)默認(rèn)輸出信息到此終端限府。
- stderr:標(biāo)準(zhǔn)出錯,默認(rèn)為當(dāng)前終端(屏幕)痢缎,我們使用的perror函數(shù)默認(rèn)輸出信息到此終端胁勺。
2.文件的打開
任何文件使用之前必須打開:
#include <stdio.h>
FILE * fopen(const char * filename, const char * mode);
功能:打開文件
參數(shù):
filename:需要打開的文件名,根據(jù)需要加上路徑
mode:打開文件的模式設(shè)置
返回值:
成功:文件指針
失敹揽酢:NULL
第一個參數(shù)的幾種形式:
//相對路徑:
//打開當(dāng)前目錄passdw文件:源文件(源程序)所在目錄
FILE *fp_passwd = fopen("passwd.txt", "r");
//打開當(dāng)前目錄(test)下passwd.txt文件
fp_passwd = fopen(". / test / passwd.txt", "r");
//打開當(dāng)前目錄上一級目錄(相對當(dāng)前目錄)passwd.txt文件
fp_passwd = fopen(".. / passwd.txt", "r");
//絕對路徑:
//打開C盤test目錄下一個叫passwd.txt文件
fp_passwd = fopen("c://test//passwd.txt","r");
第二個參數(shù)的幾種形式(打開文件的方式):
打開模式 | 含義 |
---|---|
r或rb | 以只讀方式打開一個文本文件(不創(chuàng)建文件署穗,若文件不存在則報錯) |
w或wb | 以寫方式打開文件(如果文件存在則清空文件,文件不存在則創(chuàng)建一個文件) |
a或ab | 以追加方式打開文件嵌洼,在末尾添加內(nèi)容案疲,若文件不存在則創(chuàng)建文件 |
r+或rb+ | 以可讀、可寫的方式打開文件(不創(chuàng)建新文件) |
w+或wb+ | 以可讀麻养、可寫的方式打開文件(如果文件存在則清空文件褐啡,文件不存在則創(chuàng)建一個文件) |
a+或ab+ | 以添加方式打開文件,打開文件并在末尾更改文件,若文件不存在則創(chuàng)建文件 |
注意:
- b是二進(jìn)制模式的意思鳖昌,b只是在Windows有效备畦,在Linux用r和rb的結(jié)果是一樣的
- Unix和Linux下所有的文本文件行都是\n結(jié)尾低飒,而Windows所有的文本文件行都是\r\n結(jié)尾
- 在Windows平臺下,以“文本”方式打開文件懂盐,不加b:
- 當(dāng)讀取文件的時候褥赊,系統(tǒng)會將所有的 "\r\n" 轉(zhuǎn)換成 "\n"
- 當(dāng)寫入文件的時候,系統(tǒng)會將 "\n" 轉(zhuǎn)換成 "\r\n" 寫入
- 以"二進(jìn)制"方式打開文件莉恼,則讀\寫都不會進(jìn)行這樣的轉(zhuǎn)換
- 在Unix/Linux平臺下拌喉,“文本”與“二進(jìn)制”模式?jīng)]有區(qū)別,"\r\n" 作為兩個字符原樣輸入輸出
int main(void)
{
FILE *fp = NULL;
// "\\"這樣的路徑形式类垫,只能在windows使用
// "/"這樣的路徑形式,windows和linux平臺下都可用琅坡,建議使用這種
// 路徑可以是相對路徑悉患,也可是絕對路徑
fp = fopen("../test", "w");
//fp = fopen("..\\test", "w");
if (fp == NULL) //返回空,說明打開失敗
{
//perror()是標(biāo)準(zhǔn)出錯打印函數(shù)榆俺,能打印調(diào)用庫函數(shù)出錯原因
perror("open");
return -1;
}
return 0;
}
3. 文件的關(guān)閉
任何文件在使用后應(yīng)該關(guān)閉:
- 打開的文件會占用內(nèi)存資源售躁,如果總是打開不關(guān)閉,會消耗很多內(nèi)存
- 一個進(jìn)程同時打開的文件數(shù)是有限制的茴晋,超過最大同時打開文件數(shù)陪捷,再次調(diào)用fopen打開文件會失敗
- 如果沒有明確的調(diào)用fclose關(guān)閉打開的文件,那么程序在退出的時候诺擅,操作系統(tǒng)會統(tǒng)一關(guān)閉市袖。
#include <stdio.h>
int fclose(FILE * stream);
功能:關(guān)閉先前fopen()打開的文件。此動作讓緩沖區(qū)的數(shù)據(jù)寫入文件中烁涌,并釋放系統(tǒng)所提供的文件資源苍碟。
參數(shù):
stream:文件指針
返回值:
成功:0
失敗:-1
FILE * fp = NULL;
fp = fopen("abc.txt", "r");
fclose(fp);
4.文件的順序讀寫
1.按照字符讀寫文件fgetc撮执、fputc
1)寫文件
#include <stdio.h>
int fputc(int ch, FILE * stream);
功能:將ch轉(zhuǎn)換為unsigned char后寫入stream指定的文件中
參數(shù):
ch:需要寫入文件的字符
stream:文件指針
返回值:
成功:成功寫入文件的字符
失斘⒎濉:返回-1
char buf[] = "this is a test for fputc";
int i = 0;
int n = strlen(buf);
for (i = 0; i < n; I++)
{
//往文件fp寫入字符buf[i]
int ch = fputc(buf[i], fp);
printf("ch = %c\n", ch);
}
2)讀文件
#include <stdio.h>
int fgetc(FILE * stream);
功能:從stream指定的文件中讀取一個字符
參數(shù):
stream:文件指針
返回值:
成功:返回讀取到的字符
失敗:-1
char ch;
#if 0
// EOF表示文件結(jié)束符(end of file)抒钱。在while循環(huán)中以EOF作為文件結(jié)束標(biāo)志
while ((ch = fgetc(fp)) != EOF)
{
printf("%c", ch);
}
printf("\n");
#endif
while (!feof(fp)) //文件沒有結(jié)束蜓肆,則執(zhí)行循環(huán)
{
ch = fgetc(fp);
printf("%c", ch);
}
printf("\n");
按照行讀寫文件fgets、fputs
1)寫文件
#include <stdio.h>
int fputs(const char * str, FILE * stream);
功能:將str所指定的字符串寫入到stream指定的文件中谋币,字符串結(jié)束符 '\0' 不寫入文件仗扬。
參數(shù):
str:字符串
stream:文件指針
返回值:
成功:0
失敗:-1
char *buf[] = { "123456\n", "bbbbbbbbbb\n", "ccccccccccc\n" };
int i = 0;
int n = 3;
for (i = 0; i < n; I++)
{
int len = fputs(buf[i], fp);
printf("len = %d\n", len);
}
2)讀文件
#include <stdio.h>
char * fgets(char * str, int size, FILE * stream);
功能:從stream指定的文件內(nèi)讀入字符蕾额,保存到str所指定的內(nèi)存空間厉颤,直到出現(xiàn)換行字符、讀到文件結(jié)尾或是已讀了size - 1個字符為止凡简,最后會自動加上字符 '\0' 作為字符串結(jié)束逼友。
參數(shù):
str:字符串
size:指定最大讀取字符串的長度(size - 1)
stream:文件指針
返回值:
成功:成功讀取的字符串
讀到文件尾或出錯: NULL
char buf[100] = 0;
while (!feof(fp)) //文件沒有結(jié)束
{
memset(buf, 0, sizeof(buf));
char *p = fgets(buf, sizeof(buf), fp);
if (p != NULL)
{
printf("buf = %s", buf);
}
}
3.按照格式化文件fprintf精肃、fscanf
1)寫文件
#include <stdio.h>
int fprintf(FILE * stream, const char * format, ...);
功能:根據(jù)參數(shù)format字符串來轉(zhuǎn)換并格式化數(shù)據(jù),然后將結(jié)果輸出到stream指定的文件中帜乞,指定出現(xiàn)字符串結(jié)束符 '\0' 為止司抱。
參數(shù):
stream:已經(jīng)打開的文件
format:字符串格式,用法和printf()一樣
返回值:
成功:實際寫入文件的字符個數(shù)
失斃枇摇:-1
fprintf(fp, "%d %d %d\n", 1, 2, 3);
2)讀文件
#include <stdio.h>
int fscanf(FILE * stream, const char * format, ...);
功能:從stream指定的文件讀取字符串习柠,并根據(jù)參數(shù)format字符串來轉(zhuǎn)換并格式化數(shù)據(jù)。
參數(shù):
stream:已經(jīng)打開的文件
format:字符串格式照棋,用法和scanf()一樣
返回值:
成功:參數(shù)數(shù)目资溃,成功轉(zhuǎn)換的值的個數(shù)
失敗: - 1
int a = 0;
int b = 0;
int c = 0;
fscanf(fp, "%d %d %d\n", &a, &b, &c);
printf("a = %d, b = %d, c = %d\n", a, b, c);
4.按照塊讀寫文件fread烈炭、fwrite
1)寫文件
#include <stdio.h>
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:以數(shù)據(jù)塊的方式給文件寫入內(nèi)容
參數(shù):
ptr:準(zhǔn)備寫入文件數(shù)據(jù)的地址
size: size_t 為 unsigned int類型溶锭,此參數(shù)指定寫入文件內(nèi)容的塊數(shù)據(jù)大小
nmemb:寫入文件的塊數(shù),寫入文件數(shù)據(jù)總大小為:size * nmemb
stream:已經(jīng)打開的文件指針
返回值:
成功:實際成功寫入文件數(shù)據(jù)的塊數(shù)目符隙,此值和nmemb相等
失斉客薄:0
typedef struct Stu
{
char name[50];
int id;
}Stu;
Stu s[3];
int i = 0;
for (i = 0; i < 3; I++)
{
sprintf(s[i].name, "stu%d%d%d", i, i, i);
s[i].id = i + 1;
}
int ret = fwrite(s, sizeof(Stu), 3, fp);
printf("ret = %d\n", ret);
2)讀文件
#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:以數(shù)據(jù)塊的方式從文件中讀取內(nèi)容
參數(shù):
ptr:存放讀取出來數(shù)據(jù)的內(nèi)存空間
size: size_t 為 unsigned int類型,此參數(shù)指定讀取文件內(nèi)容的塊數(shù)據(jù)大小
nmemb:讀取文件的塊數(shù)霹疫,讀取文件數(shù)據(jù)總大小為:size * nmemb
stream:已經(jīng)打開的文件指針
返回值:
成功:實際成功讀取到內(nèi)容的塊數(shù)拱绑,如果此值比nmemb小,但大于0丽蝎,說明讀到文件的結(jié)尾猎拨。
失敗:0
typedef struct Stu
{
char name[50];
int id;
}Stu;
Stu s[3];
int ret = fread(s, sizeof(Stu), 3, fp);
printf("ret = %d\n", ret);
int i = 0;
for (i = 0; i < 3; I++)
{
printf("s = %s, %d\n", s[i].name, s[i].id);
}
5.文件緩沖區(qū)
ANSI C標(biāo)準(zhǔn)采用“緩沖文件系統(tǒng)”處理數(shù)據(jù)文件屠阻。
所謂緩沖文件系統(tǒng)是指系統(tǒng)自動地在內(nèi)存區(qū)為程序中每一個正在使用的文件開辟一個文件緩沖區(qū)從內(nèi)存向磁盤輸出數(shù)據(jù)必須先送到內(nèi)存中的緩沖區(qū)迟几,裝滿緩沖區(qū)后才一起送到磁盤去。
如果從磁盤向計算機讀入數(shù)據(jù)栏笆,則一次從磁盤文件將一批數(shù)據(jù)輸入到內(nèi)存緩沖區(qū)(充滿緩沖區(qū))类腮,然后再從緩沖區(qū)逐個地將數(shù)據(jù)送到程序數(shù)據(jù)區(qū)(給程序變量) 。
磁盤文件的存取
- 磁盤文件蛉加,一般保存在硬盤蚜枢、U盤等掉電不丟失的磁盤設(shè)備中,在需要時調(diào)入內(nèi)存
- 在內(nèi)存中對文件進(jìn)行編輯處理后针饥,保存到磁盤中
- 程序與磁盤之間交互厂抽,不是立即完成,系統(tǒng)或程序可根據(jù)需要設(shè)置緩沖區(qū)丁眼,以提高存取效率
更新緩沖區(qū)
#include <stdio.h>
int fflush(FILE *stream);
功能:更新緩沖區(qū)筷凤,讓緩沖區(qū)的數(shù)據(jù)立馬寫到文件中。
參數(shù):
stream:文件指針
返回值:
成功:0
失敗:-1