2.1 iostream介紹:cout、cin和endl
本節(jié),我們將更多地討論在“Hello, World!”程序中使用的std::cout
語(yǔ)句,將文本“Hello, World!”輸出到控制臺(tái)。我們還將探討如何從用戶獲取輸入,我們將使用這些使我們的程序更具交互性。
2.1.1 輸入/輸出庫(kù)
輸入/輸出庫(kù)(io 庫(kù))是處理基本輸入和輸出的C++標(biāo)準(zhǔn)庫(kù)的一部分。我們將使用此庫(kù)中的功能從鍵盤獲取輸入并將數(shù)據(jù)輸出到控制臺(tái)。iostream的io部分代表輸入/輸出。
要使用iostream庫(kù)中定義的功能,我們需要在使用iostream中定義的內(nèi)容的任何代碼文件的頂部包含iostream 標(biāo)頭,如下所示:
#include <iostream>
// 此處使用iostream功能的其余代碼
2.1.2 std::cout
iostream庫(kù)包含一些預(yù)定義的變量供我們使用。最有用的之一是std::cout
,它允許我們將數(shù)據(jù)發(fā)送到控制臺(tái)以打印為文本。cout代表“字符輸出”。
作為復(fù)習(xí),這是我們的“Hello, World!”程序:
#include <iostream> // 為了使用 std::cout
int main()
{
std::cout << "Hello world!"; // 打印 Hello world! 到控制臺(tái)
return 0;
}
在這個(gè)程序中,我們包含了iostream,以便可以使用std::cout
。在我們的主函數(shù)中,我們使用std::cout
以及插入運(yùn)算符(<<
)來(lái)發(fā)送文本Hello world!到要打印的控制臺(tái)。
std::cout
不僅可以打印文本,還可以打印數(shù)字:
#include <iostream> // 為了使用 std::cout
int main()
{
std::cout << 4; // 打印 4 到控制臺(tái)
return 0;
}
這將產(chǎn)生結(jié)果:
4
它還可用于打印變量的值:
#include <iostream> // 為了使用 std::cout
int main()
{
int x{ 5 }; // 定義整形變量x,初始化為值5
std::cout << x; // 將x的值(5)打印到控制臺(tái)
return 0;
}
這將產(chǎn)生結(jié)果:
5
要在同一行上打印多個(gè)內(nèi)容,可以在單個(gè)語(yǔ)句中多次使用插入運(yùn)算符(<<
)來(lái)連接。例如:
#include <iostream> // 為了使用 std::cout
int main()
{
std::cout << "Hello" << " world!";
return 0;
}
此程序打?。?/p>
Hello world!
下面是另一個(gè)示例,我們?cè)谕徽Z(yǔ)句中同時(shí)打印文本和變量的值:
#include <iostream>
int main()
{
int x{ 5 };
std::cout << "x is equal to: " << x;
return 0;
}
此程序打?。?/p>
x is equal to: 5
2.1.3 std::endl
您覺(jué)得此程序會(huì)打印什么?
#include <iostream> // for std::cout
int main()
{
std::cout << "Hi!";
std::cout << "My name is Alex.";
return 0;
}
您可能會(huì)對(duì)結(jié)果感到驚訝:
Hi!My name is Alex.
單獨(dú)的輸出語(yǔ)句不會(huì)導(dǎo)致控制臺(tái)上的輸出行單獨(dú)(即換行)。
如果我們想將單獨(dú)的輸出行打印到控制臺(tái),我們需要告訴控制臺(tái)何時(shí)將光標(biāo)移動(dòng)到下一行。
一種方法是使用std::endl
。使用std::cout
輸出時(shí),std::endl
會(huì)將換行符打印到控制臺(tái)(導(dǎo)致光標(biāo)轉(zhuǎn)到下一行的開頭)。在這種情況下,endl代表“終點(diǎn)線”。
例如:
#include <iostream> // 為了使用 std::cout 和 std::endl
int main()
{
std::cout << "Hi!" << std::endl; // std::endl 將使光標(biāo)移動(dòng)到控制臺(tái)的下一行
std::cout << "My name is Alex." << std::endl;
return 0;
}
這將打印:
Hi!
My name is Alex.
拓展:
在上面的程序中,第二個(gè)std::endl
實(shí)際上不是必需的,因?yàn)槌绦驎?huì)立即結(jié)束。但是,它有一些有用的目的。
- 首先,它有助于指示輸出行是一個(gè)“完整的思想”(而不是在代碼后面的某個(gè)地方完成的部分輸出)。從這個(gè)意義上說(shuō),它的功能類似于在標(biāo)準(zhǔn)英語(yǔ)中使用句點(diǎn)。
- 其次,它將光標(biāo)定位在下一行,這樣如果我們稍后添加額外的輸出行(例如,讓程序說(shuō)“Bye!”),這些行將出現(xiàn)在我們期望的位置(而不是附加到前一行輸出)。
- 第三,從命令行運(yùn)行可執(zhí)行文件后,某些操作系統(tǒng)在再次顯示命令提示符之前不會(huì)輸出新行。如果我們的程序沒(méi)有以新行上的光標(biāo)結(jié)尾,則命令提示符可能會(huì)附加到前一行輸出,而不是用戶期望的新行的開頭。
最佳實(shí)踐
每當(dāng)一行輸出完成時(shí),就輸出換行符。
2.1.4 std::cout是緩沖的
考慮您最喜歡在游樂(lè)園乘坐過(guò)山車。乘客出現(xiàn)(以某種可變的速度)并排隊(duì)。火車定期到達(dá)并登上乘客(直到火車的最大容量)。當(dāng)火車滿員或經(jīng)過(guò)足夠的時(shí)間后,火車帶著一批乘客出發(fā),然后開始乘坐。任何無(wú)法登上當(dāng)前列車的乘客都等待下一班列車。
這個(gè)類比類似于發(fā)送到std::cout
的輸出通常在C++中處理的方式。程序中的語(yǔ)句請(qǐng)求將輸出發(fā)送到控制臺(tái)。但是,該輸出通常不會(huì)立即發(fā)送到控制臺(tái)。相反,請(qǐng)求的輸出“排隊(duì)”,并存儲(chǔ)在為收集此類請(qǐng)求而預(yù)留的內(nèi)存區(qū)域中(稱為緩沖區(qū))。緩沖區(qū)會(huì)定期刷新,這意味著緩沖區(qū)中收集的所有數(shù)據(jù)都將傳輸?shù)狡淠繕?biāo)(在本例中為控制臺(tái))。
這也意味著,如果程序在刷新緩沖區(qū)之前崩潰、中止或暫停(例如出于調(diào)試目的),則不會(huì)顯示緩沖區(qū)中仍在等待的任何輸出。
關(guān)鍵點(diǎn)
緩沖輸出的反面是無(wú)緩沖輸出。對(duì)于無(wú)緩沖輸出,每個(gè)單獨(dú)的輸出請(qǐng)求都直接發(fā)送到輸出設(shè)備。
將數(shù)據(jù)寫入緩沖區(qū)通常很快,而將數(shù)據(jù)批處理傳輸?shù)捷敵鲈O(shè)備相對(duì)較慢。緩沖可以最大程度地減少在存在多個(gè)輸出請(qǐng)求時(shí)需要執(zhí)行的慢速傳輸數(shù),從而顯著提高性能。
2.1.5 std::endl與\n
使用std::endl
可能有點(diǎn)低效,因?yàn)樗鼘?shí)際上執(zhí)行兩項(xiàng)工作:將光標(biāo)移動(dòng)到控制臺(tái)的下一行,并刷新緩沖區(qū)。將文本寫入控制臺(tái)時(shí),我們通常不需要刷新每行末尾的緩沖區(qū)。讓系統(tǒng)定期自行沖洗會(huì)更有效(它被設(shè)計(jì)為這樣有效地做)。
因此,通常首選使用“\n”字符?!癨n”字符將光標(biāo)移動(dòng)到控制臺(tái)的下一行,但不請(qǐng)求刷新,因此它通常會(huì)執(zhí)行得更好?!癨n”字符也更簡(jiǎn)潔,因?yàn)樗榷逃挚梢郧度氲浆F(xiàn)有文本中。
下面是以兩種不同方式使用“\n”的示例:
#include <iostream>
int main()
{
int x{ 5 };
std::cout << "x is equal to: " << x << '\n'; // 獨(dú)立使用'\n'
std::cout << "And that's all, folks!\n"; // 使用嵌入到雙引號(hào)文本中的'\n'(注意:這樣使用時(shí)沒(méi)有單引號(hào))
return 0;
}
這將打印:
x is equal to: 5
And that's all, folks!
當(dāng)’\n’單獨(dú)用于將光標(biāo)移動(dòng)到控制臺(tái)的下一行時(shí),它應(yīng)該用單引號(hào)引起來(lái)。當(dāng)嵌入到已用雙引號(hào)引號(hào)的文本中時(shí),不需要其他引號(hào)。
最佳實(shí)踐
將文本輸出到控制臺(tái)時(shí),選擇“\n”而不是std::endl
。
警告
'\n’使用反斜杠(與C++中的所有特殊字符一樣),而不是正斜杠。改用正斜杠(例如“/n”)可能會(huì)導(dǎo)致意外錯(cuò)誤。
2.1.6 std::cin
std::cin
是iostream庫(kù)中定義的另一個(gè)預(yù)定義變量。而使用插入運(yùn)算符(<<)將數(shù)據(jù)打印到控制臺(tái),(代表“字符輸入”)使用提取運(yùn)算符(>>)從鍵盤讀取輸入。std::cin
輸入必須存儲(chǔ)在要使用的變量中。
#include <iostream> // 為了使用 std::cout 和 std::cin
int main()
{
std::cout << "Enter a number: "; // 向用戶詢問(wèn)一個(gè)數(shù)
int x{ }; // 定義整形變量x來(lái)保存用戶輸入(將其初始化為0)
std::cin >> x; // 從鍵盤上獲取數(shù)字并存儲(chǔ)在變量x中
std::cout << "You entered " << x << '\n';
return 0;
}
嘗試編譯此程序并自己運(yùn)行它。運(yùn)行程序時(shí),第5行將打印“Enter a number: ”。當(dāng)代碼到達(dá)第8行時(shí),您的程序?qū)⒌却斎胼斎?。輸入?shù)字(然后按回車鍵)后,您輸入的數(shù)字將被分配給變量x。最后,在第10行,程序?qū)⒋蛴 癥ou entered”,后跟您剛剛輸入的數(shù)字。
例如(我輸入了 4):
Enter a number: 4
You entered 4
這是一種從用戶那里獲取鍵盤輸入的簡(jiǎn)單方法,我們將在以后的許多示例中使用它。請(qǐng)注意,接受輸入時(shí)不需要使用“\n”,因?yàn)橛脩粜枰聪翬nter鍵才能接受其輸入,這會(huì)將光標(biāo)移動(dòng)到控制臺(tái)的下一行。
就像可以在一行中輸出多位文本一樣,也可以在一行中輸入多個(gè)值:
#include <iostream>
int main()
{
std::cout << "Enter two numbers separated by a space: ";
int x{ }; // 定義變量x來(lái)保存用戶輸入(將其初始化為零)
int y{ }; // 定義變量y來(lái)保存用戶輸入(將其初始化為零)
std::cin >> x >> y; // 得到兩個(gè)數(shù)字,分別存儲(chǔ)在變量x和y中
std::cout << "You entered " << x << " and " << y << '\n';
return 0;
}
這將產(chǎn)生輸出:
Enter two numbers separated by a space: 5 6
You entered 5 and 6
2.1.7 總結(jié)
新程序員經(jīng)?;煜?code>std::cin、std::cout
、插入運(yùn)算符(<<)和提取運(yùn)算符(>>)。這里有一個(gè)簡(jiǎn)單的記憶方法:
-
std::cin
和std::cout
始終位于語(yǔ)句的左側(cè)。 -
std::cout
用于輸出值(cout = 字符輸出) -
std::cin
用于獲取輸入值(cin = 字符輸入) -
<<
與std::cout
一起使用,并顯示數(shù)據(jù)的移動(dòng)方向(如果std::cout
表示控制臺(tái),則輸出數(shù)據(jù)從變量移動(dòng)到控制臺(tái))。std::cout << 4
將值 4 移動(dòng)到控制臺(tái) -
>>
與std::cin
一起使用,并顯示數(shù)據(jù)的移動(dòng)方向(如果std::cin
表示鍵盤,則輸入數(shù)據(jù)從鍵盤移動(dòng)到變量)。std::cin >> x
將用戶從鍵盤輸入的值移動(dòng)到 x 中
2.1.8 練習(xí)時(shí)間
練習(xí)1
考慮我們上面使用的以下程序:
#include <iostream>
int main()
{
std::cout << "Enter a number: ";
int x{};
std::cin >> x;
std::cout << "You entered " << x << '\n';
return 0;
}
程序希望您輸入一個(gè)整數(shù)值,因?yàn)橛脩糨斎氲淖兞縳是一個(gè)整數(shù)變量。
多次運(yùn)行此程序,并描述輸入以下類型的輸入時(shí)會(huì)發(fā)生什么情況:
- 一個(gè)字母,例如h
- 帶有小數(shù)分量的數(shù)字。嘗試小數(shù)部分小于0.5和大于0.5的數(shù)字(例如3.2和3.7)。
- 一個(gè)小的負(fù)整數(shù),如-3
- 一個(gè)詞,例如Hello
- 一個(gè)非常大的數(shù)字(至少30億)
- 一個(gè)小數(shù)字后跟一些字母,例如123abc
2.2 未初始化的變量和未定義的行為
2.2.1 未初始化的變量
與某些編程語(yǔ)言不同,C/C++不會(huì)自動(dòng)將大多數(shù)變量初始化為給定值(如0)。因此,當(dāng)一個(gè)變量被賦予一個(gè)用于存儲(chǔ)數(shù)據(jù)的內(nèi)存地址時(shí),該變量的默認(rèn)值恰好已經(jīng)存在于該內(nèi)存地址中的那個(gè)(垃圾)值!沒(méi)有被賦予已知值(通常通過(guò)初始化或賦值)的變量稱為未初始化變量。
拓展
許多讀者期望術(shù)語(yǔ)“初始化”和“未初始化”是嚴(yán)格的對(duì)立,但事實(shí)并非如此!初始化意味著在定義時(shí)為對(duì)象提供了初始值。未初始化意味著對(duì)象尚未被賦予已知值(通過(guò)任何方式,包括賦值)。因此,一個(gè)沒(méi)有初始化但后來(lái)被賦值的對(duì)象不再是未初始化的(因?yàn)樗呀?jīng)被賦予了一個(gè)已知的值)。
回顧一下:
- 初始化 --> 對(duì)象在定義時(shí)被賦予一個(gè)已知值。
- 賦值 --> 對(duì)象被賦予一個(gè)超出定義點(diǎn)的已知值。
- 未初始化 --> 對(duì)象尚未被賦予已知值。
拓展
缺乏初始化是從C繼承來(lái)的性能優(yōu)化,當(dāng)時(shí)計(jì)算機(jī)速度很慢。想象一下,你要從一個(gè)文件中讀入100000個(gè)值。在這種情況下,您可以創(chuàng)建100000個(gè)變量,然后用文件中的數(shù)據(jù)填充它們。
如果C++在創(chuàng)建時(shí)用默認(rèn)值初始化所有這些變量,這將導(dǎo)致100000次初始化(很慢),并且?guī)缀鯖](méi)有好處(因?yàn)闊o(wú)論如何都要覆蓋這些值)。
現(xiàn)在,您應(yīng)該始終初始化變量,因?yàn)檫@樣做的成本與收益相比微不足道。一旦您對(duì)語(yǔ)言更加熟悉,可能會(huì)在某些情況下出于優(yōu)化目的而省略初始化。但這應(yīng)該始終是有選擇性和有意識(shí)的。
使用未初始化變量的值可能會(huì)導(dǎo)致意外結(jié)果??紤]以下簡(jiǎn)短程序:
#include <iostream>
int main()
{
// 定義整形變量x
int x; // 這個(gè)變量是未初始化的,因?yàn)槲覀儧](méi)有給它賦予已知值
// 將x的值打印到屏幕
std::cout << x << '\n'; // 誰(shuí)知道我們會(huì)獲得什么呢!因?yàn)槲覀儧](méi)有給它賦值
return 0;
}
在這種情況下,計(jì)算機(jī)會(huì)將一些未使用的內(nèi)存分配給x。然后,它將內(nèi)存位置的值發(fā)送到std::cout
,后者將打印該值(輸出為整數(shù))。但是它會(huì)打印什么值呢?答案是“誰(shuí)知道呢!”,每次運(yùn)行程序時(shí),答案可能會(huì)(也可能不會(huì))改變。當(dāng)作者在Visual Studio中運(yùn)行這個(gè)程序時(shí),std::cout
一次打印值7177728 ,下一次打印值5277592 。您可以自己編譯并運(yùn)行該程序(您的計(jì)算機(jī)不會(huì)爆炸)。
警告
當(dāng)您使用調(diào)試生成配置時(shí),某些編譯器(如VisualStudio)會(huì)將內(nèi)存內(nèi)容初始化為某個(gè)預(yù)設(shè)值。使用發(fā)布構(gòu)建配置時(shí)不會(huì)發(fā)生這種情況。因此,如果你想自己運(yùn)行上面的程序,請(qǐng)確保你使用的是發(fā)布版本配置。例如,如果您在Visual Studio調(diào)試配置中運(yùn)行上述程序,它將始終打印-858993460,因?yàn)檫@是Visual Studio在調(diào)試配置中初始化內(nèi)存的值(輸出為整數(shù))。
大多數(shù)現(xiàn)代編譯器將嘗試檢測(cè)是否在未給定值的情況下使用變量。如果他們能夠檢測(cè)到這一點(diǎn),他們通常會(huì)發(fā)出編譯時(shí)警告或錯(cuò)誤。例如,在Visual Studio上編譯上述程序會(huì)產(chǎn)生以下警告:
warning C4700: uninitialized local variable 'x' used
如果你的編譯器不允許你編譯和運(yùn)行上面的程序(例如因?yàn)樗鼘⒋藛?wèn)題視為錯(cuò)誤),以下是解決此問(wèn)題的可能解決方案:
#include <iostream>
void doNothing(int&) // 現(xiàn)在不要擔(dān)心&是什么,我們只是用它來(lái)欺騙編譯器認(rèn)為使用了變量x
{
}
int main()
{
// 定義整形變量x
int x; // 變量是未初始化的
doNothing(x); // 讓編譯器認(rèn)為我們?cè)诮o這個(gè)變量賦值
// 將x的值打印到屏幕上(誰(shuí)知道我們會(huì)得到什么,因?yàn)閤沒(méi)有初始化)
std::cout << x << '\n';
return 0;
}
使用未初始化的變量是新手程序員最常犯的錯(cuò)誤之一,不幸的是,它也可能是調(diào)試最具挑戰(zhàn)性的錯(cuò)誤之一(因?yàn)槿绻闯跏蓟淖兞颗銮杀环峙涞絻?nèi)存中具有合理值的位置,例如0,程序可能會(huì)運(yùn)行得很好)。
這是最佳實(shí)踐“始終初始化變量”的主要原因。
2.2.2 未定義行為
使用未初始化變量的值是我們第一個(gè)未定義行為的例子。未定義行為(Undefined behavior,通??s寫為UB)是執(zhí)行C++語(yǔ)言未定義行為的代碼的結(jié)果。在這種情況下,C++語(yǔ)言沒(méi)有任何規(guī)則來(lái)確定如果使用尚未給定已知值的變量的值會(huì)發(fā)生什么。因此,如果你真的這樣做,將導(dǎo)致未定義的行為。
實(shí)現(xiàn)未定義行為的代碼可能會(huì)出現(xiàn)以下癥狀:
- 程序每次運(yùn)行都會(huì)產(chǎn)生不同的結(jié)果。
- 你的程序總是產(chǎn)生同樣的錯(cuò)誤結(jié)果。
- 你的程序行為不一致(有時(shí)產(chǎn)生正確的結(jié)果,有時(shí)不)。
- 你的程序看起來(lái)像是在工作,但是在程序的后面產(chǎn)生了不正確的結(jié)果。
- 你的程序崩潰了,要么立即崩潰,要么稍后崩潰。
- 你的程序可以在某些編譯器上工作,但在其他編譯器上不行。
- 你的程序可以正常工作,直到你改變了其他一些看似無(wú)關(guān)的代碼。
或者,您的代碼實(shí)際上可能會(huì)產(chǎn)生正確的行為。
C++包含許多情況,如果你不小心,可能會(huì)導(dǎo)致未定義的行為。注意這些情況,并確保避免它們。
規(guī)則
注意避免所有導(dǎo)致未定義行為的情況,例如使用未初始化的變量。
2.2.3 明確定義的行為和不明確定義的行為
明確定義的行為意味著一些語(yǔ)法的行為留給實(shí)現(xiàn)(編譯器)來(lái)定義。這樣的行為必須是一致的并且有文檔記錄的,但是不同的編譯器可能會(huì)產(chǎn)生不同的結(jié)果。
讓我們看一個(gè)實(shí)現(xiàn)定義的行為的簡(jiǎn)單示例:
#include <iostream>
int main()
{
std::cout << sizeof(int); // 打印一個(gè)int變量占用多少字節(jié)的內(nèi)存
return 0;
}
在大多數(shù)編譯器上,這將產(chǎn)生4 ,但在其他編譯器上,它可能會(huì)產(chǎn)生2 。
未明確定義的行為與明確定義的行為幾乎相同,因?yàn)樾袨橛蓪?shí)現(xiàn)決定,但不要求實(shí)現(xiàn)記錄行為。
我們通常希望避免實(shí)現(xiàn)定義的和未指定的行為,因?yàn)檫@意味著如果在不同的編譯器上編譯,我們的程序可能無(wú)法按預(yù)期工作(甚至如果我們更改影響實(shí)現(xiàn)行為的項(xiàng)目設(shè)置,則在同一編譯器上編譯!)
最佳實(shí)踐
盡可能避免實(shí)現(xiàn)定義的和未指定的行為,因?yàn)樗鼈兛赡軐?dǎo)致程序在其他實(shí)現(xiàn)上出現(xiàn)故障。
2.2.4 練習(xí)時(shí)間
練習(xí)2
什么是未初始化的變量?為什么要避免使用它們?
練習(xí)3
什么是未定義的行為?如果你做了一些表現(xiàn)出未定義行為的事情,會(huì)發(fā)生什么?
2.3 關(guān)鍵字和命名標(biāo)識(shí)符
2.3.1 關(guān)鍵字
C++保留了92個(gè)關(guān)鍵字(從C++23開始)供己使用。這些詞稱為關(guān)鍵字(或保留字),每個(gè)關(guān)鍵字在C++語(yǔ)言中都有特殊的含義。
以下是所有C++關(guān)鍵字的列表(到C++23):
- | 1 | 2 | 3 | 4 |
---|---|---|---|---|
1 | alignas | alignof | and | and_eq |
2 | asm | auto | bitand | bitor |
3 | bool | break | case | catch |
4 | char | char8_t (C++20起) | char16_t | char32_t |
5 | class | compl | concept (C++20起) | const |
6 | consteval (C++20起) | constexpr | constinit (C++20起) | const_cast |
7 | continue | co_await (C++20起) | co_return (C++20起) | co_yield (C++20起) |
8 | decltype | default | delete | do |
9 | double | dynamic_cast | else | enum |
10 | explicit | export | extern | false |
11 | float | for | friend | goto |
12 | if | inline | int | long |
13 | mutable | namespace | new | noexcept |
14 | not | not_eq | nullptr | operator |
15 | or | or_eq | private | protected |
16 | public | register | reinterpret_cast | requires (C++20起) |
17 | short | signed | sizeof | static |
18 | static_cast | struct | switch | template |
19 | thread_local | throw | true | try |
20 | typeid | typename | union | unsigned |
21 | virtual | void | volatile | wchar_t |
22 | while | xor | xor_eq |
標(biāo)記了“C++20起”的關(guān)鍵字是在在C++20中添加的。如果您的編譯器不兼容C++20(或者具有C++20功能,但默認(rèn)情況下關(guān)閉),則這些關(guān)鍵字可能不起作用。
C++還定義了特殊標(biāo)識(shí)符:override
、final
、import
和module
。這些在某些上下文中使用時(shí)具有特定含義,但在其他情況下不保留。
您已經(jīng)遇到了其中的一些關(guān)鍵字,包括int
和return
。沿著一組運(yùn)算符,這些關(guān)鍵字和特殊標(biāo)識(shí)符定義了整個(gè)C++語(yǔ)言(預(yù)處理器命令除外)。由于關(guān)鍵字和特殊標(biāo)識(shí)符具有特殊的含義,IDE可能會(huì)更改這些單詞的文本顏色,使它們從其他標(biāo)識(shí)符中脫穎而出。
2.3.2 標(biāo)識(shí)符命名規(guī)則
變量(或函數(shù),類型或其他類型的項(xiàng))的名稱稱為標(biāo)識(shí)符。C++有著很大的靈活性,可以根據(jù)你的意愿命名標(biāo)識(shí)符。但是,在命名標(biāo)識(shí)符時(shí)必須遵循以下幾條規(guī)則:
- 標(biāo)識(shí)符不能是關(guān)鍵字。
- 標(biāo)識(shí)符只能由字母(小寫或大寫)、數(shù)字和下劃線字符組成。這意味著標(biāo)識(shí)符不能包含符號(hào)(下劃線除外)或空格(空格或制表符)。
- 標(biāo)識(shí)符必須以字母(小寫或大寫)或下劃線開始。不能以數(shù)字開頭。
- C++是區(qū)分大小寫的。 value 不同于 Value 不同于 VALUE 。
2.3.3 標(biāo)識(shí)符命名最佳實(shí)踐
現(xiàn)在你已經(jīng)知道了如何命名一個(gè)變量,讓我們來(lái)談?wù)勀銘?yīng)該怎樣命名一個(gè)變量(或函數(shù))。
首先,C++中的一個(gè)約定是變量名應(yīng)該以小寫字母開始。如果變量名是一個(gè)單詞,則整個(gè)內(nèi)容都應(yīng)該用小寫字母書寫。
int value; // 正規(guī)的
int Value; // 非正規(guī)的(應(yīng)該以小寫字母開頭)
int VALUE; // 非正規(guī)的(應(yīng)該以小寫字母開頭)
int VaLuE; // 非正規(guī)的(去看心理醫(yī)生吧)
大多數(shù)情況下,函數(shù)名也以小寫字母開頭(盡管在這一點(diǎn)上存在一些分歧)。我們將遵循這個(gè)約定,因?yàn)楹瘮?shù)main(所有程序都必須有)以小寫字母開頭,就像C++標(biāo)準(zhǔn)庫(kù)中的所有函數(shù)一樣。
以大寫字母開頭的標(biāo)識(shí)符名稱通常用于用戶定義的類型(例如結(jié)構(gòu)、類和枚舉,我們將在后面介紹所有這些)。
如果變量或函數(shù)名是多字的,則有兩種常見(jiàn)約定:用下劃線分隔的單詞(有時(shí)稱為snake_case),或用大寫字母分隔的單詞(有時(shí)稱為camelCase,因?yàn)榇髮懽帜赶耨橊劦鸟劮逡粯油怀觯?/p>
int my_variable_name; // 正規(guī)的(由下劃線分隔)
int my_function_name(); // 正規(guī)的(由下劃線分隔)
int myVariableName; // 正規(guī)的(由大寫字母分隔)
int myFunctionName(); // 正規(guī)的(由大寫字母分隔)
int my variable name; // 無(wú)效的(不允許空格)
int my function name(); // 無(wú)效的(不允許空格)
int MyVariableName; // 非常規(guī)(應(yīng)該以小寫字母開頭)
int MyFunctionName(); // 非常規(guī)(應(yīng)該以小寫字母開頭)
在本教程中,我們通常使用第二種方法,因?yàn)樗菀组喿x(在密集的代碼塊中很容易將下劃線誤認(rèn)為空格)。但兩者都很常見(jiàn)——C++標(biāo)準(zhǔn)庫(kù)對(duì)變量和函數(shù)都使用下劃線方法。有時(shí)你會(huì)看到兩者的混合:下劃線用于變量,大寫字母用于函數(shù)。
值得注意的是,如果你正在使用別人的代碼,通常認(rèn)為與你正在使用的代碼的風(fēng)格相匹配比嚴(yán)格遵循上面列出的命名約定更好。
其次,應(yīng)該避免以下劃線開頭命名標(biāo)識(shí)符,因?yàn)檫@些名稱通常是為操作系統(tǒng)、庫(kù)和(或)編譯器保留的。
第三,你的標(biāo)識(shí)符應(yīng)該明確它們所持有的值意味著什么(特別是如果單位不明顯的話)。標(biāo)識(shí)符的命名方式應(yīng)該能夠幫助那些不知道你的代碼是什么的人盡快弄清楚。長(zhǎng)時(shí)間后,當(dāng)你再看你的程序時(shí),你會(huì)感謝自己選擇了有意義的變量名。
然而,給一個(gè)微不足道的變量起一個(gè)過(guò)于復(fù)雜的名字會(huì)妨礙對(duì)程序正在做什么的全面理解,就像給一個(gè)廣泛使用的標(biāo)識(shí)符一個(gè)不適當(dāng)?shù)拿忠粯?。因此,一個(gè)好的經(jīng)驗(yàn)法則是使標(biāo)識(shí)符的長(zhǎng)度與它的使用范圍成正比。具有普通用途的標(biāo)識(shí)符可以具有短名稱(例如i)。更廣泛使用的標(biāo)識(shí)符(例如從程序中的許多不同位置調(diào)用的函數(shù))應(yīng)該具有更長(zhǎng)且更具描述性的名稱(例如使用openFileOnDisk而不是open)。
標(biāo)識(shí)符 | 好/壞 | 原因 |
---|---|---|
int ccount | 壞 | “count”前的“c”代表什么? |
int customerCount | 好 | 清楚我們?cè)谟?jì)數(shù)什么 |
int i | 也許 | 如果是不重要的,那么就是好的,否則就是壞的 |
int index | 也許 | 如果索引知道是什么就是好的 |
int totalScore | 也許 | 如果只有一個(gè)東西的分?jǐn)?shù),那就是好的,否則它太模糊 |
int _count | 壞 | 不要使用下劃線開頭 |
int count | 也許 | 如果清楚計(jì)數(shù)的是什么,那就是好的 |
int data | 壞 | 什么數(shù)據(jù)? |
int time | 壞 | 秒,分,還是小時(shí)? |
int minutesElapsed | 好 | 描述清楚 |
int value1, value2 | 也許 | 很難區(qū)分兩者 |
int numApples | 好 | 描述清楚 |
int monstersKilled | 好 | 描述清楚 |
int x, y | 也許 | 如果是不重要的,那么就是好的,否則就是壞的 |
在任何情況下,避免縮寫(除非它們是明確的)。雖然它們減少了編寫代碼所需的時(shí)間,但它們使代碼更難閱讀。閱讀代碼的次數(shù)比編寫代碼的次數(shù)多,你在編寫代碼時(shí)節(jié)省的時(shí)間是每個(gè)讀者,包括未來(lái)的你,在閱讀代碼時(shí)浪費(fèi)的時(shí)間。如果您希望更快地編寫代碼,請(qǐng)使用編輯器的自動(dòng)完成功能。
對(duì)于變量聲明,使用注釋來(lái)描述變量的用途或解釋其他可能不明顯的內(nèi)容是很有用的。例如,假設(shè)我們聲明了一個(gè)名為numberOfChars
的變量,它應(yīng)該存儲(chǔ)一段文本中的字符數(shù)。文本“Hello World!”有10個(gè)、11個(gè)還是12個(gè)字符?這取決于我們是否包括空格或標(biāo)點(diǎn)符號(hào)。與其命名為變量numberOfCharsIncludingWhitespaceAndPunctuation
,這是相當(dāng)冗長(zhǎng)的,在聲明行上或上方放置適當(dāng)?shù)淖⑨寫?yīng)該有助于用戶理解它:
// 一段文本中的字符數(shù),包括空格和標(biāo)點(diǎn)符號(hào)
int numberOfChars;
2.3.4 練習(xí)時(shí)間
練習(xí)4
根據(jù)如何命名一個(gè)變量,指出每個(gè)變量名是否正規(guī)或無(wú)效,以及為什么。
(1)
int sum {}; // 假設(shè)我們求的和是顯而易見(jiàn)的
(2)
int _apples {};
(3)
int VALUE {};
(4)
int my variable name {};
(5)
int TotalCustomers {};
(6)
int void {};
(7)
int numFruit {};
(8)
int 3some {};
(9)
int meters_of_pipe {};
2.4 空白和基本格式
空白是一個(gè)術(shù)語(yǔ),指用于格式化的字符。在C++中,這主要是指空格、制表符和換行符。C++中的空白通常用于3件事:分隔某些語(yǔ)言元素、在文本內(nèi)部以及用于格式化代碼。
2.4.1 某些語(yǔ)言元素必須以空格分隔
該語(yǔ)言的語(yǔ)法要求某些元素用空格分隔。當(dāng)兩個(gè)關(guān)鍵字或標(biāo)識(shí)符必須連續(xù)放置以便編譯器可以區(qū)分它們時(shí),通常會(huì)出現(xiàn)這種情況。
例如,變量聲明必須用空格分隔:
int x; // int 和 x 之間用空格分隔
如果我們輸入intx ,編譯器會(huì)將其解釋為標(biāo)識(shí)符,然后抱怨它不知道標(biāo)識(shí)符intx是什么。
另一個(gè)例子,函數(shù)的返回類型和名稱必須用空格分隔:
int main(); // int 和 main 之間用空格分隔
當(dāng)需要空白作為分隔符時(shí),編譯器不關(guān)心使用了多少空白,只要存在一些空白即可。
以下變量定義均有效:
int x;
int y;
int
z;
在某些情況下,換行符用作分隔符。單行注釋以換行符結(jié)束。
舉個(gè)例子,做這樣的事情會(huì)讓你陷入麻煩:
std::cout << "Hello world!"; // 這是注釋的一部分
這不是注釋的一部分
預(yù)處理器指令(例如#include <iostream>
)也必須放在單獨(dú)的行上:
#include <iostream>
#include <string>
2.4.2 引用的文本按字面意思使用空格的數(shù)量
在引用的文本中,空格的數(shù)量是按字面意思計(jì)算的。
std::cout << "Hello world!";
不同于:
std::cout << "Hello world!";
引用的文本中不允許有換行符:
std::cout << "Hello
world!"; // 不允許!
引用的文本由空白(空格、制表符或換行符)分隔,將被連接:
std::cout << "Hello "
"world!"; // 打印"Hello world!"
2.4.3 使用空白來(lái)格式化代碼
空白通常被忽略。這意味著我們可以在任何我們喜歡的地方使用空白來(lái)格式化代碼,以使其更容易閱讀。
例如,以下內(nèi)容很難閱讀:
#include <iostream>
int main(){std::cout<<"Hello world";return 0;}
以下是更好的(但仍然相當(dāng)密集):
#include <iostream>
int main() {
std::cout << "Hello world";
return 0;
}
如果需要,可以將語(yǔ)句拆分為多行:
#include <iostream>
int main()
{
std::cout
<< "Hello world"; // 正常工作
return 0;
}
這對(duì)于特別長(zhǎng)的語(yǔ)句很有用。
2.4.4 基本格式
與其他語(yǔ)言不同,C++不對(duì)程序員施加任何格式限制。因此,我們說(shuō)C++是一個(gè)空白獨(dú)立的語(yǔ)言。
這是喜憂參半的。一方面,有做任何你喜歡的事情的自由是很好的。另一方面,這些年來(lái)已經(jīng)開發(fā)了許多不同的格式化C++程序的方法,你會(huì)發(fā)現(xiàn)(有時(shí)是重要的和分散注意力的)關(guān)于哪些是最好有很多分歧。我們的基本經(jīng)驗(yàn)法則是,最好的樣式是生成最可讀的代碼,并提供最大一致性的樣式。
以下是我對(duì)基本格式的建議:
- 可以使用制表符或空格進(jìn)行縮進(jìn)(大多數(shù)IDE都有這個(gè)設(shè)置,可以將制表符按下轉(zhuǎn)換為適當(dāng)數(shù)量的空格)。
- 函數(shù)大括號(hào)有兩種可接受的樣式。
Google C++樣式指南建議將左花括號(hào)放在與語(yǔ)句相同的行上:
int main() {
}
這樣做的理由是,它減少了垂直空格的數(shù)量(除了開始的花括號(hào)之外,您不會(huì)將整行都用于其他內(nèi)容),因此您可以在屏幕上容納更多的代碼。屏幕上的代碼越多,程序就越容易理解。
但是,我更喜歡常見(jiàn)的替代方案,其中左大括號(hào)出現(xiàn)在自己的行上:
int main()
{
}
這增強(qiáng)了可讀性,并且不容易出錯(cuò),因?yàn)榇罄ㄌ?hào)對(duì)應(yīng)該總是縮進(jìn)在同一級(jí)別。如果由于大括號(hào)不匹配而出現(xiàn)編譯器錯(cuò)誤,很容易看出錯(cuò)誤的位置。
- 花括號(hào)內(nèi)的每個(gè)語(yǔ)句都應(yīng)該從它所屬函數(shù)的左括號(hào)開始一個(gè)制表符。舉例來(lái)說(shuō):
int main()
{
std::cout << "Hello world!\n"; // 一個(gè)制表符(4個(gè)空格)
std::cout << "Nice to meet you.\n"; // 一個(gè)制表符(4個(gè)空格)
}
- 行不應(yīng)該太長(zhǎng)。通常,80個(gè)字符已經(jīng)成為行的最大長(zhǎng)度的事實(shí)標(biāo)準(zhǔn)。如果一行代碼需要很長(zhǎng),它應(yīng)該被分割(在一個(gè)合理的點(diǎn))成多行。
int main()
{
std::cout << "這是一個(gè)很長(zhǎng)很長(zhǎng)很長(zhǎng)很長(zhǎng)很長(zhǎng)很長(zhǎng)很長(zhǎng)很長(zhǎng)很長(zhǎng)很長(zhǎng)很長(zhǎng)很長(zhǎng)"
"很長(zhǎng)的代碼\n"; // 延續(xù)行的一個(gè)額外縮進(jìn)
std::cout << "這是另一一個(gè)很長(zhǎng)很長(zhǎng)很長(zhǎng)很長(zhǎng)很長(zhǎng)很長(zhǎng)很長(zhǎng)很長(zhǎng)很長(zhǎng)很長(zhǎng)很長(zhǎng)很長(zhǎng)"
"很長(zhǎng)的代碼\n"; // 文本與上一行對(duì)齊,作為延續(xù)行
std::cout << "這一行比較短\n";
}
最佳實(shí)踐
考慮將行的長(zhǎng)度控制在80個(gè)字符或更少。
- 如果使用操作符(例如<<或+),則運(yùn)算符應(yīng)放在下一行的開頭,而不是當(dāng)前行的結(jié)尾。
std::cout << 3 + 4
+ 5 + 6
* 7 * 8;
- 使用空白可以通過(guò)對(duì)齊值或注釋或在代碼塊之間添加間距來(lái)使代碼更易于閱讀。
更難閱讀:
cost = 57;
pricePerItem = 24;
value = 5;
numberOfItems = 17;
更容易閱讀:
cost = 57;
pricePerItem = 24;
value = 5;
numberOfItems = 17;
更難閱讀:
std::cout << "Hello world!\n"; // cout 在 iostream 庫(kù)中
std::cout << "It is very nice to meet you!\n"; // 這些注釋使程序難以閱讀
std::cout << "Yeah!\n"; // 特別是不同行的時(shí)候
更容易閱讀:
std::cout << "Hello world!\n"; // cout 在 iostream 庫(kù)中
std::cout << "It is very nice to meet you!\n"; // 這些注釋使程序更容易閱讀
std::cout << "Yeah!\n"; // 尤其是當(dāng)所有行都排隊(duì)的時(shí)候
2.5 本章答案
練習(xí)1
- x為0。
- 小數(shù)部分被舍棄。
由于列表初始化不允許收縮轉(zhuǎn)換,新程序員有時(shí)會(huì)搞不清楚為什么這里允許刪除分?jǐn)?shù)。x的初始化發(fā)生在第6行,對(duì)收縮轉(zhuǎn)換的限制只適用于這一行的列表初始化。用戶的輸入被處理并賦給第7行的x(此處此類限制不適用,因?yàn)榇苏Z(yǔ)句不是列表初始化)。 - 正常運(yùn)行。
- x為0。
- 你最有可能得到的號(hào)碼是2147483647。這是因?yàn)閤只能容納一定大小的數(shù)字。如果您輸入的值大于x可以容納的最大數(shù)值,它將被設(shè)置為x可以容納的最大數(shù)值(可能是2147483647,但在您的系統(tǒng)上可能有所不同)。
- x獲取數(shù)值(例如123)。
練習(xí)2
未初始化的變量是一個(gè)沒(méi)有被程序賦予值的變量(通常通過(guò)初始化或賦值)。使用存儲(chǔ)在未初始化變量中的值將導(dǎo)致未定義的行為。
練習(xí)3
未定義的行為是執(zhí)行行為沒(méi)有被語(yǔ)言很好定義的代碼的結(jié)果。結(jié)果幾乎可以是任何東西,包括行為正確的東西。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-733131.html
練習(xí)4
(1)正規(guī)。
(2)不正規(guī)。變量名不應(yīng)該以下劃線開頭。
(3)不正規(guī)。變量名應(yīng)該以小寫字母開頭。
(4)無(wú)效。變量名不能包含空格。
(5)非正規(guī)。變量名應(yīng)該以小寫字母開頭。
(6)無(wú)效。void是關(guān)鍵字。
(7)正規(guī)。
(8)無(wú)效。變量名不能以數(shù)字開頭。
(9)正規(guī)。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-733131.html
到了這里,關(guān)于【C++從入門到精通】第2篇:C++基礎(chǔ)知識(shí)(中)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!