首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C語言核心概念深度解析:從數據類型到預處理器

     編輯:關於C語言

基本數據類型與變量本質

在C語言中,數據類型不僅是固定內存大小的別名,更是創建變量的「模具」。以基本類型為例:char佔1字節、short佔2字節、int佔4字節。當我們宣告char c; short s; int i;時,實際上是在向編譯器申請對應大小的連續存儲空間,並用變量名作為這段空間的別名。理解「變量是一段實際連續存儲空間的別名」這一本質,對於掌握指針、內存佈局至關重要。

存儲類別關鍵字:auto、register、static

C語言變量擁有屬性關鍵字:

  • auto:局部變量的默認屬性,存儲在棧區,編譯器自動添加,鮮少顯式使用。
  • static:雙重含義。修飾局部變量時,改變存儲位置至靜態區,延長生命周期至程序結束;修飾全局變量/函數時,限制作用域於當前文件(內部鏈接)。
  • register:請求將變量存入CPU寄存器以提高訪問速度,但僅是請求,不保證成功。註意:register變量不能取地址(&運算符失效)。

流程控制語句深度剖析

分支語句:if 與 switch

if適合區間判斷(按片判斷),switch適合離散值匹配。關鍵細節:

  • else總與最近的if匹配,不可獨立存在。
  • 零值比較:布爾變量直接作為條件;普通變量建議0 == var防止誤寫成賦值;float不可直接比較0,需定義精度EPS。
  • switch的case值必須為整型/字符型,建議按邏輯順序排列,default處理真正的異常情況。
  • if可功能性替代switch,但反之不行;多分支時switch更簡潔。

循環語句:do-while、while、for

核心差異在於判斷時機:

  • do-while:先執行後判斷,循環體至少執行一次。
  • while、for:先判斷後執行,可能零次執行;for語法更緊湊,適合已知次數循環。

break終止整個循環,continue跳過本次迭代直接進入下一次判斷。特別註意:switch中不能使用continue(僅在循環中有效)。

特殊關鍵字:goto、void、extern、sizeof

  • goto:公認有害,破壞程序結構,工程中嚴禁使用。
  • void:表示「無類型」。無void變量(無大小、無標尺),但void*是通用指針:作為左值接收任意指針,作為右值賦值需強制轉換。
  • extern:聲明外部符號,亦可指示編譯器以C語言鏈接規範編譯(如extern "C")。
  • sizeof:編譯期運算符(非函數),值在編譯期確定,用於計算類型/表達式佔用內存字節數。

const 與 volatile 雙重奏

const:修飾變量為「只讀」,本質仍是變量(佔內存,運行時可被指釖修改),主要約束編譯器。遞歸應用於指針:

const int* p;     // *p不可變,p可變
int* const p;     // p不可變,*p可變
const int* const p; // 兩者皆不可變

口訣:「左數右指」——const在*左邊限制指向數據,在右邊限制指針本身。

volatile:編譯器偽碼警告,強制每次從內存讀取,用於多線程共享變量、硬體寄存器、信號處理器等「未知因素可能修改」的場景。

結構體、聯合體與枚舉

  • struct:各成員獨立佔用內存,支持柔性數組(最後一個成員為未知大小數組)。
  • union:所有成員共享最大成員大小的內存空間,受大小端影響,同一時刻只能存儲一個有效值。
  • enum:真正的常量,有類型信息,可調試,默認遞增1;優於#define宏常量。

typedef:類型別名而非新類型

typedef為現有類型起別名,不創建新類型,故不能用unsigned/signed擴展。本質區別於#define的文本替換:typedef有語義,受作用域約束;#define純粹預處理替換。

註釋、行連接與轉義符

  • 註釋被空格替換而非刪除,雙引號內的//非註釋,/* */不可嵌套。經典陷阱:x/*p被誤判為註釋開始。
  • 反斜槓\雙重身份:行連接符(預處理階段合併行,後不能有空格)與字符串/字符常量中的轉義符。

字符與字符串常量

單引號'a':字符常量,本質是整數(ASCII碼),佔1字節,'a'+1 == 'b'。
雙引號"a":字符串常量,本質是指針(指向以\0結尾的字符數組),佔2字節('a'+\0),"a"+1指向結束符。
編譯器允許字符串賦值給字符變量、字符與字符串比較,但語義錯誤/可笑,嚴禁使用。

邏輯運算符與短路求值

  • ||:左至右,遇真短路返回真,全假才假。
  • &&:左至右,遇假短路返回假,全真才真。
  • !:只識別0,!0 == 1,非0值結果為0。
  • 三目運算符a?b:c可作為邏輯載體。

位運算符實戰技巧

左移<<n相當於乘2^n,右移>>n相當於除2^n,效率高於算術運算。
關鍵規則:左移高位丟棄低位補0;右移低位丟棄高位補符號位(算術右移)。
防錯準則:避免位運算、邏輯運算、算術運算混用;必須混用時多加括號明確優先級。

自增自減運算符陷阱

++i+++i+++i此類表達式依賴編譯器「貪心法」詞法分析:從左到右盡可能多讀取字符構成合法Token。實際開發中嚴禁編寫副作用不明的複合表達式。

隱式類型轉換層級

算術轉換:低向高(char/short → int → unsigned → long → unsigned long → float → double)。
賦值轉換:右值向左值類型轉換。
函數調用/返回:實參/返回表達式向形參/返回類型轉換。

宏定義與條件編譯

宏:預處理階段文本替換,無調用開銷,但無類型檢查,參數不求值直接替換,易出錯(副作用、優先級、遞歸)。
條件編譯:#if/#else/#endif在預處理期決定編譯哪些代碼,實現:

  • 多產品線共用代碼庫
  • 區分Debug/Release版本
  • 防止頭文件重複包含(#ifndef守衛)

區別於運行期if-else:條件編譯不生成目標代碼分支,由命令行宏定義控制。

經典案例:break/continue/雙重循環

break實現不定次數循環

for (int i = 0; ; i++) { // 無限循環
    int num;
    printf("請輸入整數(0退出):");
    scanf("%d", &num);
    if (num > 0) printf("%d是正數\n", num);
    else if (num < 0) printf("%d是負數\n", num);
    else break; // 輸入0退出
}

猜數字遊戲:循環次數不確定

srand(time(0));
int ran = rand() % 100 + 1;
for (;;) { // 無限循環
    int num;
    scanf("%d", &num);
    if (num < 1 || num > 100) continue;
    if (num == ran) break;
    else if (num > ran) printf("大了\n");
    else printf("小了\n");
}
printf("猜對了\n");

continue輸出100以內偶數

for (int i = 0; i <= 100; i++) {
    if (i % 2) continue; // 奇數跳過
    printf("%d ", i);
}

雙重循環打印三角形

// 直角三角形
for (int i = 0; i < 5; i++) {
    for (int j = 0; j <= i; j++) printf("*");
    printf("\n");
}

// 等腰三角形
for (int i = 0; i < 5; i++) {
    for (int j = 0; j <= i * 2; j++) printf("*");
    printf("\n");
}

外層控制行數,內層控制每行星號數,printf("\n")在內層結束後換行,是關鍵。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved