c語言基礎學習

来源:https://www.cnblogs.com/tomatoandpotato/p/18224791
-Advertisement-
Play Games

在嵌入式Linux設備中,經常使用jffs2文件系統來作為參數區的文件系統格式。至於為什麼要使用jffs2來作為參數區的文件系統,我猜大部分人都沒有做過多的思考。 jffs2在2001年被設計出來,距今已過二十多年,現在在嵌入式設備中它還在被大量使用、說明這套設計本身是沒有問題。 但是,你是否有... ...


數據類型

1. 基本數據類型

  • 整數類型:用於表示整數值,包括intshortlong 等。

    • int:通常為 4 個位元組,即 32 位。
    • short:通常為 2 個位元組,即 16 位。
    • long:通常為 4 個位元組,即 32 位(在 32 位系統上),為 8 個位元組,即 64 位(在 64 位系統上)。
    • long long:通常為 8 個位元組,即 64 位。
    int integerVar = 10;
    short shortVar = 20;
    long longVar = 3000000L;
    
  • 浮點類型:用於表示浮點數值,包括floatdoublelong double 等。

    • float:通常為 4 個位元組,即 32 位。
    • double:通常為 8 個位元組,即 64 位。
    • long double:在某些平臺上可能為 12 位元組或 16 位元組。
    float floatVar = 3.14;
    double doubleVar = 6.28;
    long double longDoubleVar = 10.0L;
    
  • 字元類型:用於表示單個字元,包括char

    • char:通常為 1 個位元組(8 位)。
    char charVar = 'A';
    
  • 布爾類型:用於表示邏輯值,包括bool(C99 標準引入)需要引入 stdbool.h

    • bool:通常為 1 個位元組(8 位)。
    bool boolVar = 1;  // true
    

2. 派生數據類型

  • 數組類型:用於表示同一類型的連續元素列表。

    int numbers[5] = {1, 2, 3, 4, 5};
    
  • 結構體類型:用於自定義複合數據結構。

    struct Person {
        char name[20];
        int age;
    };
    
  • 聯合體類型:用於節省記憶體,多個成員共用同一塊記憶體區域。

    union Data {
        int i;
        float f;
        char str[20];
    };
    
  • 枚舉類型:用於定義符號常量。

    enum Color { RED, GREEN, BLUE };
    

3. 空類型

  • 空類型:用於表示空值,包括void

    void func() { /* ... */ }
    

運算符

1. 算術運算符

用於執行基本的數學運算,例如加法、減法、乘法、除法和取模等。

  • + :加法
  • - :減法
  • * :乘法
  • / :除法
  • % :取模(取餘)

2. 邏輯運算符

用於執行邏輯運算,例如與、或、非等。

  • && :邏輯與(AND)
  • || :邏輯或(OR)
  • ! :邏輯非(NOT)

3. 關係運算符

用於比較兩個值的關係,例如相等、不相等、大於、小於、大於等於、小於等於等。

  • == :等於
  • != :不等於
  • > :大於
  • < :小於
  • >= :大於等於
  • <= :小於等於

4. 位運算符

用於執行位級別的操作,例如與、或、異或和取反等。

  • & :按位與
  • | :按位或
  • ^ :按位異或
  • ~ :按位取反
  • << :左移
  • >> :右移

5. 賦值運算符

用於給變數賦值。

  • = :賦值
  • += :加後賦值
  • -= :減後賦值
  • *= :乘後賦值
  • /= :除後賦值
  • %= :取模後賦值
  • &= :按位與後賦值
  • |= :按位或後賦值
  • ^= :按位異或後賦值
  • <<= :左移後賦值
  • >>= :右移後賦值

6. 其他運算符

  • sizeof :返回變數或類型的大小(以位元組為單位)
  • & :取地址
  • * :指針解引用
  • ?: :三元條件運算符
  • ++ :自增
  • -- :自減

7.原理

1. 按位與(AND)

按位與運算符 & 的原理是:兩個操作數的對應位都為 1 時,結果位為 1;否則結果位為 0。

   0101  (5)
