三件事:初始化、輸入輸出與const詳解)
C每天會(huì)收到無數(shù)句憑什么。憑什么局部變量不初始化就可能是垃圾值憑什么輸入一個(gè)字母程序就死循環(huán)憑什么一個(gè)const變量碰都不能碰這些問題的答案其實(shí)都藏在C的性格里。我一直覺得學(xué)C基礎(chǔ)核心最好的方式不是逐句背語法而是把它當(dāng)成一個(gè)人來讀初始化是出生記錄輸入輸出是待人接物的方式const是承諾和底線。這篇博客就把初始化、輸入輸出、const這三件事放到一起講透適合剛學(xué)到變量章節(jié)、正被各種寫法繞暈的初學(xué)者也適合想用最短時(shí)間把基礎(chǔ)核心重新串一遍的讀者。我會(huì)盡量用大白話配合能直接跑起來的代碼把規(guī)則背后的為什么也帶上。我理解C的方式一直帶著點(diǎn)傳記視角語言有自己的歷史、脾氣和習(xí)慣。你越是順著它的性格寫代碼它越少給你找麻煩。1. 變量初始化C的出生記錄有哪幾種寫法1.1 先分清初始化和賦值兩件事很多教材把int a 0;說成給 a 賦初值這句話沒有大錯(cuò)但會(huì)讓新手忽略一個(gè)關(guān)鍵事實(shí)初始化發(fā)生在變量誕生的那一刻賦值發(fā)生在變量已經(jīng)存在之后。C對(duì)這兩種情況的規(guī)則完全不同尤其在類類型和資源管理里誤把初始化當(dāng)賦值可能造成先構(gòu)造一個(gè)空對(duì)象再整體拷貝覆蓋的多余開銷甚至改變語義。int a; // 對(duì)象已經(jīng)存在但值不確定局部變量默認(rèn)不置零 a 7; // 賦值發(fā)生在 a 誕生之后 int b 7; // 拷貝初始化定義時(shí)立刻有了確定值你可能會(huì)想反正結(jié)果都是 a 和 b 的值是 7計(jì)較這些干什么因?yàn)镃是一門講究每一步都有明確含義的語言。初始化和賦值在性能上、在對(duì)象的生命周期上、在const變量能否使用上差別都非常大。比如一個(gè)const int total 100;可以在初始化時(shí)定死卻絕不能在賦值語句里修改。如果你頭腦里沒有初始化是出生賦值是成長(zhǎng)這條分界線后面學(xué)類、學(xué)引用、學(xué)移動(dòng)語義時(shí)很多報(bào)錯(cuò)都會(huì)看不懂。1.2 花括號(hào)初始化給出生記錄加上審核員C里定義一個(gè)變量可以有好幾種姿勢(shì)int v1 3.14; // 拷貝初始化3.14 被截成 3一般只有警告 int v2(3.14); // 直接初始化同樣發(fā)生窄化 int v3{3.14}; // 列表初始化編譯錯(cuò)誤不允許窄化同樣是3.14到int花括號(hào)版本直接報(bào)錯(cuò)其余版本大多只給警告甚至靜默轉(zhuǎn)換。很多人覺得這是C矯情但我恰恰認(rèn)為這是現(xiàn)代C最值得養(yǎng)成的習(xí)慣花括號(hào)初始化把丟失信息的隱式縮窄變成編譯期錯(cuò)誤。int存不下3.14的小數(shù)部分編譯器替你把關(guān)不讓這個(gè)出生記錄造假。C11之后我更推薦你默認(rèn)寫花括號(hào)int count{}; // 值初始化count 為 0 double ratio{0.5}; // 普通初始化 std::vectorint v{1, 2, 3}; // 容器初始化int count{}這種空花括號(hào)寫法的好處是你明確告訴編譯器給我一個(gè)該類型默認(rèn)狀態(tài)的值內(nèi)置類型得到0類類型調(diào)用默認(rèn)構(gòu)造函數(shù)。它比int count;安全得多比int count 0;更現(xiàn)代。等你在函數(shù)里定義一堆局部變量時(shí)這個(gè)習(xí)慣能幫你擋住一大半未初始化變量的坑。1.3 默認(rèn)初始化局部變量不置零別賭它一定是垃圾值C里有一個(gè)非常容易翻車的規(guī)則局部變量不會(huì)自動(dòng)獲得一個(gè)安全值。定義int uninit;之后它可能拿到棧上殘留的任意數(shù)據(jù)。讀取它屬于標(biāo)準(zhǔn)里明確的未定義行為我見過太多Debug正常、Release隨機(jī)崩潰的疑難bug最后定位到源頭就只是一行沒初始化的局部變量。#include iostream int main() { int counter; counter; // 未定義行為 std::cout counter \n; return 0; }這段代碼在不同編譯器、不同優(yōu)化等級(jí)下可能輸出0可能輸出某個(gè)隨機(jī)數(shù)也可能程序直接異常。它之所以隱蔽是因?yàn)榕紶柨雌饋碚?。全局變量和靜態(tài)變量會(huì)默認(rèn)零初始化類成員變量有一整套初始化規(guī)則但函數(shù)里的內(nèi)置類型局部變量就是不給你默認(rèn)值。我的實(shí)操建議很簡(jiǎn)單聲明的同時(shí)就初始化哪怕只是int i{}或int j{0}。不要抱著反正后面會(huì)賦值的心態(tài)中間一旦出現(xiàn)提前返回或者異常分支那個(gè)變量就會(huì)變成定時(shí)炸彈。如果你用的編譯器支持-Wmaybe-uninitialized記得打開。這個(gè)警告雖然偶爾誤報(bào)但抓到真兇的概率遠(yuǎn)大于冤枉好人的概率。2. 輸入輸出和C打交道的兩種基本口吻2.1 cin和cout的基礎(chǔ)禮儀學(xué)習(xí)C第二個(gè)繞不開的主題就是iostream。它像一個(gè)信使負(fù)責(zé)在程序和用戶之間傳話std::cin負(fù)責(zé)讀取鍵盤輸入std::cout負(fù)責(zé)把結(jié)果打到屏幕上std::cerr和std::clog則用來輸出錯(cuò)誤和日志。幾乎所有入門程序都要靠它們完成和世界的第一次對(duì)話。#include iostream #include string int main() { std::cout 請(qǐng)輸入你的姓名: ; std::string name; std::cin name; std::cout 你好, name \n; return 0; }這里有兩個(gè)很有意思的小點(diǎn)。第一std::cin name按空白字符分隔輸入所以輸入張三 李四時(shí)只會(huì)讀取張三想讀一整行得用std::getline(std::cin, name)。第二我習(xí)慣用\n而不是std::endl來換行。std::endl除了換行還會(huì)強(qiáng)制刷新輸出緩沖區(qū)密集輸出時(shí)性能差距很明顯默認(rèn)情況用\n就好別把兩者當(dāng)同一個(gè)東西。std::cout的使用還要注意鏈?zhǔn)綄懛ǖ捻樞騿栴}。它本身是從左到右逐段插入的但對(duì)于同一個(gè)變量如果在一條鏈里既寫它又修改它行為可能受求值順序影響。新手階段不用過度擔(dān)心但心里要有個(gè)概念輸入輸出不止是打印它涉及緩沖區(qū)、格式化、狀態(tài)標(biāo)志這些背后的機(jī)制。2.2 輸入不匹配和緩沖區(qū)殘留cin翻車現(xiàn)場(chǎng)新手最常見的求救消息是我輸入一個(gè)字母程序就死循環(huán)了。下面這段代碼是典型的翻車現(xiàn)場(chǎng)int age 0; std::cin age; std::cout 你的年齡是: age \n;如果用戶輸入abcstd::cin嘗試把a(bǔ)bc解析成int失敗后會(huì)把a(bǔ)ge留在原值同時(shí)把輸入流置于失敗狀態(tài)。更麻煩的是失敗的輸入會(huì)殘留在緩沖區(qū)里如果不清除后續(xù)所有std::cin讀取都會(huì)直接失敗。正確姿勢(shì)是檢查失敗狀態(tài)清除標(biāo)志再跳過本次殘留內(nèi)容int age 0; std::cin age; if (std::cin.fail()) { std::cin.clear(); // 清除失敗狀態(tài)標(biāo)志 std::cin.ignore(12345, \n); // 跳過當(dāng)前行剩余內(nèi)容 }std::cin.ignore(12345, \n)的意思是從緩沖區(qū)里最多丟棄12345個(gè)字符直到遇到換行符為止。這個(gè)數(shù)字寫大一點(diǎn)只是為了確保把殘留內(nèi)容清干凈專業(yè)一點(diǎn)可以寫成std::numeric_limitsstd::streamsize::max()需要包含limits頭文件。另一個(gè)常見寫法是循環(huán)讀取直到成功int age 0; while (!(std::cin age)) { std::cin.clear(); std::cin.ignore(12345, \n); std::cout 輸入無效請(qǐng)重新輸入一個(gè)整數(shù): ; }這段代碼的原理是讀取成功時(shí)std::cin age的結(jié)果可以隱式轉(zhuǎn)換為布爾值true讀取失敗時(shí)返回false于是進(jìn)入循環(huán)處理錯(cuò)誤。我建議你把這個(gè)模式背下來后面寫任何帶交互輸入的工具都會(huì)用到。2.3 輸出格式化讓數(shù)字按你想要的樣子說話std::cout直接輸出浮點(diǎn)數(shù)時(shí)默認(rèn)只顯示幾位有效數(shù)字小數(shù)點(diǎn)后的位數(shù)可能是3.14159也可能是3。要精確控制輸出格式需要用到iomanip里的操作符。下面這張表是使用頻率最高的幾個(gè)控制符/操作作用典型用法std::setw(n)設(shè)置下一個(gè)輸出的最小寬度cout setw(10) xstd::setfill(c)設(shè)置填充字符cout setfill(0) setw(5) xstd::fixed使用固定小數(shù)計(jì)數(shù)法cout fixed setprecision(2) xstd::setprecision(n)設(shè)置精度有效數(shù)字或小數(shù)位cout setprecision(3) xstd::left/std::right設(shè)置對(duì)齊方向cout left setw(8) x實(shí)際組合起來大概長(zhǎng)這樣#include iostream #include iomanip int main() { std::cout std::fixed std::setprecision(2); std::cout 3.14159 \n; // 輸出 3.14 std::cout std::setw(8) std::right 42 \n; // 輸出 42 return 0; }我自己的經(jīng)驗(yàn)是std::fixed和std::setprecision(2)是處理金額、面積這類精確兩位小數(shù)場(chǎng)景的黃金組合。注意std::fixed一旦設(shè)置會(huì)持續(xù)影響后續(xù)所有浮點(diǎn)輸出直到你切換成別的格式。做出一個(gè)好看的輸出表格不算核心邏輯但絕對(duì)是讓程序看起來專業(yè)的最快方式。3. const限定符定了就不許改這是代碼的信用體系3.1 const讀法核心是綁定不是值不能變const是C基礎(chǔ)核心里最容易被誤解的關(guān)鍵字。很多新手把它理解成把一個(gè)值的屬性改成只讀但更準(zhǔn)確的說法是const建立了一個(gè)名字與可變權(quán)限之間的綁定。當(dāng)你寫下const int N 100;你承諾不通過N這個(gè)名字修改底層存儲(chǔ)。底層存儲(chǔ)本身是否可改取決于對(duì)象本身是否為const。const int N 100; // N 200; // 編譯錯(cuò)誤這個(gè)設(shè)計(jì)讓const成為代碼的信用體系看到const參數(shù)調(diào)用者就知道這個(gè)函數(shù)不會(huì)偷改我的數(shù)據(jù)看到const變量后續(xù)維護(hù)者就知道這個(gè)值是一個(gè)不能動(dòng)的既定約定。對(duì)比C語言時(shí)代的#define N 100const有明確的類型、作用域在調(diào)試器里也看得見不會(huì)在預(yù)處理階段無腦替換污染全局這是C推薦用const替代宏的重要原因之一。3.2 指針和const從右往左讀一讀就懂指針和const組合起來是新手重災(zāi)區(qū)。比如const int* p和int* const p有什么區(qū)別死記硬背效果很差我教你一個(gè)讀法技巧從右往左讀聲明。const int *pp 是指向const int的指針。不能通過*p修改它指向的整型值但 p 本身可以指向別處。int *const pp 是const指針。意味著 p 這個(gè)指針變量不能再指向別處但可以通過*p修改它指向的值。const int *const p兩者都不能改。int x 1, y 2; const int* p x; // *p 10; // 錯(cuò)誤不能通過 p 修改 x p y; // 正確p 可以換目標(biāo) int* const q x; *q 10; // 正確x 被改成 10 // q y; // 錯(cuò)誤q 不能再換目標(biāo)還有一個(gè)高頻誤解const int* p指向的對(duì)象本身一定是const嗎不是。我可以先定義普通int x, 再讓const int* p x;這完全合法只表示通過 p 這條路徑不能寫x 本身仍然可以被x 5;修改。反過來真正 const 對(duì)象也不能隨便交給普通指針去修改那會(huì)直接破壞const承諾。const引用的規(guī)則也一并說清楚const int r 42;是合法的因?yàn)閏onst引用可以綁定到臨時(shí)對(duì)象并延長(zhǎng)其生命周期如果去掉const直接int r 42;會(huì)編譯失敗。所以函數(shù)形參里只要不需要修改外部數(shù)據(jù)強(qiáng)烈建議寫成const 引用既能避免拷貝開銷又能表達(dá)我只讀不改的意圖。3.3 constexpr比const更硬的編譯期常量const表示運(yùn)行期不可變但它的值未必在編譯期就能確定。比如const int x getRuntimeValue(); // 合法x 運(yùn)行期定值之后不可改 constexpr int y 2 3; // 必須編譯期能算出 5C11引入了constexpr它要求編譯器在編譯階段就能求出值適合用來定義數(shù)組長(zhǎng)度、模板參數(shù)這類必須編譯期確定的量。現(xiàn)代C里我優(yōu)先推薦凡是明確要當(dāng)編譯期常量使用的就直接用constexpr。C14放寬了constexpr函數(shù)的限制C17和C20又進(jìn)一步加強(qiáng)現(xiàn)在constexpr函數(shù)里寫循環(huán)、分支都很正常了。constexpr int kBufSize 4096; char buffer[kBufSize]; // 合法kBufSize 是編譯期常量如果這里寫成const int kBufSize getInput();char buffer[kBufSize]在標(biāo)準(zhǔn)C里就不合法。學(xué)會(huì)constexpr還有一個(gè)附帶好處你能更清晰地判斷代碼里哪些計(jì)算可以提前到編譯期這對(duì)性能敏感的程序非常重要。3.4 給初學(xué)者的const使用準(zhǔn)則寧多勿少、意圖明確關(guān)于const我在實(shí)際代碼里給自己定了三條準(zhǔn)則分享給你函數(shù)參數(shù)只讀不改時(shí)傳引用優(yōu)先加const。比如void printName(const std::string name);。無const的按值傳遞雖然安全但多一次拷貝無const的引用傳遞雖然省拷貝但調(diào)用者會(huì)擔(dān)心你是否會(huì)偷偷修改。明明不會(huì)變的局部值就用const聲明。const double width readInput(...);這種寫法相當(dāng)于給后續(xù)維護(hù)者留了張便條這個(gè)值算出來之后就不要?jiǎng)恿恕2灰胏onst_cast去修改真正的const對(duì)象。那是未定義行為。const_cast只該用于接口設(shè)計(jì)有問題、但確認(rèn)底層對(duì)象本非const的極少數(shù)場(chǎng)景新手階段請(qǐng)把它當(dāng)禁詞。4. 把它們串起來寫一個(gè)帶輸入校驗(yàn)的常量化計(jì)算Demo4.1 這次要解決什么問題前兩章的知識(shí)點(diǎn)如果只是分開看感受不深。我建議你馬上把它們拼成一個(gè)完整的小工具。這里做一個(gè)矩形面積計(jì)算器支持反復(fù)讀取寬度和高度遇到非數(shù)字輸入自動(dòng)重試最多重試3次輸出結(jié)果保留兩位小數(shù)。這個(gè)Demo不大但覆蓋了初始化、輸入狀態(tài)處理、const/constexpr聲明、格式化輸出全套操作比單看語法規(guī)則有用得多。設(shè)計(jì)時(shí)我特意做了兩個(gè)決策。第一個(gè)是把最大重試次數(shù)定義成constexpr int kMaxRetry 3;因?yàn)樗蔷幾g期常量后續(xù)想改成5次只需改一行。第二個(gè)是把讀入后不再修改的寬度、高度、面積都加const一旦我誤寫area 0;編譯器會(huì)立刻報(bào)錯(cuò)而不是讓Bug在運(yùn)行期悄悄溜過去。4.2 完整參考代碼#include iostream #include iomanip #include limits constexpr int kMaxRetry 3; double readPositiveDouble(const char* prompt) { double value 0.0; for (int attempt 1; attempt kMaxRetry; attempt) { std::cout prompt; std::cin value; if (std::cin.fail()) { std::cin.clear(); std::cin.ignore(std::numeric_limitsstd::streamsize::max(), \n); std::cout 輸入無效請(qǐng)重新輸入一個(gè)數(shù)字。\n; continue; } if (value 0.0) { return value; } std::cout 數(shù)值必須大于 0。\n; } return -1.0; // 表示重試次數(shù)耗盡 } int main() { const double width readPositiveDouble(請(qǐng)輸入寬度: ); if (width 0.0) { std::cout 重試次數(shù)過多程序退出。\n; return 1; } const double height readPositiveDouble(請(qǐng)輸入高度: ); if (height 0.0) { std::cout 重試次數(shù)過多程序退出。\n; return 1; } const double area width * height; std::cout std::fixed std::setprecision(2); std::cout 矩形面積: area \n; return 0; }這段代碼的核心是readPositiveDouble函數(shù)。它用一個(gè)for循環(huán)控制重試次數(shù)失敗或非法輸入就繼續(xù)循環(huán)成功且大于0就返回。std::cin.clear()和std::cin.ignore()必須成對(duì)出現(xiàn)前者恢復(fù)流狀態(tài)后者清掉殘留字符缺一個(gè)就可能陷入讀一次失敗一次的死循環(huán)。你可以把kMaxRetry改成1試試輸入兩次非法數(shù)據(jù)后程序會(huì)立刻走return 1分支邏輯非常直觀。4.3 編譯運(yùn)行與常見報(bào)錯(cuò)我用下面這條命令編譯并運(yùn)行g(shù) -stdc17 -Wall -Wextra -pedantic rectangle_area.cpp -o rectangle_area-Wall -Wextra -pedantic三個(gè)選項(xiàng)是學(xué)習(xí)階段的護(hù)身符。運(yùn)行效果大概這樣請(qǐng)輸入寬度: abc 輸入無效請(qǐng)重新輸入一個(gè)數(shù)字。 請(qǐng)輸入寬度: 10.5 請(qǐng)輸入高度: -3 數(shù)值必須大于 0。 請(qǐng)輸入高度: 7 矩形面積: 73.50我特意列出幾個(gè)初學(xué)者最容易遇到的報(bào)錯(cuò)你可以對(duì)照檢查癥狀原因解決方向編譯時(shí)報(bào)limits file not found忘記包含limits加上#include limitserror: assignment of read-only variable area試圖修改const變量檢查代碼邏輯去掉賦值或改非const輸入字母后程序死循環(huán)失敗狀態(tài)沒有清除std::cin.clear()后std::cin.ignore(...)輸出小數(shù)位數(shù)不符合預(yù)期沒設(shè)置std::fixed或std::setprecision加上iomanip相關(guān)控制符這個(gè)地方我想多說一句編譯器報(bào)錯(cuò)信息的第一行往往已經(jīng)告訴你答案。很多新人見到英文報(bào)錯(cuò)就慌直接復(fù)制到搜索引擎其實(shí)先自己讀一遍至少能看懂read-only variablefile not found這類關(guān)鍵短語。報(bào)錯(cuò)不是審判書是調(diào)試線索。4.4 寫完這個(gè)Demo后你可以做的三個(gè)改動(dòng)練習(xí)這個(gè)Demo跑通后別急著關(guān)掉。我建議你做三個(gè)小實(shí)驗(yàn)來加深理解把kMaxRetry從constexpr改成const int編譯看看是否報(bào)錯(cuò)在標(biāo)準(zhǔn)C里循環(huán)上限用運(yùn)行期變量也能正常工作但你會(huì)感受到constexpr在編譯期定值這件事上的特殊地位。把readPositiveDouble里的const char* prompt參數(shù)改成按值傳std::string。你會(huì)發(fā)現(xiàn)調(diào)用處寫法變了還會(huì)多一次字符串拷貝這正是const引用和字符串對(duì)象之間微妙的取舍。把width的const去掉在main末尾加一行width width 1;再故意把height也改掉看編譯器給你哪些錯(cuò)誤。記住這些報(bào)錯(cuò)的英文表達(dá)以后一眼就能識(shí)別是不是有人動(dòng)了const變量。5. 我的三條學(xué)習(xí)建議順序、編譯器和調(diào)試習(xí)慣5.1 把三個(gè)主題串成一條主線而不是背三個(gè)碎片初始化、輸入輸出、const這三者看上去是獨(dú)立知識(shí)點(diǎn)但其實(shí)可以串成一條完整的敘事線變量先被正確地帶到這個(gè)世界上初始化然后與世界交換信息輸入輸出最后用const守住不可動(dòng)搖的邊界規(guī)則。我見過不少初學(xué)者把這三個(gè)主題當(dāng)成三個(gè)孤島學(xué)初始化時(shí)拼命記寫法學(xué)輸入輸出時(shí)只知道cin和cout學(xué)const時(shí)背指針組合結(jié)果一到寫完整程序就不知道哪個(gè)環(huán)節(jié)出了問題。按上述主線理解之后你會(huì)發(fā)現(xiàn)很多問題可以互相解釋為什么輸入失敗要重新初始化變量因?yàn)榱鳡顟B(tài)和變量狀態(tài)都要恢復(fù)到可靠值。為什么面積這種“算出來就不該改”的值要用const因?yàn)橐坏┖罄m(xù)想通過賦值偷改就破壞了程序的規(guī)則一致性。學(xué)到這里下一站可以按順序去碰函數(shù)重載、引用生命周期、類構(gòu)造函數(shù)初始化列表。構(gòu)造函數(shù)初始化列表其實(shí)就是初始化概念在類層面的延伸類里大量const成員變量也只能通過初始化列表來設(shè)初值這就是為什么基礎(chǔ)階段的初始化習(xí)慣如此重要。5.2 固定現(xiàn)代編譯器并把警告當(dāng)成朋友學(xué)習(xí)C時(shí)我不建議長(zhǎng)期依賴瀏覽器在線運(yùn)行工具。那些工具對(duì)單文件小示例很方便但你得不到本地編譯器、調(diào)試器、靜態(tài)分析的完整支持。請(qǐng)裝一個(gè)現(xiàn)代編譯器用標(biāo)準(zhǔn)C17甚至C20來編譯g -stdc17 -Wall -Wextra -pedantic your_file.cpp -o your_program出現(xiàn)警告時(shí)盡早處理而不是反正能運(yùn)行就算成功。警告是編譯器在免費(fèi)給你做代碼審查。我曾經(jīng)在調(diào)試一個(gè)模擬項(xiàng)目X的排查過程里遇到詭異隨機(jī)結(jié)果加-fsanitizeaddress,undefined重新編譯后不到一分鐘就定位到一行為初始化的數(shù)組索引變量。相比肉眼讀代碼編譯器工具的定位效率高得多。運(yùn)行時(shí)內(nèi)存問題還可以用valgrind檢查它雖然慢但能抓到越界訪問、使用未初始化內(nèi)存等一批經(jīng)典問題。作為新手養(yǎng)成編譯必開警告、排查先跑清洗工具的習(xí)慣比多看十篇博客都有用。5.3 三個(gè)常見的愚蠢錯(cuò)誤我都替你踩過了最后分享三個(gè)我親眼見過很多次的學(xué)習(xí)期錯(cuò)誤。第一個(gè)是未初始化變量Debug模式下棧內(nèi)存可能碰巧是0一旦切到Release同一段代碼表現(xiàn)完全不同。不要賭不要偷懶聲明即初始化。第二個(gè)是輸入失敗后不清理狀態(tài)用std::cin 讀取失敗必須clear并ignore否則后續(xù)所有讀取都會(huì)立刻失敗。這個(gè)坑最隱蔽的地方在于它不會(huì)直接崩潰而是讓你的程序看似卡住新手常常誤以為是死循環(huán)實(shí)際上只是流被鎖死。第三個(gè)是把const引用參數(shù)當(dāng)普通變量隨手改比如void format(const std::string s)內(nèi)部想給s加個(gè)后綴直接修改會(huì)編譯報(bào)錯(cuò)。正確做法是定義一個(gè)非const的局部副本或使用新變量。這個(gè)錯(cuò)誤會(huì)逼你重新理解const是承諾承諾不改就得用別的變量來存儲(chǔ)臨時(shí)結(jié)果。最后一個(gè)小技巧如果你正在學(xué)習(xí)C建議從今天開始在代碼文件頂部寫一行注釋記錄你使用的編譯標(biāo)準(zhǔn)、編譯器命令和編譯選項(xiàng)??雌饋聿黄鹧鄣^上一個(gè)月回看自己寫過的代碼當(dāng)你需要復(fù)現(xiàn)某個(gè)結(jié)果時(shí)這行字能幫你省下一堆當(dāng)時(shí)是怎么編譯的的猜謎時(shí)間。C基礎(chǔ)核心確實(shí)瑣碎但把初始化、輸入輸出、const這一組知識(shí)點(diǎn)串起來以后你會(huì)慢慢感覺它不再是散落的語法條目而是一個(gè)有著清晰性格的伙伴在陪你寫每一行代碼。