1. 程式人生 > >C語言從入門到精通,看這一篇就夠了

C語言從入門到精通,看這一篇就夠了

影響 內容 當前 位置 replace 雙精度 下標 寄存器變量 一個

No.1 計算機與程序設計語言的關系

計算機系統由硬件系統和軟件系統構成,硬件相當於人類的肉體,而軟件相當於人類的靈魂,如果脫離了靈魂,人類就是一具行屍走肉

No.2 C語言的特點

  • 代碼簡潔,靈活性高
  • 語言簡單易懂
  • 生成目標代碼質量高,效率高
  • 允許直接訪問物理地址,操作硬件
  • 可移植性較好
  • 數據的封裝,在安全性上存在很大缺陷
  • 對於字符串處理,只能通過自負數組實現,繪圖操作復雜
  • 類型檢查機制較弱,缺乏支持代碼重用的語言結構

No.3 國際慣例HelloWorld

#include <stdio.h>  
int main()  
{  
    printf("Hello World!\n");  
    return 0;  
} 

No.4 C程序的編譯與運行

當我們在編譯器上將代碼寫好,此時的文件是.c文件,該文件是C語言程序源代碼的擴展名,此時的程序叫源程序,當程序通過編譯器進行編譯時,此時會產生一個目標程序.obj,將目標程序和庫文件連接成機器碼文件,就是exe可執行程序

No.5 配置Clion

  • Clion安裝,直接點點點
  • 破解方法,傳送門
  • 安裝Cygwin

    • 下載Cywin,傳送門
    • 點點點,直到direct connection,添加網易鏡像源<http://mirrors.163.com/cygwin/>
    • 選擇安裝的模塊,依次搜索 gcc-core、gcc-g++、make、gdb、binutils,選擇devel文件夾,點擊前面的skip,將skip切換成版本號
    • 繼續奠奠奠
  • 配置Cygwin,打開Clion,選擇File->Setting->Build, Execution, Deployment->Toolchains

No.5 基本字符集

標識符

什麽是標識符 ?

在C語言中,變量名、常量名、關鍵字、函數名、方法名都屬於標識符

標識符如何命名?

  • 只能有字母、數字、下劃線組成,且數字不能打頭
  • 嚴格區分大小寫,變量和函數一般都是小寫字母命名

關鍵字

什麽是關鍵字?

C語言中具有特殊含義的英文單詞,通常用於構成語句,存儲結構、定義數據類型等

關鍵字有哪些?

  • 基本數據類型
    • char 聲明字符串類型
    • iint 聲明整型類型
    • float 聲明浮點型
    • double 聲明雙精度浮點型
    • void 空類型
  • 類型修飾
    • short 聲明短類型
    • llong 聲明長類型
    • signed 聲明有符號型
    • unsigned 聲明無符號型
  • 其他數據類型
    • enum 聲明枚舉類型
    • struct 聲明結構體
    • union 聲明共用體
  • 流程控制
    • 條件判斷 if/else/switch/case/default/goto
    • 循環 while/do/for/break/continue
    • 返回 return
  • 存儲類型
    • auto 自動變量
    • static 靜態類型
    • register 寄存器變量
    • extern 外部變量
  • 其他
    • sizeof 查看類型長度
    • typedef 聲明類型別名
    • const 指定變量不能被當前線程修改
    • volattile 強制編譯器每次從內存中讀取該變量的值

分隔符

  • ; 語句結尾
  • {} 函數體
  • () 函數定義時用來包含參數,還可以用來修改運算順序
  • [] 定義數組類型
  • . 結構體引用成員
  • , 參數分割
  • // 單行註釋
  • /**/ 多行註釋

No.6 數據類型

  • 整型 用於準確地表示整數,根據表示範圍的不同分為以下三種: 短整型(short) < 整型(int) < 長整型(long)

  • 浮點型 用於標識實數(小數)根據範圍和精度不同分為以下兩種: 單精度浮點數(float) < 雙精度浮點數(double)

  • 字符型 用來描述單個字符,char除了上面三種以外,還可以使用long、short、signed、unsigned來修飾不同的數據類型