&  0011  (3)
---------
   0001  (1)

2. 按位或(OR)

按位或運算符 | 的原理是:兩個操作數的對應位有一個為 1 時,結果位為 1;否則結果位為 0。

   0101  (5)
|  0011  (3)
---------
   0111  (7)

3. 按位異或(XOR)

按位異或運算符 ^ 的原理是:兩個操作數的對應位不相同時結果位為 1;相同時結果位為 0。

   0101  (5)
^  0011  (3)
---------
   0110  (6)

4. 按位取反(NOT)

按位取反運算符 ~ 的原理是:對操作數的每一位取反,即 0 變為 1,1 變為 0。

~ 00011001 (25)
--------------
  11100110  (230)

5. 左移和右移

左移運算符 << 和右移運算符 >> 的原理是:將操作數的二進位位按照指定方向移動指定的位數,溢出位被丟棄,空出的位用 0 填補。

   00000101 (5) 左移 2 位
----------------
   00010100 (20)

   00010100 (20) 右移 2 位
----------------
   00000101 (5)

流程式控制制

1. 條件語句

條件語句用於根據特定條件選擇性地執行代碼塊。C 語言中最常見的條件語句是 if-else 語句。

int num = 10;
if (num > 0) {
    printf("The number is positive.\n");
} else if (num < 0) {
    printf("The number is negative.\n");
} else {
    printf("The number is zero.\n");
}

C 語言還提供了條件三元運算符(?:),它也可以完成簡單的條件選擇。

2. 迴圈語句

迴圈語句用於重覆執行某段代碼,直到滿足特定條件為止。C 語言中有 forwhiledo-while 三種類型的迴圈語句。

// for 迴圈
for (int i = 0; i < 5; i++) {
    printf("Count: %d\n", i);
}

// while 迴圈
int j = 0;
while (j < 5) {
    printf("Count: %d\n", j);
    j++;
}

// do-while 迴圈
int k = 0;
do {
    printf("Count: %d\n", k);
    k++;
} while (k < 5);

3. 跳轉語句

跳轉語句用於改變程式的正常執行順序。C 語言中的跳轉語句包括 breakcontinuereturn 等。

// break 語句
for (int i = 0; i < 10; i++) {
    if (i == 5) {
        break;  // 跳出迴圈
    }
}

// continue 語句
for (int i = 0; i < 5; i++) {
    if (i == 2) {
        continue;  // 跳過本次迴圈的剩餘代碼,執行下一次迴圈
    }
    printf("Count: %d\n", i);
}

// return 語句
int myFunction() {
    // ...
    return 0;  // 結束函數執行並返回值
}

數組

C 語言中的數組是一種用於存儲相同類型數據的集合。數組在 C 語言中被廣泛使用,可以使用下標來訪問數組中的元素。

下麵是一個簡單的 C 語言數組的聲明和初始化示例:

#include <stdio.h>

int main() {
    // 聲明一個包含5個整數的數組
    int numbers[5];

    // 初始化數組
    numbers[0] = 10;
    numbers[1] = 20;
    numbers[2] = 30;
    numbers[3] = 40;
    numbers[4] = 50;

    // 訪問數組元素並列印輸出
    printf("%d\n", numbers[0]);  // 輸出 10
    printf("%d\n", numbers[2]);  // 輸出 30

    return 0;
}

除了上述的靜態初始化方式,C 語言還支持動態初始化數組。下麵是一個動態初始化數組的示例:

#include <stdio.h>

int main() {
    // 動態初始化一個包含5個整數的數組
    int numbers[] = {10, 20, 30, 40, 50};

    // 訪問數組元素並列印輸出
    printf("%d\n", numbers[1]);  // 輸出 20
    printf("%d\n", numbers[4]);  // 輸出 50

    return 0;
}

在 C 語言中,數組的下標是從 0 開始的,所以第一個元素的下標為 0,第二個元素的下標為 1,依此類推。當訪問數組元素時,需要確保下標在數組的合法範圍內,否則可能導致訪問越界錯誤。

1. 聲明和初始化數組

在 C 語言中,數組可以通過指定數組的類型和元素個數來聲明和初始化。例如:

int numbers[5];  // 聲明瞭一個包含5個整數的數組
int numbers2[5] = {1, 2, 3, 4, 5};  // 聲明並初始化了一個包含5個整數的數組

2. 數組元素的訪問

在 C 語言中,可以通過下標(索引)來訪問數組中的元素。數組的下標從 0 開始。例如:

int value = numbers[2];  // 獲取數組 numbers 的第三個元素的值

3. 多維數組

除了一維數組,C 語言還支持多維數組,例如二維數組、三維數組等。多維數組的聲明和訪問方式類似於一維數組,只是增加了多個維度的下標。例如:

int matrix[3][3];  // 聲明瞭一個3x3的二維數組
int element = matrix[1][2];  // 獲取二維數組 matrix 中的某個元素的值

4. 數組和指針

在 C 語言中,數組名可以看作是數組第一個元素的地址,因此可以使用指針來訪問數組元素。例如:

int numbers[] = {1, 2, 3, 4, 5};
int *ptr = numbers;  // 將指針指向數組的第一個元素

5. 數組和函數

C 語言中可以將數組作為函數的參數進行傳遞,也可以從函數中返回數組。

void printArray(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
}

int main() {
    int numbers[] = {1, 2, 3, 4, 5};
    printArray(numbers, 5);  // 傳遞數組給函數
    return 0;
}

指針

在 C 語言中,指針是一種非常重要的概念,它提供了直接訪問記憶體地址的能力。指針在 C 語言中被廣泛用於動態記憶體分配、數組訪問和函數傳遞等場景。

以下是一個簡單的 C 語言指針的聲明和使用示例:

#include <stdio.h>

int main() {
    int num = 10;  // 聲明一個整數變數
    int *ptr;      // 聲明一個整型指針

    ptr = &num;    // 將指針指向 num 變數的記憶體地址

    printf("num 的值:%d\n", num);     // 輸出 10
    printf("通過指針訪問 num 的值:%d\n", *ptr);  // 輸出 10

    *ptr = 20;     // 通過指針修改 num 的值

    printf("num 的新值:%d\n", num);    // 輸出 20

    return 0;
}

在上面的示例中,我們首先定義了一個整數變數 num,然後聲明瞭一個指向整數類型的指針 ptr。通過 & 運算符,我們將 ptr 指向了 num 變數的記憶體地址,然後通過 * 運算符可以訪問指針所指向的記憶體地址的值。

除了上述的基本用法,指針還可以用於動態記憶體分配,例如使用 malloc 函數在堆上分配記憶體,使用完後使用 free 函數釋放記憶體。

1. 指針的聲明和初始化

在C語言中,指針的聲明方式為在變數名前加上*符號,例如 int *ptr; 表示聲明瞭一個指向整數的指針變數 ptr。指針變數可以通過地址運算符 & 來獲取變數的地址,或者直接賦值為其他指針變數。

int num = 10;
int *ptr;      // 聲明一個整型指針
ptr = &num;    // 將指針指向num變數的記憶體地址
int *ptr2 = ptr;  // 指針變數的賦值

2. 通過指針訪問記憶體

通過解引用運算符 *,可以通過指針來訪問指針所指向記憶體地址的值。

printf("%d\n", *ptr);  // 訪問ptr所指向的記憶體地址的值

3. 指針和數組

指針和數組在C語言中有著密切的關係。在C語言中,數組名可以看作是一個指向數組第一個元素的指針,也可以通過指針來訪問數組的元素。

int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr;  // 將指針指向數組的第一個元素
printf("%d\n", *ptr);  // 輸出數組第一個元素的值

4. 指針和函數

指針在C語言中還廣泛用於函數參數的傳遞,可以通過指針在函數內部修改函數外部變數的值。

void modifyValue(int *ptr) {
    *ptr = 100;
}

