ISO C++ conformant _read()? XD
因為在 porting 某支程式,所以用 MSDN 找 read(),想要確定該 include 哪個 header,link 哪個 library,結果 MSDN 的 read() 頁面,僅僅顯示這麼一行:
This POSIX function is deprecated beginning in Visual C++ 2005. Use the ISO C++ conformant
_readinstead.
這真是活見鬼了,什麼時候 ISO C++ 裡有以底線開頭的 API 了?更別提 _read() 根本不是 ISO C++/POSIX 的 function。
好吧,他說的是 conformant,所以不是在說 _read() 是 ISO C++ 的標準,而是說用 _read() 的方法呼叫 read(),才符合 ISO C++ 的一些規定。但即使如此,這個解釋,還是怎麼聽來就怎麼怪。
勉強為其解釋,我記得 Microsoft 平台有一種 calling convention 在處理 name decoration (name mangling) 時,不會在前面加底線。因為在 ANSI C/ISO C++ 裡,實際上唯一有規定的 name decoration,就是「在 function 名字前面加底線」。也許是因為用了某種 calling convention,導致最後編譯成執行碼時,沒有在前面加底線,因此為了要在 binary level 與 run-time library 對應,所以程式裡要加底線。
也就是說,按正常標準的 C/C++/POSIX 標準,read() 是沒有底線的,而是在 compile-time,因為是 C linkage 的關係,compiler 負責幫我們加底線。但是在 VC2005 裡,因為用了某種 calling convention 的關係,compiler 不會幫我們加底線,所以我們的程式,必須加底線。
總結就是:VC2005 的某種不標準作法,導致我們必須寫不標準的程式。
確實是活見鬼,Microsoft 總是喜歡搞出這樣的飛機出來。查一下 read() 和 _read() 是怎麼在 Visual C++ 的 header 裡宣告的:
VC6 的 io.h 裡有:
_CRTIMP int __cdecl _read(int, void *, unsigned int); _CRTIMP int __cdecl read(int, void *, unsigned int);
Visual C++ 2005 的 io.h 裡有 (太長故折行):
_CRTIMP __checkReturn \
int __cdecl _read(__in int _FileHandle, \
__out_bcount(_MaxCharCount) void * _DstBuf, \
__in unsigned int _MaxCharCount);
...
_CRT_NONSTDC_DEPRECATE(_read) _CRTIMP \
int __cdecl read(int _FileHandle, \
__out_bcount(_MaxCharCount) void * _DstBuf, \
__in unsigned int _MaxCharCount);
都是用 __cdecl 宣告的。故再查了一下 MSDN 的這篇《Argument Passing and Naming Conventions》,發現常見的有這三種:
| keyword | parameter passing | stack cleanup | name-decoration |
|---|---|---|---|
__cdecl |
right to left | caller | Underscore character (_) is prefixed to names, except when exporting __cdecl functions that use C linkage. |
__stdcall |
right to left | callee | An underscore (_) is prefixed to the name. The name is followed by the at sign (@) followed by the number of bytes (in decimal) in the argument list. Therefore, the function declared as int func(int a, double b) is decorated as follows: _func@12. |
__fastcall |
The first two arguments are passed by registers; others are passed right to left. | callee | At sign (@) is prefixed to names; an at sign followed by the number of bytes (in decimal) in the parameter list is suffixed to names. |
至此,總算發現了「兇手」就是 __cdecl。對 __cdecl 的描述是這麼說的:
Underscore character (
_) is prefixed to names, except when exporting__cdeclfunctions that use C linkage.
翻成中文就是,「在 function 名字前面加底線,除非這個 function 採用 C linkage」。因為 read() 採用 C linkage,再加上 __cdecl,在編譯時 compiler 就不會幫我們加底線。是故,「為了符合 ISO C++ 規定」[1],我們的程式裡,必須用不標準的寫法,為 Microsoft奇怪的作法買單。
這也說明了,為甚麼許多的 POSIX 函式,在 VC 裡總要在前面加底線。這種情形不是 VS2005 問世後才有,至少 VC6 時代就有一大堆。這種搞法,實在是亂來。
再罵一次,這真是活見鬼了。
- 或者更明白地說,「為了要讓 VS2005 編譯出來的程式,能夠在任何在 binary level 只有提供
_read()之 run-time 環境(即所有版本的 windows)上執行」。 ↩
Random Posts
- None Found
Similar Posts
- None Found
3 Comments
我現在都會對這類 warning 視而不見了 *誤*
b6s,
如果是一個人搞的程式,這樣沒關係。但如果是要和同事一起合作,就很麻煩了。總不能平常都遊說同事,程式要寫到沒有半個 warning 才厲害,結果同事認真起來,又對他說,那是 Microsoft 的錯,不要理這個 warning。XD
Jeff Hung
那只好 #define _CRT_SECURE_NO_WARNINGS 了 XD
Post a Comment