數據類型的轉換

  • 強制類型轉換 (類型關鍵字)(表達式)

    • 例如(int)5.2/2,首先將5.2轉換成5,然後再用5/2,結果是2
  • 自動類型轉換
    • 不同數據類型的變量混合運算時,會自動升級成數據類型最大的那個,所有浮點運算都是以double進行運算的,即使最大的數據類型僅僅是float
    • 賦值時,右側表達式的值轉換為左邊變量的類型

No.7 常量與變量

常量

  • 整型常量
    • 十進制常量,正負號開頭,後面跟0-9中的數字,正號可以省略
    • 八進制,正負號開頭,後面跟0-7中的數字,正號可以省略
    • 十六進制,正負號開頭,接著是0x,後面跟0-9中的數字或a-f,其中a-f分別表示10-15,正號可以省略
    • 除上述情況外,還可以在常量屁股後面加L或U,L表示長整型,U表示無符號型
  • 實型常量
    • 十進制小數,由0-9和小數點組成
    • 十進制指數,由0-9、小數點和e組成
  • 字符常量

    • 用單引號括起來的一個普通字符或轉義字符
    • 字符常量在存儲時,保存的是該字符對應的ASCII碼
  • 字符串常量
    • 用一對雙引號括起來的或者多個字符構成的字符串序列
    • 系統會在存儲字符串的時候自動加上\0,表示字符串結束的標誌
  • 符號常量
    • 又叫宏常量,是一個預處理命令:#define 常量名 常量值
    • 定義後,程序中所有出現這個標識符的地方都會用該常量值替換

變量

什麽是變量?

程序在運行過程中,它的值可以隨著程序的運行不斷的動態修改,變量代表的內存的一個空間,而變量名的作用是可以通過變量名找到這個變量名對應的那個變量的空間

定義變量到底做了什麽?

int a = 123,首先在內存中開辟了4個字節的內存空間,並且並這個內存空間起了一個名字,叫做a,後面的= 123,這一步完成的操作是變量的初始化,通過a這個變量,就那個內存空間存儲了123這個數據

No.8 運算符與表達式

  • 算術運算符

    • +、-、*、/、%分別表示加、減、乘、除、取余

    • 除號前後都是整數,結果才是整數,否則,結果是小數,取余運算要求前後都是整數
#include <stdio.h>

int main() {
    int num;
    printf("請輸入一個5位數:");
    scanf("%d",&num);
    printf("倒敘輸出:\n");
    for (int i = 0; i < 5; i++) {
        printf("%d",num % 10);
        num /= 10;
    }
    printf("\n");
    return 0;
}
  • 關系運算符

    • <、<=、>、>=、==、!=分別表示小於、小於等於、大於、大於等於、等於和不等於
    • 關系表達式運行的結果是一個邏輯值,即0或1
#include <stdio.h>

int main() {
    int a = 1,b = 2,c = 3;
    int x,y;
    x = a + b == c; // x = 1
    y = 11 >b != a; // y = 0
    printf("x = %d,y = %d\n",x,y);
    return 0;
}
  • 邏輯運算符

    • !、&&、||分別是邏輯非、邏輯與、邏輯或
    • &&前面的表達式為假,就不會運行第二個表達式,||前面的表達式為真,就不會運行第二個表達式
#include <stdio.h>

int main() {
    int year;
    printf("請輸入年份:");
    scanf("%d",&year);
    if ((!(year % 100) && !(year % 400))||year % 4 == 0){
        printf("%d年是閏年",year);
    }
    else{
        printf("%d年是平年",year);
    }
    return 0;
}
  • 賦值運算符

    • +=、-=、*=、/=、%=、=分別是加等、減等、乘等、除等、模等、等於
  • 條件表達式

    • a?b:c
    • a為true,執行b,反之,執行c
#include <stdio.h>

int main() {
    int a = 1;
    int b = 2;
    int c = 3;
    int max = 0;
    max = a>b?(a>c?a:c):(b>c?b:c);
    printf("max=%d",max);
    return 0;
}
  • 逗號表達式

    • 從左到右依次執行每個表達式
    • 賦值的話取最右邊表達式的值