int main() {
    int num = 10;
    modifyValue(&num);  // 通過指針修改num的值
    printf("%d\n", num);  // 輸出修改後的值 100
    return 0;
}

5. 指針和動態記憶體分配

在C語言中,可以使用指針來進行動態記憶體分配,比如使用 malloc 函數在堆上分配記憶體,然後使用完後通過 free 函數釋放記憶體。

int *ptr = (int *)malloc(5 * sizeof(int));  // 動態分配5個整數的記憶體空間
free(ptr);  // 釋放記憶體

6. 指針的註意事項

在使用指針時,需要格外註意空指針、野指針以及指針的安全性。指針操作可能引入一些潛在的錯誤,比如記憶體泄漏、越界訪問等。

函數

1. 函數的聲明和定義

函數的聲明指定函數名、參數列表和返回類型,函數的定義包括了函數的具體實現。

// 函數聲明
int add(int a, int b);

// 函數定義
int add(int a, int b) {
    return a + b;
}

2. 函數調用

通過函數名和參數列表,可以在程式的任何地方調用函數。

int result = add(3, 5);  // 調用 add 函數並將結果保存到 result 變數中

3. 參數傳遞

函數可以接收一個或多個參數,這些參數的類型和數量在函數聲明中指定。

void greetUser(char *name) {
    printf("Hello, %s!\n", name);
}

// 調用函數
greetUser("Alice");
greetUser("Bob");

4. 返回值

函數可以返回一個值,這個值的類型在函數聲明中指定。如果函數沒有返回值,可以使用 void 類型指定。

int multiply(int x, int y) {
    return x * y;
}

int result = multiply(3, 4);  // 返回值為 12

5. 函數參數

函數參數可以是基本類型、指針、數組、結構體等。

void printArray(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
}

int numbers[] = {1, 2, 3, 4, 5};
printArray(numbers, 5);

6. 遞歸函數

C語言支持遞歸函數。

int factorial(int n) {
    if (n == 1) {
        return 1;
    } else {
        return n * factorial(n - 1);
    }
}

int result = factorial(5);  // 返回值為 120

7.回調函數

回調函數是一種在軟體開發中常見的設計模式,在C語言中也經常使用。回調函數是指將一個函數作為參數傳遞給另一個函數,在特定條件滿足時由另一個函數來調用。下麵是一個簡單的例子,演示瞭如何在C語言中使用回調函數。

#include <stdio.h>

// 回調函數接受一個整數參數並將其列印出來
void callbackFunction(int value) {
    printf("Callback function: %d\n", value);
}

// 執行某個操作,併在操作完成後調用回調函數
void performOperation(void (*callback)(int)) {
    // 模擬操作
    int result = 42;

    // 調用回調函數
    callback(result);
}

int main() {
    // 將回調函數作為參數傳遞給 performOperation 函數
    performOperation(callbackFunction);

    return 0;
}

8.字元串函數

在C語言中,字元串函數用於操作和處理字元串,這些函數包含在標準庫的<string.h>頭文件中。以下是一些常用的字元串函數及其簡要介紹:

1. strlen

  • 功能:返回字元串的長度(不包括空字元\0)。

  • 示例:

    #include <string.h>
    char str[] = "Hello";
    int length = strlen(str);  // length 等於 5
    

2. strcpy

  • 功能:將一個字元串複製到另一個字元串。

  • 示例:

    #include <string.h>
    char source[] = "source";
    char destination[20];
    strcpy(destination, source);  // 現在 destination 中包含 "source"
    

3. strcat

  • 功能:將一個字元串追加到另一個字元串的末尾。

  • 示例:

    #include <string.h>
    char str1[20] = "Hello";
    char str2[] = "world";
    strcat(str1, str2);  // 現在 str1 中包含 "Helloworld"
    

4. strcmp

  • 功能:比較兩個字元串,相等返回0,不等返回非0。

  • 示例:

    #include <string.h>
    char str1[] = "apple";
    char str2[] = "banana";
    int result = strcmp(str1, str2);  // result 小於 0
    

5. strchr

  • 功能:在字元串中查找特定字元的首次出現位置。

  • 示例:

    #include <string.h>
    char str[] = "Hello";
    char *ptr = strchr(str, 'l');  // ptr 指向 "llo" 中的 'l'
    

6. strstr

  • 功能:在字元串中查找特定子串的首次出現位置。

  • 示例:

    #include <string.h>
    char str[] = "Hello, world!";
    char *ptr = strstr(str, "world");  // ptr 指向 "world!" 中的 'w'
    

記憶體管理

動態記憶體管理是在程式運行時分配和釋放記憶體空間的過程。在C語言中,可以使用malloc函數動態分配記憶體,並使用free函數釋放已分配的記憶體。

動態記憶體分配

使用malloc函數可以在堆記憶體中分配一塊指定大小的記憶體空間。語法如下:

ptr = (int*)malloc(size);

其中,ptr是一個指針,用於存儲分配的記憶體空間的起始地址,而size是以位元組為單位的分配記憶體大小。

示例:

int *ptr;
ptr = (int*)malloc(5 * sizeof(int));

上面的代碼將分配5個整型變數所占的記憶體空間,並將起始地址存儲在ptr指針中。

使用分配的記憶體空間

分配記憶體後,可以通過指針操作這塊記憶體空間,就像操作普通變數一樣。示例如下:

for (int i = 0; i < 5; i++) {
    ptr[i] = i;
}

這段代碼將0到4依次賦值給ptr指向的記憶體空間。這樣就可以在程式運行過程中動態地為變數分配記憶體。

釋放記憶體

在動態分配記憶體後,要確保在不再需要這些記憶體空間時及時釋放,以免造成記憶體泄漏。使用free函數可以釋放已分配的記憶體空間。語法如下:

free(ptr);

示例:

free(ptr);

在釋放記憶體後,應該將指針設置為NULL以避免出現懸空指針的問題:

ptr = NULL;

這種動態記憶體管理的能力使得程式可以根據需要在運行時動態地申請和釋放記憶體,靈活性更高。但需要註意在使用動態記憶體管理時要小心記憶體泄漏和懸空指針的問題。

作用域;存儲期

在C語言中,作用域和存儲期是兩個相關但不同的概念。作用域定義了變數或函數的可見性,而存儲期定義了變數或對象在記憶體中存在的時間範圍。

作用域(Scope)

作用域定義了標識符(變數名、函數名等)在程式中可見的範圍。在C語言中,作用域主要有以下幾種:

  • 塊作用域(Block scope):變數在特定的塊(由花括弧{}定義)中可見。例如局部變數的作用域就是塊作用域。
void function() {
    int x;  // x的作用域是在function函數內
}
  • 函數作用域(Function scope):變數在整個函數內可見。
int globalVar;  // 全局變數,函數作用域為整個源文件

void function() {
    // 這裡可以訪問globalVar
}
  • 文件作用域(File scope):變數在整個源文件中可見,通過使用static關鍵字定義的變數具有文件作用域。
static int fileVar;  // 文件作用域只限於當前源文件

存儲期(Storage Duration)

存儲期定義了變數或對象在記憶體中存在的時間範圍。在C語言中,存儲期主要有以下幾種:

  • 靜態存儲期(Static storage duration):在程式運行期間始終存在,例如全局變數和使用static關鍵字定義的局部變數。
  • 自動存儲期(Automatic storage duration):在進入作用域時創建,在離開作用域時銷毀,例如普通的局部變數。
  • 動態分配存儲期(Allocated storage duration):通過malloc等函數動態分配的記憶體,需要顯式釋放,否則會一直存在直到程式結束。

對於函數或塊作用域內的變數,存儲期一般是自動的,而全局變數和static變數的存儲期是靜態的。動態分配的記憶體具有動態分配的存儲期。

類型組合

類型組合指的是將不同的數據類型組合在一起,形成新的數據結構,如結構體和聯合體。

結構體(Struct)

