關於C++中的下劃線?

《C++Primer》第5版2.2.3標識符這一節里說,…….The

identifiers we define in our own programs may not contain two consecutive

underscores, nor can an identifier begin with an underscore followed immediately by

an uppercase letter. In addition, identifiers defined outside a function may not begin

with an underscore. 中文版里是將may not翻譯為「不能」,而實際上C++語法是允許以下劃線開頭的,那麼這裡的may not 翻譯成「不應該」是不是更恰當呢?另外我自己喜歡以下劃線命名全局變數,這是不是一種很不好的命名習慣?


語法上可以過,但是絕對不要這樣做。因為「__"打頭的和"_"加大寫字母打頭的名字是給編譯器實現保留的。如果你碰巧和編譯器實現者取了同樣的名字,就大坑了,這和編譯器相關。。。

但是除此以外,"_"後面跟一個小寫字母的標識符是可以用的,有人喜歡用它表示類的私有變數,表示全局變數真心沒見過。

請參考:

C++ 03標準(C++ 11沒變)

17.4.3.2.1 Global names [lib.global.names]

Certain sets of names and function signatures are always reserved to the implementation:

  • Each name that contains a double underscore (_ _) or begins with an underscore followed by an uppercase letter (2.11) is reserved to the implementation for any use.
  • Each name that begins with an underscore is reserved to the implementation for use as a name in the global namespace.165

165) Such names are also reserved in namespace ::std (17.4.3.1).

還有C99標準

7.1.3 Reserved identifiers

Each header declares or defines all identifiers listed in its associated subclause, and optionally declares or defines identifiers listed in its associated future library directions subclause and identifiers which are always reserved either for any use or for use as file scope identifiers.

  • All identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use.
  • All identifiers that begin with an underscore are always reserved for use as identifiers with file scope in both the ordinary and tag name spaces.
  • Each macro name in any of the following subclauses (including the future library directions) is reserved for use as specified if any of its associated headers is included; unless explicitly stated otherwise (see 7.1.4).
  • All identifiers with external linkage in any of the following subclauses (including the future library directions) are always reserved for use as identifiers with external linkage.154
  • Each identifier with file scope listed in any of the following subclauses (including the future library directions) is reserved for use as a macro name and as an identifier with file scope in the same name space if any of its associated headers is included.

No other identifiers are reserved. If the program declares or defines an identifier in a context in which it is reserved (other than as allowed by 7.1.4), or defines a reserved identifier as a macro name, the behavior is undefined.

If the program removes (with #undef) any macro definition of an identifier in the first group listed above, the behavior is undefined.

154) The list of reserved identifiers with external linkage includes errno, math_errhandling,setjmp, and va_end.


首先,C++里關於下劃線的問題是源於C語言,因為C++允許用extern "C"來修飾代碼以C語言語法方式編譯。

然後說C語言里的下劃線:

C語言確實允許以下劃線開頭的函數存在,實際上你用一個下劃線開頭的函數名或者變數是沒問題的,但有可能會發生命名衝突。

凡是以兩個或一個下劃線開始,後面緊跟著一個大寫字母的標識符,不管它出現在哪裡,都是保留給編譯程序或標準庫函數使用的。此外,凡是以一個下劃線開始,後面不管跟著什麼內容的標識符,如果它出現在文件範圍內(即它不是出現在一個函數內),那麼它也是被保留的。

如果你用一個保留的標識符來作一個變數的名稱,結果是沒有定義的(程序可能無法編譯,或者可以編譯但會崩潰)。即使你能非常幸運地找到一個目前還沒有被你的編譯程序或函數庫使用的標識符,你也應該記住這樣的標識符是保留起來供將來使用的。因此,最好還是避免使用以下劃線開始的變數名或函數名。舉例說明:

VC里getch函數不是一個標準庫函數,因此,它的名字是_getch,大多數編譯器都把非標準庫、但又是編譯器自己提供的庫函數以下劃線開頭。

另外,也有編譯器的main函數在編譯完成以後,名字是_main,如果你自己寫了一個函數叫_main,那麼編譯器可能不知道最終該鏈接哪個。

所以,你可以理解為:編譯器需要預留一些名字,為了方便起見,大多數編譯器預留的名字都是以下劃線開頭的,久而久之就形成了標準。

雖然下劃線開頭也可以用,但不推薦使用,因為容易造成名字衝突。


從語言律師的角度來講,你用了保留字就UB了

而UB是沒有為什麼的


剛好手頭上就有一本《C++ Primer》第五版的中文版,在P42上提到這個建議的原文是

同時,C++也為標準庫保留了一些名字。用戶自定義的標識符中不能連續出現兩個下畫線,也不能以下畫線緊連大寫字母開頭。此外,定義在函數體外的標識符不能以下畫線開頭。

引用:

  • 《C++ Primer》第五版 中文版 電子工業出版社 2013年9月第一次印刷版


我舉個例子,曾經知乎上有一位屠龍少年,在 VS 里寫下了這樣的代碼:

class Foo
{
public:
explicit Foo(int __in)
{
_in = __in;
}

private:
int _in;
};

int main()
{
return 0;
}

----


may not是不能,你說的不應該是shall not

萬一哪天編譯器心血來潮把__vczh給用了你寫了15年的程序就掛掉了,何苦呢

全局的名字如果你一定要加下劃線,你可以加後面呀

評論說後面也不行,那就學我全局前面加g_


在C語言的年代,

人們為了避免變數/函數名稱衝突,喜歡給名稱前加若干個下劃線。

但是到了C++的年代,

有了namespace之後,

只需要將變數/函數名稱放到不同的namespace就可以避免名稱衝突,

所以就不需要/提倡使用下劃線這種古老/原始/醜陋的辦法了。


說是這麼說,但是我不相信編譯器會把namespace __vczh給用了。


雖然說是不可以…

然而我不信編譯器會在我的VFMemBlock里插一個__iBlockCount

更不信會在裡面插一個

__fLocalMalloc


最好不要這樣用,下劃線開頭的標識符是預留給標準庫和編譯器實現使用的,你自己用的話存在和某個標準庫或編譯期的實現衝突的風險


原文意思應該是在我們的程序里可能不能這麼用。

所以翻譯過來應該是不建議這麼用。

但是如果按照翻譯過來的說法寫進去會出現大量的程序員(可能以我這種新手為主)表示:我不接受你的建議。

如果按照原意翻譯成可能不能這麼用那就更出事了,這邊就可能理解成,哦,可以這麼用,偶爾出毛病罷了。

所以翻譯成強制的不能竊以為是譯者為了避免這種問題規範語法吧


may not就是「不能」/「不準」這樣語氣強硬的意思,「不應該」對應的單詞是「should not」

利益相關:剛考完四級

「C++語法是允許以下劃線開頭的」,那麼是不是就可以用呢?打個比方,公路限速60km/h,但是我的車能開到200km/h,那麼我可不可以把車開到200km/h?不可以,不管路況多好都不可以


推薦閱讀:

Google C++ Style Guide 中為什麼禁止使用預設函數參數?
C++ 如何自動推導遞歸 lambda 函數的類型?
關於c++ default constructor的問題,這個說法對嗎?
c++中的左值跟右值怎麼區分?
語句str2= str1 + (str1.size() - 1," ")為什麼只有1個空格添加進去了?

TAG:編程語言 | 編程 | C編程語言 | C |