#include <stdio.h>

int main() {
    int a = 1;
    int b = 2;
    int c = 3;
    c = (a = 10,b = 10,2 * a + 5,a + a * b + c);
    printf("a = %d, b = %d, c = %d\n",a,b,c); // a = 10, b = 10, c = 113
    return 0;
}
  • 長度測試運算符

    • sizeof 通常用來測試某個數據類型占用的內存空間大小
#include <stdio.h>

int main() {
    int a = 1;
    float b = 2.0;
    char c = ‘3‘;
    printf("%d %d %d\n", sizeof(a), sizeof(b), sizeof(c)); // 4 4 2
    return 0;
}
  • 運算符的優先級

    • () > ! > 算術運算符 > 關系運算符 > 邏輯運算符 > 賦值運算符

No.9 輸入與輸出

  • 字符輸入、輸出函數
    • getchar() 例如,str = getchar();
    • putchar() 例如,putchar(str);
  • 格式化輸入、輸出函數
    • scanf() 例如,scanf("%c",&str);
    • printf() 例如,printf("%c",str);

No.10 基本程序結構

  • 順序結構與判斷結構if的使用

    技術分享圖片

  • 判斷結構switch的用法

    技術分享圖片

  • 循環結構

    技術分享圖片

  • 流程跳轉控制語句
    • break 只能用來循環和switch中,跳出switch或跳出循環,在多層循環中,只能跳出一層循環
    • continue 跳出循環體中該語句下面的業務邏輯,繼續執行下一次循環

No.11 數組

一維數組與二維數組

什麽是數組?

將數據類型一致的多個變量存放在一塊連續的的內存空間中,元素按照索引的方式連續存放、訪問

一維數組

  • 數組定義 數據類型 數組名 [數組長度],例如 int num[10];

  • 初始化

    • int num[] = {1,2,3,4,5}; 所有元素都賦值過了,所以不需要指定數組長度
    • int num[5] = {1, 2 , 3}; 賦值了部分元素,沒有賦值的系統會被初始化為0

    • int num[]; 全部初始化為0
  • 引用 數組下標都是從0開始的

二維數組

  • 數組定義 數據類型 數組名 [數組長度][數組長度],例如 int nums[10][10];
  • 初始化

    • 連續賦值 int[2][2] = {1,2,3,4};
    • 分段賦值 int[2][2] = {{1,2},{3,4}}
    • 部分賦值 int[2][2] = {{1,2},{3}}
    • 全部不賦值 int[2][2] = {};系統全部賦值為0
    • 如果全部元素都賦值了,可以省略第一維的長度
  • 引用 和一維數組類似

冒泡排序

#include <stdio.h>

int main() {
    int nums[10];
    // 隨機為數組賦值
    for (int i = 0; i < 10; ++i) {
        nums[i] = rand()%100+1;
    }
    printf("原數組: \n");
    // 編譯原數組
    for (int i = 0; i < 10; ++i) {
        printf("%d ",nums[i]);
    }
    // 冒泡排序
    for (int i = 0; i < 10; ++i) {
        for (int j = 0; j < i; ++j) {
            if(nums[j]>nums[j+1]){
                int temp = nums[j];
                nums[j] = nums[j+1];
                nums[j+1] = temp;
            }
        }
    }
    printf("\n");
    printf("新數組: \n");
    for (int i = 0; i < 10; ++i) {
        printf("%d ",nums[i]);
    }
    return 0;
}

字符串與字符數組

前面我們學習了數組的定義,假如我需要存儲一個名字,就需要char[5] = {‘k‘, ‘e‘, ‘r‘, ‘n‘, ‘e‘, ‘l‘},但是這樣當我們需要使用名字的時候就需要遍歷整個數組,於是,我們可以把它當作一個整體來保存,這就是字符串

初始化

  • char name[7] = "kernel";

  • char name[7] = {"kernel"};

  • 可以不指定數組長度,數組的長度必須比有效字符個數多一個,用於存儲\0,字符串結束標誌