結構體是一種用戶自定義的複合數據類型,能夠將不同類型的數據組合成一個整體,具有一定的記憶體佈局和對應的成員。

struct Person {
    char name[50];
    int age;
    float height;
};

這裡定義了一個Person結構體,包括了姓名、年齡和身高三個成員。結構體提供了一種方式來組合不同類型的數據,便於程式中對相關數據進行封裝和操作。

結構體初始化

在C語言中,結構體的初始化可以通過幾種不同的方式來完成。下麵是結構體初始化的方法示例:

1. 按順序初始化

struct Person {
    char name[50];
    int age;
    float height;
};

struct Person person1 = { "Alice", 25, 1.75 };

在這個例子中,結構體Person中的成員按照定義時的順序進行初始化,即先是name,然後是age,最後是height

2. 指定成員初始化

struct Person person1 = { .name = "Bob", .age = 30, .height = 1.80 };

通過指定成員名的方式可以更清晰地對結構體進行初始化,不依賴於順序。

3. 部分成員初始化

struct Person person1 = { .name = "Charlie" };

如果只對結構體的部分成員進行初始化,則其餘成員將被初始化為0或空值,具體取決於成員的類型。

4. 動態初始化

struct Person person1;
person1.name = "David";
person1.age = 35;
person1.height = 1.85;

在這種方式下,可以先分配結構體記憶體,然後逐個成員賦值。

這些是常見的結構體初始化方法,選擇合適的方式取決於具體的需求和代碼風格。

聯合體(Union)

當涉及數據結構時,C語言中的聯合體(Union)與枚舉(Enum)都可以用來組織數據,但它們的工作方式和用途不同。

定義聯合體:

union Data {
    int int_val;
    float float_val;
    char str_val[20];
};

在這個例子中,Data聯合體可以存儲整型、浮點型或字元串類型的值,但一次只能保存其中的一種類型。這樣的設計可以節省記憶體空間,但在某些情況下可能會引發數據訪問的困難。

使用聯合體:

union Data data;
data.int_val = 10;
printf("Integer value: %d\n", data.int_val);

data.float_val = 3.14;
printf("Float value: %f\n", data.float_val);

枚舉(Enum)

枚舉是一種用來定義標識符常量的用戶定義數據類型。枚舉類型可以為一組數值賦予語義上的名稱,使得程式更易讀,更易理解。

定義枚舉:

enum Month {
    JANUARY,
    FEBRUARY,
    MARCH,
    /* ... */
    DECEMBER
};

上面例子中,定義了一個Month枚舉類型,包含了十二個月份的常量。

使用枚舉:

enum Month currentMonth = MARCH;
if (currentMonth == MARCH) {
    printf("It's March!\n");
}

枚舉類型的值可以直觀地使用它們的語義上的名稱,而不需要記住或瞭解其實際值。

總結

聯合體允許在相同的記憶體位置存儲不同類型的數據,而枚舉允許為一組數值賦予語義上的名稱。這兩種數據類型各自具有特定的用途和優勢,程式員可以根據實際需求選擇合適的數據類型。

預處理

預處理是C語言編譯過程中的第一個階段,由預處理器完成。在此階段,預處理器會對源代碼進行處理,包括文件包含、巨集替換、條件編譯等操作,生成經過預處理的源代碼,然後再進入編譯階段。

以下是預處理器的一些主要功能:

文件包含(File Inclusion)

通過#include指令,可以將其他文件包含到當前文件中,這樣在編譯時就可以將這些文件的內容插入到當前文件中。

#include <stdio.h>
#include "mylibrary.h"

在上面的例子中,#include <stdio.h>包含了標準庫頭文件,而#include "mylibrary.h"包含了自定義的頭文件。

巨集定義

巨集定義是C語言中的一種預處理指令,用於定義標識符和值之間的映射關係。巨集定義可以簡化代碼編寫、提高代碼的可讀性、降低維護成本,並且支持代碼的靈活調整。

基本巨集定義語法

使用#define關鍵字可以定義巨集。其基本語法為:

