1 布爾類型
1.1 布爾類型概述
????????布爾類型用于表示邏輯上的真(true)和假(false)兩種狀態,是編程中條件判斷和邏輯運算的基礎。在 C 語言中,布爾值的表示方式隨著標準的發展而不斷完善。
1.2 布爾類型的三種聲明方式
宏定義方式(C89 及以前版本)
- 在 C89 及以前的 C 語言標準中,沒有原生的布爾類型。
- 開發者通常使用宏定義來模擬布爾類型,將 TRUE 和 FALSE 分別定義為 1 和 0。
- 這種方式不夠直觀,但能在一定程度上滿足布爾邏輯的需求。
#include <stdio.h>// 宏定義布爾類型
#define BOOL int
#define TRUE 1
#define FALSE 0int main()
{BOOL isPass = FALSE; // 初始化為假BOOL isOk = TRUE; // 初始化為真printf("isPass=%d, isOk=%d\n", isPass, isOk); // 輸出: isPass=0, isOk=1if (isPass){printf("不會執行\n"); // 條件不滿足,不會執行}if (isOk){printf("會執行\n"); // 條件滿足,會執行}return 0;
}
????????程序在 VS Code 中的運行結果如下所示:
_Bool 類型(C99 標準引入)
- C99 標準引入了原生的布爾類型 _Bool。
- _Bool 類型只能存儲 0 或 1,任何非 0 值賦給 _Bool 變量都會被隱式轉換為 1(真)。
- 這種方式比宏定義更規范,但代碼可讀性稍差,因為通常需要輸出為整數形式。
#include <stdio.h>int main()
{_Bool isPass = 0; // 初始化為假// _Bool isOk = 1; // 初始化為真_Bool isOk = -4; // 非 0 值被轉換為 1(真)printf("isPass=%d, isOk=%d\n", isPass, isOk); // 輸出: isPass=0, isOk=1if (isPass){printf("不會執行\n"); // 條件不滿足,不會執行}if (isOk){printf("會執行\n"); // 條件滿足,會執行}return 0;
}
????????程序在 VS Code 中的運行結果如下所示:
bool 類型(通過 stdbool.h 頭文件)
- C99 標準還提供了 <stdbool.h> 頭文件,其中定義了 bool 類型作為 _Bool 的別名。
- 同時定義了 true 和 false 宏,分別表示 1 和 0,使得代碼更加直觀易讀。
- 這是現代 C 編程中推薦使用的布爾類型表示方式。
#include <stdio.h>
#include <stdbool.h> // 包含布爾類型定義int main()
{bool isPass = false; // 初始化為假bool isOk = true; // 初始化為真printf("isPass=%d, isOk=%d\n", isPass, isOk); // 輸出: isPass=0, isOk=1if (isPass){printf("不會執行\n"); // 條件不滿足,不會執行}if (isOk){printf("會執行\n"); // 條件滿足,會執行}return 0;
}
????????程序在 VS Code 中的運行結果如下所示:
????????我們可以通過 VS Code 查看 <stdbool.h> 的源代碼。將鼠標放在 stdbool.h 或 true 或 false 上,按住 Ctrl 鍵并點擊鼠標左鍵即可查看其實現。<stdbool.h> 的源代碼片段通常如下:
????????這進一步驗證了 bool 是 _Bool 的別名,true 和 false 分別對應 1 和 0。?
1.3?三種聲明方式的執行時間
宏定義方式
- 執行時間:
- 宏定義是在預處理階段執行的。預處理器會在編譯之前處理所有的宏定義、文件包含等指令。
- 在預處理階段,所有的宏定義會被替換為對應的文本。例如,BOOL 會被替換為 int,TRUE 會被替換為 1,FALSE 會被替換為 0。
- 特點:
- 宏定義不會引入新的類型,只是簡單的文本替換。
- 由于是在預處理階段完成,因此在編譯階段,編譯器看到的已經是替換后的代碼。
_Bool 類型
- 執行時間:
- _Bool 類型的處理是在編譯階段完成的。
- 編譯器會識別 _Bool 類型,并在生成目標代碼時進行相應的處理。
- 特點:
- _Bool 是 C 語言原生的布爾類型,提供了更好的類型安全性。
- 編譯器會在編譯階段對 _Bool 類型的變量進行優化,確保其只存儲 0 或 1。
bool 類型
- 執行時間:
- bool 類型的處理也是在編譯階段完成的。
- 當包含 <stdbool.h> 頭文件時,預處理器會將 bool 替換為 _Bool,true 替換為 1,false 替換為 0。
- 編譯器在編譯階段會識別 _Bool 類型,并進行相應的處理。
- 特點:
- bool 是 _Bool 的別名,提供了更直觀和可讀的布爾類型定義。
- 使用 <stdbool.h> 是現代 C 編程中推薦的方式,因為它提高了代碼的可讀性和一致性。
執行時間總結
方式 | 處理階段 | 特點 |
---|---|---|
宏定義 | 預處理階段 | 簡單文本替換,無類型檢查,靈活性高但安全性低 |
_Bool 類型 | 編譯階段 | 原生布爾類型,類型安全,編譯器優化 |
bool 類型 | 編譯階段 | _Bool 的別名,通過 <stdbool.h> 提供,直觀可讀,推薦使用 |
1.4?布爾類型的賦值規則
接受任何整數值
- 布爾變量可以接受任何整數值(如 char、short、int、long 等類型)或任何可隱式轉換為整數的表達式(如算術運算、位運算、比較運算等)。
- 賦值時,編譯器會自動將值轉換為 0(false)或 1(true),無需手動處理。
非零值視為 true(存儲為 1)
- 任何非零值(包括正整數、負整數、大整數、甚至超出 bool 存儲范圍的值)都會被視為 true,并存儲為 1。
- bool 的存儲大小是固定的(通常為 1 字節,即 _Bool 類型),但賦值時不會因值過大而報錯或截斷。
- 編譯器會自動轉換:
- 無論賦值 1、42 還是 1000000,最終存儲的值只能是 0 或 1。
- 這是因為 bool 類型的本質是邏輯值,而非數值,編譯器會隱式地將任何非零值轉換為 1。
#include <stdio.h>
#include <stdbool.h>int main()
{bool b1 = 42; // 42 → true(存儲為 1)bool b2 = -1; // -1 → true(存儲為 1)bool b3 = 1000000; // 1,000,000 → true(存儲為 1)printf("b1=%d (實際存儲值: %d)\n", b1, b1); // 輸出: b1=1 (實際存儲值: 1)printf("b2=%d (實際存儲值: %d)\n", b2, b2); // 輸出: b2=1 (實際存儲值: 1)printf("b3=%d (實際存儲值: %d)\n", b3, b3); // 輸出: b3=1 (實際存儲值: 1)return 0;
}
????????程序在 VS Code 中的運行結果如下所示:
零值視為 false(存儲為 0)
- 只有 0 或顯式的 false 會被視為 false,并存儲為 0。
#include <stdio.h>
#include <stdbool.h>int main()
{bool b4 = 0; // 0 → false(存儲為 0)bool b5 = false; // false → false(存儲為 0)printf("b4 = %d\n", b4); // 0printf("b5 = %d\n", b5); // 0return 0;
}
????????程序在 VS Code 中的運行結果如下所示:
規則總結
規則 | 示例 | 存儲值 | 說明 |
---|---|---|---|
非零值 → true(1) | bool b = 42; | 1 | 任何非零值(包括負數、大數) |
零值 → false(0) | bool b = 0; | 0 | 僅 0 或 false |
隱式轉換 | bool b = -1; | 1 | 編譯器自動處理非零值 |
- bool 類型僅存儲 0 或 1,賦值時會自動轉換,無需擔心數值大小。
- 這一規則確保了布爾邏輯的簡潔性和一致性。
2?sizeof 運算符
????????sizeof 是 C 語言中的一個重要運算符,用于獲取數據類型、變量、字面量或表達式的存儲大小(以字節為單位)。
????????它返回一個 size_t 類型的無符號整數值,通常使用 %zu 格式占位符進行輸出。size_t 的具體類型(如 unsigned int 或 unsigned long)由系統和編譯器決定。
2.1?sizeof 運算符的基本用法
????????sizeof 運算符的主要功能是計算內存占用大小,適用于以下場景:
- 基本數據類型:如 bool、char、short、int、long、long long、float、double、long double 等。
- 變量:直接作用于變量名。
- 字面量:直接作用于常量值。
- 表達式:計算表達式的存儲大小。
語法形式:
- 對于基本數據類型或表達式:sizeof 后面必須使用括號包裹,例如?sizeof(int)、sizeof(1 + 1)。
- 對于變量或字面量:sizeof 后面的括號是可選的,例如 sizeof a 或 sizeof(a)。
2.2?sizeof 的使用場景
計算基本數據類型的大小
????????基本數據類型的大小可能因編譯器和平臺而異。以下是常見數據類型的典型大小:
#include <stdio.h>
#include <stdbool.h> // 包含 bool 類型int main()
{// 使用轉義字符 \t 格式化輸出printf("數據類型\t大小(字節)\n");printf("--------\t-----------\n");// 對于基本數據類型:sizeof 必須使用括號包裹printf("bool \t\t %zu \n", sizeof(bool)); // 通常為 1 字節printf("char \t\t %zu \n", sizeof(char)); // 通常為 1 字節printf("short \t\t %zu \n", sizeof(short)); // 通常為 2 字節printf("int \t\t %zu \n", sizeof(int)); // 通常為 4 字節printf("long \t\t %zu \n", sizeof(long)); // 通常為 4 或 8 字節printf("long long \t %zu \n", sizeof(long long)); // 通常為 8 字節printf("float \t\t %zu \n", sizeof(float)); // 通常為 4 字節printf("double \t\t %zu \n", sizeof(double)); // 通常為 8 字節printf("long double:\t %zu \n", sizeof(long double)); // 通常為 16 字節return 0;
}
????????程序在 VS Code 中的運行結果如下所示:
計算字面量的大小
????????字面量的大小取決于其類型,sizeof 后面的括號是可選的:
- 字符字面量(如 'a'):通常被提升為 int 類型,因此 sizeof('a') 返回 4(字節)。
- 整數和浮點數字面量:大小由其類型決定。例如:
- 431 是 int 類型,sizeof(431) 返回 4。
- 4.31 是 double 類型,sizeof 4.31?返回 8。
#include <stdio.h>
#include <stdbool.h>int main()
{// 字符類型字面量:在 C 中字符常量如 'a' 實際上是 int 類型(通常為 4 字節)printf("字符類型字面量('a'):%zu\n", sizeof('a')); // 輸出通常為 4(bool 提升為 int)printf("布爾類型字面量(true):%zu\n", sizeof(true)); // 輸出通常為 4(bool 提升為 int)printf("布爾類型字面量(false):%zu\n", sizeof(false)); // 輸出通常為 4(bool 提升為 int)// 整型字面量:默認是 int 類型printf("整型字面量(431):%zu\n", sizeof(431)); // 輸出通常為 4(int)// 長整型后綴 L:long int(具體大小依賴平臺)printf("長整型字面量(431L):%zu\n", sizeof(431L)); // 具體大小依賴平臺// 長長整型后綴 LL:long long int(通常為 8 字節)printf("長長整型字面量(431LL):%zu\n", sizeof(431LL)); // 輸出通常為 8(long long)// 單精度浮點數字面量:使用 f 后綴表示 float(通常為 4 字節)printf("單精度浮點型字面量(4.31f):%zu\n", sizeof(4.31f)); // 輸出通常為 4(float)// 雙精度浮點數字面量:默認是 double 類型(通常為 8 字節)printf("雙精度浮點型字面量(4.31):%zu\n", sizeof(4.31)); // 輸出通常為 8(double)// 長雙精度浮點數字面量:使用 L 后綴表示 long double(通常為 16 字節)printf("長雙精度浮點型字面量(4.31L):%zu\n", sizeof(4.31L)); // 輸出通常 16// 對于字面量:sizeof 后面的括號是可選的printf("對于字面量:sizeof 后面的括號是可選的:%zu\n", sizeof true); // 輸出通常為 4(bool 提升為 int)printf("對于字面量:sizeof 后面的括號是可選的:%zu\n", sizeof 431); // 輸出通常為 4(int)printf("對于字面量:sizeof 后面的括號是可選的:%zu\n", sizeof 431L); // 具體大小依賴平臺printf("對于字面量:sizeof 后面的括號是可選的:%zu\n", sizeof 431LL); // 輸出通常為 8(long long)printf("對于字面量:sizeof 后面的括號是可選的:%zu\n", sizeof 4.31f); // 輸出通常為 4(float)printf("對于字面量:sizeof 后面的括號是可選的:%zu\n", sizeof 4.31); // 輸出通常為 8(double)printf("對于字面量:sizeof 后面的括號是可選的:%zu\n", sizeof 4.31L); // 輸出通常為 16(long double)return 0;
}
????????程序在 VS Code 中的運行結果如下所示:
計算變量的大小
????????變量的大小與其類型一致,sizeof 后面的括號是可選的:
#include <stdio.h>int main()
{char a = 'A'; // char 類型變量short b = 66; // short 類型變量int c = 66; // int 類型變量long d = 8888888L; // long 類型變量long long e = 99999999LL; // long long 類型變量float f = 5.6f; // float 類型變量double g = 10.8; // double 類型變量long double h = 12.34L; // long double 類型變量printf("a: %zu \n", sizeof(a)); // 輸出為 1(char 類型)printf("b: %zu \n", sizeof(b)); // 輸出為 2(short 類型)printf("c: %zu \n", sizeof(c)); // 輸出為 4(int 類型)printf("d: %zu \n", sizeof(d)); // 具體大小依賴平臺// 對于變量:sizeof 后面的括號是可選的printf("e: %zu \n", sizeof e); // 輸出為 8(long long 類型)printf("f: %zu \n", sizeof f); // 輸出為 4(float 類型)printf("g: %zu \n", sizeof g); // 輸出為 8(double 類型)printf("h: %zu \n", sizeof h); // 輸出為 16(long double 類型)return 0;
}
????????程序在 VS Code 中的運行結果如下所示:
計算表達式的大小
????????sizeof 也可以用于計算表達式的存儲大小:
#include <stdio.h>int main()
{int d1 = 10;double d2 = 20.0;// printf("表達式(d1 + d2):%zu \n", sizeof d1 + d2); // 不加括號這里輸出會出問題,printf("表達式(d1 + d2)的存儲大小:%zu \n", sizeof(d1 + d2)); // 加上括號當成一個整體// int 類型和 double 類型相加,結果是 double 類型,所以 sizeof(d1 + d2) 的結果是 8printf("表達式(1 + 2)的存儲大小:%zu \n", sizeof(1 + 2)); // 4return 0;
}
????????程序在 VS Code 中的運行結果如下所示:
2.3 注意事項
- 括號的可選性:
- 對基本數據類型和表達式必須使用括號(如 sizeof(int))。
- 對變量或字面量可省略括號(如 sizeof a)。
- 建議:為了統一規范和代碼可讀性,建議在使用 sizeof 時,始終使用括號,無論是對數據類型、變量、字面量還是表達式。例如:sizeof(int)、sizeof(variableName)、sizeof(42)。
- 字符字面量(如 'a')在 sizeof 運算中會被提升為 int 類型。
- 平臺和編譯器依賴性:
- 數據類型的大小可能因平臺和編譯器而異(如 long 可能是 4 或 8 字節、long double 可能是 8 字節、10 字節、12 字節或 16 字節)。