輸入輸出函數

  • scanf() 例如 scanf("%s",name);

  • gets() 例如 gets(name);

  • printf() 例如 printf("%s",name);

  • puts() 例如 puts(name);

復制

#include <stdio.h>
#include <string.h>

int main() {
    char name[7] = {"kernel"};
    char names[7];
    strcpy(names, name);
    puts(names);
    return 0;
}

拼接

#include <stdio.h>
#include <string.h>

int main() {
    char str[10] = {"hello"};
    strcat(str, "-world");
    puts(str);
}

比較

#include <stdio.h>
#include <string.h>

int main() {
    char name[7] = "kernel";
    char names[7] = "kernel";
    // strcmp函數,從左到右根據ASCII碼進行比較,知道遇到不同的字符為止
    // 相同返回0,第一個變量 > 第二個變量,返回一個比0大的數,否則,返回一個比0小的數
    // 直接使用==比較的是兩個變量的內存地址
    printf("%d",strcmp(name,names));
    printf("%d",name == names);
    return 0;
}

長度

#include <stdio.h>
#include <string.h>

int main() {
    char name[] = "kernel";
    printf("字符串的長度=%d",strlen(name));
    return 0;
}

No.12 函數

函數是什麽?

C語言作為結構化的程序設計語言,模塊化是一種重要的設計思想,在模塊化設計中,我們通常將復雜的任務分割成若幹模塊的組合,讓每個模塊實現一定的功能,我們將這些模塊成為函數,使用函數可以提高代碼的可讀性和復用性

函數的聲明

當我們想調用自定義函數時,我們需要讓系統知道存在這麽一個函數,所以我們需要對函數進行聲明,將自定義函數寫在main函數之前的不用進行聲明,寫在main函數後面的函數需要進行聲明

函數的定義

  • 函數類型

    • 無返回值 void
    • 有返回值根據返回值的類型確定函數類型
  • 函數名

    • 遵循小駝峰命名法,見名知意
  • 形參與實參

    • 實參 實參是調用函數時傳入的參數
    • 形參 形參是函數定義的時候使用的,形參定義時需指定類型,但是只有在被調用時才分配內存單元,只有在該函數中可以正常調用,其他地方會報錯
  • 函數體

    • 函數的主體結構
  • 返回值
    • 函數的返回值需要跟函數類型保持一致,只能返回一個值,給程序提供一個出口

函數的使用

# 函數的聲明
int sum(int, int);
# 函數體
int sum(int x, int y){
    return x + y;
}
# 函數的使用
int result = sum(10, 20);

函數的傳值與傳址

傳值是單向傳遞,形參的改變並不影響實參,實參和形參各自占用一段內存空間

傳址是將實參的地址作為值傳遞給形參,實參與形參指向同一塊內存空間,所以形參的改變會影響實參,在C語言中通過傳址傳遞數據的一般是數據和指針,但是操作大批量數據時,會影響性能

函數的遞歸和嵌套

  • 嵌套 在一個函數中調用其他的函數,但是不可以調用mian函數
  • 遞歸 在該函數中不僅可以調用其他的函數,還可以調用自己,這種方式成為遞歸
    • 直接遞歸 函數中直接調用自己
    • 間接遞歸 函數中調用其他函數,其他函數又調用該函數
#include <stdio.h>
#include <stdlib.h>
int main() {
    int box(int);
    int n;
    long num;
    printf("請輸入一個整數:\n");
    scanf("%d",&n);
    num = box(n);
    printf("%d的階乘是:%d\n",n,num);
}
int box(int n){
    if (n < 0){
        printf("輸入數據有誤");
        exit(0);
    }
    if (n == 0 || n == 1){
        return 1;
    }
    else{
        return n * box(n-1);
    }
}

No.13 指針

訪問變量有兩種方式

  • 間接訪問 通過指針指向變量,先獲得變量地址,然後根據地址去訪問對應的存儲單元
  • 直接訪問 通過變量名或地址直接訪問

指針的使用

  • 定義 類型說明符號 *變量名
  • 初始化 可以使用&取變量地址 int *p = &a
  • 訪問 可以使用*來訪問指針指向的變量