#define MACRO_NAME replacement
  • MACRO_NAME為標識符,將被用作替換的名稱。
  • replacement為標識符在代碼中出現時將被替換為的內容。

例如,定義一個表示圓周率π的巨集:

#define PI 3.14159

使用巨集

定義好巨集之後,可以在代碼中使用該巨集,預處理階段會將巨集名替換為對應的值。例如:

float radius = 5.0;
float area = PI * radius * radius;

在上述代碼中,預處理階段會將PI替換為3.14159,然後再進行編譯。

參數化巨集

巨集可以帶有參數,以實現在不同情況下的替換。帶有參數的巨集的基本語法為:

#define MACRO_NAME(parameter_list) replacement

例如,定義一個比較兩個數大小並返回較大值的巨集:

#define MAX(a, b) ((a) > (b) ? (a) : (b))

之後可以在代碼中使用這個巨集:

int max_value = MAX(10, 20);

在這個例子中,MAX巨集接受兩個參數,並返回較大的那個數。

條件編譯(Conditional Compilation)

條件編譯是C語言預處理階段的重要功能,它可以根據預定義的巨集來選擇性地編譯代碼,以在不同的編譯環境下實現定製化的編譯。

#if#ifdef#ifndef#elif#else#endif

條件編譯主要使用以上預處理指令來實現,以便根據巨集的定義情況選擇性地編譯特定的代碼塊。

  • #if:如果給定的條件為真,就編譯後面的代碼。
  • #ifdef:如果指定的巨集已經定義,就編譯後面的代碼。
  • #ifndef:如果指定的巨集尚未定義,就編譯後面的代碼。
  • #elif:前一個條件不成立時,測試另一個條件。
  • #else:如果前面的條件不成立,則編譯後面的代碼。
  • #endif:條件編譯指令塊的結束標記。

示例

#define DEBUG 1

#ifdef DEBUG
    // 調試模式下的代碼
#else
    // 發佈版本的代碼
#endif

在上述示例中,如果巨集DEBUG被定義,預處理器會編譯調試模式下的代碼;否則,會編譯發佈版本的代碼。

條件編譯的應用

條件編譯可以用於根據不同的編譯目標或環境,選擇性地編譯特定的代碼。它可以用於開發環境和生產環境、不同的操作系統、不同的體繫結構等方面的定製化需求。

// 示例:根據操作系統選擇性編譯
#ifdef _WIN32
    // Windows平臺下的代碼
#else
    // 非Windows平臺下的代碼
#endif

條件編譯還可以結合巨集定義實現一些特定功能的開關,從而在不同情況下定製化編譯不同的代碼。

條件編譯為程式提供了很大的靈活性,可以根據不同的編譯環境和需求選擇性地編譯代碼,這在實際開發中非常有用。

其他功能

預處理的結果是生成經過處理的源代碼,在這之後才會進入編譯器的詞法分析和語法分析階段。因此,預處理是為了在編譯時對源代碼進行一些文本替換和條件判斷,使得程式在不同的環境和條件下能夠靈活地進行編譯和定製。

在C語言中,良好的文件組織結構對於程式的可維護性、可擴展性和可復用性都非常重要。文件組織主要涉及頭文件的使用、模塊化編程和代碼的結構化佈局。

文件組織

頭文件(Header File)

頭文件通常包含類型定義、函數聲明和常數定義,可以被多個源文件包含以便共用這些定義。頭文件採用.h作為文件擴展名。

示例頭文件 mylib.h

#ifndef MYLIB_H
#define MYLIB_H

// 聲明函數
int add(int a, int b);

// 定義常量
#define PI 3.14159

#endif

源文件(Source File)

源文件包含實際的函數定義和全局變數的聲明,並且通常以.c作為文件擴展名。

示例源文件 mylib.c

#include "mylib.h"

// 定義函數
int add(int a, int b) {
    return a + b;
}

模塊化編程(Modular Programming)

模塊化編程是一種將代碼分割成小模塊,以便於管理和維護的編程風格。每個模塊都有自己的介面和實現,這些模塊可以利用模塊化的思想設計和開發。

