學 C 語言時,有沒有遇到過讓你「痛不欲生」、「揪心」或「不得要領」的術語?現在又是怎麼理解它的?
輪帶逛有感: @vczh大大的回答說:
一開始的時候看到longjmp我就覺得這是個什麼雞吧東西,後來學了彙編,就明白了。
我對setjmp() / longjmp()的感想是:這玩兒用Clang編譯的話可能會運行出錯 &>_&<
==================================
回到正題。肯定有很多回答會說指針、函數、函數指針之類。我就先說點別的。
隨便舉個栗子:C裡面的cast是有內建的轉換語義的。例如說:int i = 1;
float f = (float) i;
此時f的值會是1.0F,等於來源 i 的整數值,而不是把 i 給 bitcast 成一個 float。
但是有時候我們就是要bitcast怎麼辦?C語言說:自己想辦法。於是就有這樣的標準解法:union {
int i;
float f;
} s;
s.i = 1;
float f = s.f;
這樣就能得到內部編碼為0x00000001的float,也就是數值為1.4E-45的那個float值了。
普通cast與bitcast概念的區別,大概能讓很多初學者痛不欲生好一會兒。
不過很快就能掌握。然後又可以原地滿血復活了 &>_&<掌握了bitcast的概念後,同學們對於各種類型的值的表現形式應該會有更深入的了解,然後就有很多引伸玩法了。於是這裡推薦3本我喜歡的書:- Introduction to Computing Systems: From Bits and Gates to C and Beyond
- Computer Systems: A Programmer"s Perspective (3rd Edition)
- Hacker"s Delight (2nd Edition)
==================================
至於評論區里說 bitcast 是跟指針與內存相關的概念——bitcast只跟值的表現形式有關係,跟內存/指針是正交的概念。
當然可以通過指針操作來實現bitcast,但以為那就是bitcast的本質就不對了。請看看這個例子:float bitcast(int i) {
union {
int i;
float f;
} s;
s.i = i;
return s.f;
}
在Linux x86-64上用Clang 3.7.1 -O2編譯得到的結果是:
bitcast(int): # @bitcast(int)
movd %edi, %xmm0
retq
/* LLVM IR
; Function Attrs: norecurse nounwind readnone
define float @bitcast(i32) local_unnamed_addr #0 {
%2 = bitcast i32 %0 to float
ret float %2
}
*/
在Linux ARM32上用GCC 4.8.2 -O2編譯得到的結果是:
bitcast(int):
vmov s0, r0
bx lr
在Linux MIPS64上用GCC 5.4 -O2編譯得到的結果是:
bitcast(int):
j $31
mtc1 $4,$f0
(給我自己的筆記:libFirm在x86-64上的結果也可以檢查一下。在32位x86上的結果也挺有趣)
請說說看這個bitcast實現中,指針與內存在哪裡?使用指針操作來實現bitcast自然是可以的:
float bitcast(int i) {
float f;
*((int*)f) = i;
return f;
}
通過指針與memcpy來實現bitcast也是可以的:
float bitcast(int i) {
float f;
memcpy(f, i, sizeof(int));
return f;
}
在不少編譯器上,這兩個版本(優化-)編譯出來的結果會跟前面用union的版本一樣。因為這些模式都實在是太常見了。
但既然不通過指針操作也可以實現,又怎能說它是本質。其實我現在有的時候,用 printf, fprintf, sprintf, snprintf, printf_s, fprintf_s 還是要看文檔。還要查 Fixed width integer types (since C99) 里的 PRIxx等,以前還有 VC 自訂的格式如 %Iu 等。
一堆括弧加指針,比如這種:int (*(*foo)(const void *))[3] 直接暈倒。後來有了讀了Clockwise/Spiral Rule 以及cdecl: C gibberish ? English ,逐漸可以理解和掌握了
句柄,handle!句柄你大爺!
一開始的時候看到longjmp我就覺得這是個什麼雞吧東西,後來學了彙編,就明白了。
一句話可以幫小白c碼農省好多事兒:treat warning as error
scanf的格式字元串其實可以當半個正則表達式用。。。
所以sscanf就有了很神奇的用法……預設,句柄,魯棒性
C99的compound literal
當然看不懂主要是因為C Primer Plus翻譯的太垃圾,什麼複合文字最後懂了,這不就是數組/結構體字面值嘛
所以我決定以後不向初學者推薦C Primer Plus了逗號表達式,到現在我都記不清規則... 公司禁用
嵌入式鏈表,一堆看不懂的宏,還有看不出來結構體類型的coredump
大一剛開始學那會兒,對於數組取地址的含義繞了很久最近一個應該是找模板批量特化的方法,最後還是無果,但沒那麼多時間研究了,不知道能不能實現這種:template &
//對上面這些類型有個特化實現方式
};還是說得動手把下面這些類型一個個都寫一遍hPenRed = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
hPen = SelectObject(hdc, hPenRed);
為什麼把新建的畫筆用SelectObject()選入DC以後返回的是之前畫筆的句柄?
因為一個DC里一次只能用一種畫筆,就像在紙上畫圖一樣,要換鴨嘴筆了,就把原來的鉛筆頂替出來了。畫完圖以後要SelectObject(hdc, hPen);
DeleteObject(hPenRed);
新建的畫筆是要佔內存的,畫完以後要把它釋放掉。不然畫圖的時候電腦一下子就……然後內存就被吃光了,這個是畫圖操作里最痛苦的。
如果不把原來的畫筆選回來,那麼DeleteObject()也就不能順利進行了。所以旁邊經常放著MSDN幫助文檔來供查閱,發現程序有問題就用VS去調試,不能指望用記事本去寫程序,不然這個efficiency會非常低下typedef 和 define
尤其define,年輕的時候以為就是很簡單的替換…後來…
函數指針,數組指針,指針數組。
我的第一個hello world程序全靠背。什麼iostream,我一直以為是跟蘋果有關係(有iOS啊,誰讓我是個果粉,下意識想到)。自習的時候還會默寫到紙上(我學的是商科,周圍的同學就都默默看著我,挺不好意思的)。後來懂了之後感覺當時的我好可愛。哦對了,我當時被騙了,學著C++還以為自己學的是C。不過後來我又把C學了一遍。
作為學嚴謹的Pascal入門,直接過渡到面向對象的C++,避開了c的很多奇巧淫技。
handle,是在接觸win32 api 最讓人難受的術語。
大學的時候還問過c++任課老師(浙大博士),當時給的答案就是句柄。切,這金山詞霸也知道,等於沒回答。現在看來,只是用c實現了部分面向對象思想的一種方式,
handle也好,context也好,只是一個指向特定結構的指針而已,用宏轉義罷了。
舉例:開源代碼XZip.h中聲明了一個自定義的handle,HZIP,DECLARE_HANDLE(HZIP);
#define DECLARE_HANDLE(name) struct name##__; typedef struct name##__ *name
展開就是
struct HZIP__;
typedef struct HZIP__ *HZIP;
XZip.cpp實際用起來,直接強轉:
typedef struct
{ DWORD flag;
TZip *zip;
} TZipHandleData;
TZipHandleData *han = new TZipHandleData;
han-&>flag = 2;
han-&>zip = zip;
return (HZIP)han;
總結:反正都當對象來,就沒啥疑義了。
設計者的某些設定,好比數學上的公理,接受就好了,深究為什麼,並沒多大意義。還有就是c++的template ,主題是討論c,就不多說了。printf和scanf的格式化語法有很大差異,一直是個坑我就不說微軟的更加特別了
我踏馬開始的時候怎麼都搞不懂賦值運算符,為此還和老師/學長來往了幾封郵件……就比如,咳:
int a = 1;
int b = a;為什麼啊為什麼啊 b 等於 a ??? 蛤???
所以後來被教導:等於你大爺啊,跟我讀,b 賦值為 a
ヾ(?&>﹏&)???*。C語言倒是沒有,當初學 C++ 的時候對繼承、重載相當迷惑。
後來學 Delphi 的時候弄明白了……(肯定是當初看的書不好~~~推薦閱讀:
※寫程序需要編譯器,編譯器是程序,輸入輸出也需要驅動,驅動也是程序,那麼第一個在電子計算機運行的程序是怎麼產生的?
※Scheme語言的優勢?
※Mathematica 能否成為取代 Python 乃至其他編程語言的程序設計語言?
※新入職的軟體開發公司,看不懂代碼怎麼辦?
※Python 中循環 import 造成的問題如何解決?