指針與一維數組

  • 指向數組元素 用指針指向數組的某個元素 int *p;p = &num[4];

  • 指向數組 用指針指向數組,數組名存儲的是該數組的首地址,int *p;p = num;

  • 引用數組元素
    • 訪問數組的第2個元素 num[2]
    • 訪問數組的第2個元素 *(a+2)
    • 訪問數組的第2個元素 *(p+2)

指針與字符串

字符串的定義除了之前提到的字符數組外,還有另外一種方式,現在我們可以使用char *str = "Hello",系統為該字符串分配相應的連續內存空間,將存儲空間的首地址賦值給指針變量str

指針與二維數組

二維數組是按行與列存儲數據的,可以看成若幹行個長度為若幹列一維數組,如num[4][2],就可以看成4個長度為2的一維數組,num代表二維數組首元素的地址,即&num[0],我們可以將num[0]看作指向num[0][0]的指針,那麽num[0]+1即指向num[0][1],同理num[1]+2就指向num[1][2],通過前面的一維數組,我們知道num[i]等價於(num+i),同樣的,擴展到二維數組上面來,num[i][j]與*(num+i)+j、num[i]+j都是等價的,而求num[i][j]的值,用、*(num[i]+j)與、*((num+i)+j)

函數與指針

  • 指針作為函數的形參
#include <stdio.h>

int main(){
    void replace(char *, char);
    char str[] = "how do you do";
    char c = ‘o‘;
    replace(str,c);
    puts(str);
}
void replace(char *s,char c){
    char *p;
    char *q;
    for (p = s; *p && *p != c; p++) {
        q = p;
        while (*p){
            while (*p && *p == c){
                p++;
            }
            while (*p && *p != c){
                *q++ = *p++;
            }
        }
        *q = ‘\0‘;
    }
}
  • 指向函數的指針 函數的首地址一般為函數的指針,如果我們聲明一個指針指向函數名,就可以通過這個變量執行函數
#include <stdio.h>

void points(int x, int y, int(*p)()){
    int res;
    res = (*p)(x,y);
    printf("%d",res);
}

int main(){
    int max(int, int);
    int min(int, int);
    int x,y;
    scanf("%d",&x);
    scanf("%d",&y);
    points(x,y,max);
    points(x,y,min);
}

int max(int x, int y){
    if (x > y){
        return x;
    } else{
        return y;
    }
}

int min(int x, int y){
    if (x > y){
        return y;
    } else{
        return x;
    }
}
  • 指針函數 函數的返回值是一個地址的函數成為指針函數
#include <stdio.h>

char *day_name(int n){
    static char name[7][20] = {
            "Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"
    };
    return (n <1 || n>7)?name[0]:name[n];
}

int main(){
    int i;
    printf("請輸入一個星期編號:\n");
    scanf("%d",&i);
    printf("編號%d對應的星期為:%s",i,day_name(i));
}

指針數組

保存保存多個字符串的方式有兩種

  • 二維數組 char name[4][10] = {"kernel","alex","eric","nino"};
  • 指針數組char *p_name[4] = {"kernel","alex","eric","nino"};

二級指針

二級指針通常用來存儲指針數組的地址以及實現指針數組元素的訪問

使用方法 int a,*b,**c;b = &b;c = &b;

#include <stdio.h>

int main(){
    int a[5] = {1,2,3,4,5};
    int *b[5];
    int **c;
    for (int i = 0; i < 5; ++i) {
        b[i] = &a[i];
    }
    c = b;
    for (int i = 0; i < 5; ++i, ++c) {
        printf("%d\n",**c);
    }
}
//1
//2
//3
//4
//5

內存的動態分配

內存中存放了什麽?

  • 棧 函數調用返回地址,形參,局部變量等程序運行信息

  • 堆 自由存儲區域,動態分配申請該空間

  • 靜態存儲區 存放全局變量與靜態變量,編譯期就分配號內存了
  • 程序代碼存儲區 存放正在執行的代碼