示例分割模塊:

// math.h - 頭文件
#ifndef MATH_H
#define MATH_H

int add(int a, int b);  // 函數聲明

#endif
// math.c - 源文件
#include "math.h"

int add(int a, int b) {  // 函數定義
    return a + b;
}

結構化佈局(Structured Layout)

合理的文件結構能夠讓代碼更易於閱讀和理解。對於大型項目,推薦採用一定的目錄結構,將相關文件組織到不同的子目錄中。

例如,可以將頭文件放在include目錄中,將源文件放在src目錄中,將測試相關文件放在test目錄中。

文件組織的良好結構有助於更好地理解和維護代碼,可以通過簡單的目錄結構就能找到所需要的文件。

通過良好的文件組織,可以更好地組織代碼、提高代碼的可維護性和可移植性,便於團隊協作和後期維護。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • C#.NET與JAVA互通之MD5哈希V2024 配套視頻: 要點: 1.計算MD5時,SDK自帶的計算哈希(ComputeHash)方法,輸入輸出參數都是byte數組。就涉及到字元串轉byte數組轉換時,編碼選擇的問題。 2.輸入參數,字元串轉byte數組時,編碼雙方要統一,一般為:UTF-8。 ...
  • CSharpe中的IO+NPOI+序列化 文件文件夾操作 學習一下常見的文件、文件夾的操作。 什麼是IO流? I:就是input O:就是output,故稱:輸入輸出流 將數據讀入記憶體或者記憶體輸出的過程。 常見的IO流操作,一般說的是[記憶體]與[磁碟]之間的輸入輸出。 作用 持久化數據,保證數據不再 ...
  • 一:背景 1. 講故事 前些天有位朋友在微信上丟了一個崩潰的dump給我,讓我幫忙看下為什麼出現了崩潰,在 Windows 的事件查看器上顯示的是經典的 訪問違例 ,即 c0000005 錯誤碼,不管怎麼說有dump就可以上windbg開幹了。 二:WinDbg 分析 1. 程式為誰崩潰了 在 Wi ...
  • 在C#中,經常會有一些耗時較長的CPU密集型運算,因為如果直接在UI線程執行這樣的運算就會出現UI不響應的問題。解決這類問題的主要途徑是使用多線程,啟動一個後臺線程,把運算操作放在這個後臺線程中完成。但是原生介面的線程操作有一些難度,如果要更進一步的去完成線程間的通訊就會難上加難。 因此,.NET類 ...
  • SQLSugar是什麼 **1. 輕量級ORM框架,專為.NET CORE開發人員設計,它提供了簡單、高效的方式來處理資料庫操作,使開發人員能夠更輕鬆地與資料庫進行交互 2. 簡化資料庫操作和數據訪問,允許開發人員在C#代碼中直接操作資料庫,而不需要編寫複雜的SQL語句 3. 支持多種資料庫,包括但 ...
  • PasteSpider是什麼? 一款使用.net編寫的開源的Linux容器部署助手,支持一鍵發佈,平滑升級,自動伸縮, Key-Value配置,項目網關,環境隔離,運行報表,差量升級,私有倉庫,集群部署,版本管理等! 30分鐘上手,讓開發也可以很容易的學會在linux上部署你得項目! [從需求角度介 ...
  • 痞子衡嵌入式半月刊: 第 102 期 這裡分享嵌入式領域有用有趣的項目/工具以及一些熱點新聞,農曆年分二十四節氣,希望在每個交節之日準時發佈一期。 本期刊是開源項目(GitHub: JayHeng/pzh-mcu-bi-weekly),歡迎提交 issue,投稿或推薦你知道的嵌入式那些事兒。 上期回 ...
  • 本文是IMX6ULL開發板spi OLED驅動學習筆記,方便後面查看時快速的回顧,而不需要一點點的看視頻 視頻地址: https://www.bilibili.com/video/BV1Yb4y1t7Uj?p=144&spm_id_from=pageDriver&vd_source=1d93d6a5 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...