C/C++ | C 語言學(xué)習(xí)&總結(jié)

介紹

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步:

  1. 預(yù)處理:宏定義展開缠诅、頭文件展開溶浴、條件編譯等,同時將代碼中的注釋刪除管引,這里并不會檢查語法
  2. 編譯:檢查語法士败,將預(yù)處理后文件編譯生成匯編文件
  3. 匯編:將匯編文件生成目標(biāo)文件(二進(jìn)制文件)
  4. 鏈接: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)存空間大小饥臂。

image.png

常量:

  • 在程序運行過程中,其值不能被改變的量
  • 常量一般出現(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)換,以保證精度不降低阳啥。


image.png

隱式轉(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語句

image.png

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語句

image.png

int main()
{
    int a = 20;
    while (a > 10)
    {
        scanf("%d", &a);
        printf("a = %d\n", a);
    }
    return 0;
}

2. do…while語句

image.png
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ù)的宵睦。


image.png

數(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ù)
image.png

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ù)都沒用拐格。

image.png

注意:

  • 允許在不同的函數(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)回收媒殉。

image.png
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)體類型變量(無類型名)
image.png

結(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)行各種操作键痛。


image.png

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ū)(給程序變量) 。

磁盤文件的存取

image.png

  • 磁盤文件蛉加,一般保存在硬盤蚜枢、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
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末藐守,一起剝皮案震驚了整個濱河市挪丢,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌卢厂,老刑警劉巖乾蓬,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異慎恒,居然都是意外死亡任内,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門融柬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來死嗦,“玉大人,你說我怎么就攤上這事粒氧≡匠” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵靠欢,是天一觀的道長廊敌。 經(jīng)常有香客問我铜跑,道長门怪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任锅纺,我火速辦了婚禮掷空,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘囤锉。我一直安慰自己坦弟,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布官地。 她就那樣靜靜地躺著酿傍,像睡著了一般。 火紅的嫁衣襯著肌膚如雪驱入。 梳的紋絲不亂的頭發(fā)上赤炒,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天,我揣著相機與錄音亏较,去河邊找鬼莺褒。 笑死,一個胖子當(dāng)著我的面吹牛雪情,可吹牛的內(nèi)容都是我干的遵岩。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼巡通,長吁一口氣:“原來是場噩夢啊……” “哼尘执!你這毒婦竟也來了舍哄?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤正卧,失蹤者是張志新(化名)和其女友劉穎蠢熄,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體炉旷,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡签孔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了窘行。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片饥追。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖罐盔,靈堂內(nèi)的尸體忽然破棺而出但绕,到底是詐尸還是另有隱情,我是刑警寧澤惶看,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布捏顺,位于F島的核電站,受9級特大地震影響纬黎,放射性物質(zhì)發(fā)生泄漏幅骄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一本今、第九天 我趴在偏房一處隱蔽的房頂上張望拆座。 院中可真熱鬧,春花似錦冠息、人聲如沸挪凑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽躏碳。三九已至,卻和暖如春散怖,著一層夾襖步出監(jiān)牢的瞬間菇绵,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工杭抠, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留脸甘,地道東北人。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓偏灿,卻偏偏與公主長得像丹诀,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 指針是C語言中廣泛使用的一種數(shù)據(jù)類型铆遭。 運用指針編程是C語言最主要的風(fēng)格之一硝桩。利用指針變量可以表示各種數(shù)據(jù)結(jié)構(gòu); ...
    朱森閱讀 3,440評論 3 44
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,097評論 1 32
  • 第十章 指針 1. 地址指針的基本概念: 在計算機中枚荣,所有的數(shù)據(jù)都是存放在存儲器中的碗脊。一般把存儲器中的一個字節(jié)稱為...
    堅持到底v2閱讀 1,069評論 2 3
  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy閱讀 9,516評論 1 51
  • C語言是面向過程的,而C++是面向?qū)ο蟮?第一章概述 1. C語言的特點 語言簡潔橄妆、緊湊衙伶,使用方便、靈活害碾。共有32...
    小辰帶你看世界閱讀 1,189評論 0 1