動態分配函數

  • malloc 分配若幹字節的連續內存空間,分配成功返回指向該存儲區的首地址,否則返回空指針
  • calloc 分配若幹字節的連續內存空間,分配成功返回指向該存儲區的首地址,否則返回空指針
  • realloc 將p所指的存儲區大小更改為n,如果成功,就不能再通過p使用原來的存儲空間,反之,返回空,p依舊指向原來的位置 int *q;q=(int)realloc(p,MAX_SIZE\sizeof(int))
  • free 釋放動態申請的空間給系統,由系統再次支配
#include <stdio.h>
#include <stdlib.h>
#define N 10
int main()
{
    int *p = 0;
    int i, num, *q = 0;
    p = (int *) malloc((N * sizeof(int)));
    if (p == 0)
    {
        printf("內存分配錯誤!\n");
        exit(0);
    }
    printf("請輸入要存儲的元素個數:\n");
    scanf("%d", &num);
    if (num <= N) {
        for (i = 0; i < num; i++){
            scanf("%d", p + i);
        }
    } else {
        for (i = 0; i < num; i++){
            q = (int *) realloc(p, (N + N) * sizeof(int));
        }
        if (q == 0){
            exit(0);
        }
        for (i = 0; i < num; i++){
            scanf("%d", q + i);
        }
        p = q;
    } for (i = 0; i < num; i++){
        printf("%3d", *(p + i));
    }

    printf("\n");
    free(p);
    return(0);
}

使用const修飾指針變量

const關鍵字修飾的數據類型是指常類型,最簡單的用法是用來定義const
常量,具有不變性,const修飾的常量在聲明的時候就要進行初始化賦值,不然後面是不能賦值的

  • int const *a; //修飾指向的內容,a可變,a指向的內容不可變
  • int *const a; //修飾指針a,a不可變,a指向的內容可變
  • const int *a; //修飾指向的內容,a可變,a指向的內容不可變
  • const int *const A; //指針a和a指向的內容都不可變

No.13 結構體

結構體類型定義

struct student{
    int id; //學號
    char name[20]; //姓名
    char sex[4]; //性別
    int age; //年齡
};

結構體變量定義

  • 先定義結構體類型,再定義結構體變量
struct sudent{
    int id; //學號
    char name[20]; //姓名
    char sex[4]; //性別
    int age; //年齡
};
struct student stu1,stu2;
  • 定義結構體類型的同時定義結構體變量
struct student{
    int id; //學號
    char name[20]; //姓名
    char sex[4]; //性別
    int age; //年齡
}stu1,stu2;
  • 省略結構體名稱,直接定義結構體變量
    struct{
    int id; //學號
    char name[20]; //姓名
    char sex[4]; //性別
    int age; //年齡
    }stu1,stu2;

結構體變量初始化

struct stu1;
stu1 = {10001,‘kernel‘,‘boy‘,18};

結構體變量的訪問

  • 使用結構體變量.成員名
  • 使用結構體變量->成員名

結構體訪問數組

  • 使用結構體數組[下標].成員名
  • 使用結構體數組[下標]->成員名

No.14 共用體

使用方法和結構體一樣

區別

  • 結構體占據連續存儲區域,每個成員都擁有自己的存儲空間

  • 共用體將不同數據類型的所有成員存儲在一段存儲區域內,各個成員共享一塊存儲空間

  • 空間的大小取決於占存儲空間最大的那個成員

  • 不同時刻可以表現為不同的數據類型和數據長度,但是同一時刻只有一個成員的值有意義

  • 使用union關鍵字
#include <stdio.h>

union type{
    int class_id; //班級
    char title[20]; //職稱
};

struct Person{
    char name[10]; //姓名
    char sex; //性別
    int age; //年齡
    int flag; //標記用戶
    union type t;
};

int main(){
    struct Person person[10];
    int i;
    int num;
    printf("請輸入要錄入的人員數量:\n");
    scanf("%d",&num);
    for (int i = 0; i < num; i++) {
        printf("請輸入ID為%d的用戶的信息:\n",i + 1);
        printf("姓名:");
        scanf("%s",&person[i].name);
        getchar();
        printf("性別:");scanf("%c",&person[i].sex);getchar();
        printf("年齡:");
        scanf("%d",&person[i].age);
        getchar();
        printf("標識符:");
        scanf("%d",&person[i].flag);
        getchar();
        if (person[i].flag == 0){
            printf("請輸入班級ID:");
            scanf("%d",&person[i].t.class_id);
        } else if (person[i].flag == 1){
            printf("請輸入教師的職稱:");
            scanf("%d",&person[i].t.title);
        } else{
            printf("輸入有誤,請重新輸入!\n");
            i -= 1;
        }
    }
    printf("輸出所有用戶的信息:\n");
    for (int i = 0; i < num; i++) {
        printf("輸出ID為%d的用戶的信息:\n",i + 1);
        printf("姓名:");puts(person[i].name);
        printf("性別: \n");printf("%c",person[i].sex);
        printf("年齡:\n");printf("%d",person[i].age);
        if (person[i].flag == 0){
            printf("學生的班級ID:");
            printf("%d\n",person[i].t.class_id);
        } else if (person[i].flag == 1){
            printf("教師的職稱:");
            puts(person[i].t.title);
        }
    }

}

No.15 枚舉

我們定義一些變量的時候,比如性別,不是男的就是女的,對於這些可控的變量值,我們就可以使用枚舉來表示

#include <stdio.h>

int main(){
    enum sex{
        man,
        woman
    }s;
    char msg[2][10] = {"男性","女性"};
    for (s = man; s <= woman; s++) {
        switch (s){
            case man:puts(msg[man]);
                break;
            case woman:puts(msg[woman]);
                break;
        }
    }
}

No.16 類型定義符

我們可以自定義數據類型,可以通過類型定義符起一個別名

#include <stdio.h>

typedef struct students{
    char name[10];
    char sex;
    int age;
}stu;

int main(){
    stu s1 = {"kernel",‘n‘,18};
}

No.17 文件

  • 分類

    • 存放格式
    • 二進制文件 直接將內存中的數據存到文件中,一般文本編輯器打開該文件會亂碼
    • 文本文件 文件內容原樣顯示,但需要轉換成ASCII碼保存,占用空間較大
    • 讀寫方式
    • 順序文件 每條記錄需要按照順序從頭到尾一個接一個存取
    • 隨機文件 在文件的任意位置進行讀寫操作,不用從頭讀到尾
  • 文件類型指針 FILE結構體變量

  • 文件打開與關閉

    • open 打開文件
    • close 關閉文件
  • 文件讀寫操作

    • 字符

    • fgetc 字符寫入
    • fputc 字符輸出
    # 寫入
    #include <stdio.h>
    #include <string.h>
    
    int main() {
      FILE *fp;
      char str[] = "Write The Data to File!";
      int index = 0;
      fp = fopen("test1.txt","w");
      if(fp == NULL) {
          printf("文件打開失敗!\n");
          exit(1);
      }
      while(index < strlen(str)){
        fputc(str[index++],fp);
    }
      fclose(fp);
      return 0;
    }
    # 輸出
    #include "stdio.h"
    
    int main() {
      FILE *fp;
      char c;
      int n = 1;
      if((fp = fopen("test1.txt","r")) == NULL) {
          printf("文件打開失敗!\n");
          exit(1);
      }
      while((c = fgetc(fp)) != EOF) 
      { 
          printf("%c",c); n++; 
      } 
      printf("\n"); 
      fclose(fp); 
      return 0;
    }
    • 字符串

    • fgetsc 字符串寫入
    • fputs 字符串輸出
    # 寫入
    #include <stdio.h>  
    
    int main() {
      char str[] = "通過fputs方法往文件中寫入信息!"; 
      FILE *fp = fopen("test2.txt","w"); 
      if(fp == NULL) {
          printf("文件打開失敗!\n"); exit(1); 
      } 
      fputs(str,fp); fclose(fp); 
      return 0; 
    }
    # 讀取
    #include <stdio.h>
    int main()
    
    {
      FILE *fp;
      char str[20];
      if((fp = fopen("test2.txt","r")) == NULL) {
          printf("文件打開失敗!\n");
          exit(1);
      }
      while(fgets(str,3,fp) != NULL){
          printf("%s",str);
      }
      printf("\n");
      fclose(fp); 
      return 0;
    }
    • 格式化

    • fscanf 格式化寫入
    • fprintf 格式化輸出
    # 寫入
    #include <stdio.h>
    int main()
    {
      char c[] = "Today is 2014 July ";
      int day = 22; char a = ‘t‘,b = ‘h‘;
      FILE *fp; if((fp = fopen("test3.txt","w"))== NULL) {
          printf("文件打開失敗!\n"); exit(1);
      }
      fprintf(fp,"%s%d %c%c",c,day,a,b); 
      fclose(fp); 
      return 0; 
    }
    # 讀取
    #include <stdio.h>  
    int main() {
      //這裏很容易出問題,所以我並不喜歡用fscanf這個函數 
      char c1[5],c2[5],c3[5],c4[5],c5[5],c6[5]; 
      int day,year; FILE *fp; 
      if((fp = fopen("test3.txt","r"))== NULL) {
          printf("文件打開失敗!\n"); exit(1); 
      }
      fscanf(fp,"%s%s%s%s%s%s",&c1,&c2,&c3,&c4,&c5,&c6); 
      printf("%s %s %s %s %s %s\n",c1,c2,c3,c4,c5,c6); 
      fclose(fp); 
      return 0; 
    }
    • 數據塊

    • fwrite 數據塊寫入
    • fread 數據塊輸出
    # 寫入
    #include <stdio.h>
    
    typedef struct
    {
      int id;
      char name[20];
      int age;
    }STU;
    
    int main()
    {
      STU stu;
      FILE *fp;
      char c;
      //內容是追加的!   
      if((fp = fopen("test4.data","ab"))== NULL)
      {
          printf("文件打開失敗!\n");
          exit(1);
      }
      while(1)
      {
          printf("請輸入學號: ");scanf("%d",&stu.id);
          printf("\n請輸入姓名: ");scanf("%s",&stu.name);
          printf("\n請輸入年齡: ");scanf("%d",&stu.age);
          fwrite(&stu,sizeof(stu),1,fp);
          printf("\n繼續輸入?(Y/N)\n");
          getchar();
          c = getchar();
          if(c == ‘y‘ || c == ‘Y‘)continue;
          else break;
      }
      fclose(fp);
      return 0;
    }
    # 讀取
    #include <stdio.h>
    
    typedef struct
    {
      int id;
      char name[20];
      int age;
    }STU;
    
    int main()
    {
      STU stu;
      FILE *fp;
      if((fp = fopen("test4.data","rb"))== NULL)
      {
          printf("文件打開失敗!\n");
          exit(1);
      }
      printf("輸出文件中的內容:\n");
      printf("學生學號     學生姓名        學生年齡\n");
      while(fread(&stu,sizeof(stu),1,fp) == 1)
      {
          printf("%-15d%-15s%-15d\n",stu.id,stu.name,stu.age);
      }
      fclose(fp);
      return 0;
    }
    • getw 字寫入
    • putw 字輸出
    • feof 檢測文件是否結束

    • ferror 執行輸入輸出時,文件是否出錯,返回0則無錯

    • clearerr 重新設置文件出錯標誌,無返回值
  • 文件定位
    • rewind 移動位置到文件開頭
    • fseek 根據起始位移動偏移量
    • ftell 返回指針位置

No.18 變量的作用域與生存周期

作用域

  • 局部變量 在函數內部定義說明的,作用域僅限於函數內,函數外無法調用
  • 全局變量 在函數外部定義說明的,在所有函數中都可以調用,例如在main函數外定義結構體

生存周期

  • auto(自動變量) 這個是默認的,我們平時定義的變量都是自動變量

  • static(靜態變量) 靜態局部變量的初始化是在編譯期進行的,如果沒有進行賦值,系統默認初始值是0或\0,只能賦值依次,程序運行期間不能釋放,生命周期是整個程序的運行周期

  • register(寄存器變量) 對於頻繁讀寫的變量,為了提高程序的運行效率,允許將變量放到CPU的寄存器中,需要時直接從寄存器中取出直接參與計算

C語言從入門到精通,看這一篇就夠了