讀書筆記===C專家程式設計[1]
讀書筆記,僅作為博主關心的、不理解的 相關點的解釋,整理為筆記,方便之後的再次檢視。
第一章:C: 穿越時空的迷霧
C簡史:
a>Multics工程,目的是建立一個作業系統,,,,失敗==經驗
b>UNIX系統的產生:一個叫Ken Thompson的研究員+同事 Dennis Ritchie 移植某些軟體到PDP-7硬體平臺;期間Ken Thompson 為PDP-7 編寫了作業系統(彙編實現),最後 另一個大爺Brian Kernighan 在1970年給它取名為UNIX,並總結了Multics工程的經驗教訓。
c>C語言的產生:開發UNIX過程中,Ken Thompson+ Dennis Ritchie二位嘗試+創造了 BCPL> B> new B> C語言(早期的)==1965.7月–1972.3月期間
d>可移植的標準I/O庫(open、read、write等io函式):最早由Mike Lesk於1972年左右 編寫;
e>C語言最早流通版本(K&R C):1978年,經典著作 《The C Programming Language》出版,作者Ken Thompson+ Dennis Ritchie;二位因此而聞名
e2>C語言編譯器:1978年,steve Johnson編寫了pcc可移植的C編譯器
f> ANSI C:1983年,ANSI組織成立C語言工作小組,開始了C語言的標準化工作;
1989年12月,C語言標準草案被ANSI組織接納;之後ISO C標準出現;最終1990年ANSI C接納ISO C;於是 世人說的 標準C語言=ISO C,,,,但ANSI C參與的工作更多,業內名聲也就更大,最終定格於 ANSI C標準;
提及的技術點:
雖然約定採用“.h”作為標頭檔案的副檔名,但在標頭檔案和包含實現程式碼的 物件庫之間在命名上卻沒有相應的約定,這多少令人不快。????===不理解,
1978年,steve Johnson編寫了pcc可移植的C編譯器==有機會嘗試瞭解下。
可移植的程式碼定義:–p13
- 只是用ASNI C語言標準 已確定的 C語言特性
- 不突破由編譯器實現的限制
- 不產生任何依賴於編譯器定義的 或未確定或未定義等特性的 輸出
不可移植的程式碼:p12
a>由編譯器確定的特性:當整型數向右移位時,要不要擴充套件他的符號位???
int a= (~0);//0xFF FF FF FF
a>>=1;//此時 最高位 bit31位,應不應該動???
b>未確定的==ANSI C標準中,未明確規定該怎樣做
引數求值順序?
int i = 3;
printf("%d,%d,%d\n", i, ++i, ++i );
測試結果:5,5,5
感興趣的可以再試下:
int i = 3; printf("%d,%d,%d\n", i, i++, i++ );
測試結果:
5,4,3
已知的是:
1、引數傳遞順序:從右到左 入棧
2、i++,為先用後加;++i為加完再用。
壞程式碼:
c>未定義的:在某些不正確情況下的做法,ANSI C並未規定應該如何處理
eg:當一個有符號整數溢位時,該採取什麼行動???
p14:
/*並不嚴格遵守標準:其輸出結果是由編譯器定義的*/
#include <limits.h>
#include <stdio.h>
int main()
{
(void)printf("biggest int is %d",INT_MAX);
return 0;
}
INT_MAX巨集為標準C庫定義,為int型別的最大值;
對於16位編譯器,int佔16位(2位元組),int的最大值為32767.
對於32位編譯器,…
預處理:p17
a>替換源文字中的三聯符。三連符是C語言為了適應某些輸入裝置上沒有的字元而設定的,比如??=表示"#"。
b>預處理規則定義得更加嚴格,有一條新規則就是相鄰的字串字面值會被自動連線在一起;eg:“ab”“cde” 等價於 “abcde”
整型升級與 尋常算數轉換:p22
ANSI C標準:
第6.2.1.1節 字元和整型(整形升級)
char,short int或者int型位段(bit-field),包括它們的有符號或無符號變型,以及列舉型別,可以使用在需要int或unsigned int的表示式中。
如果int可以完整地表示源型別的所有值,那麼該型別的值就轉換為int,否則轉換為unsigned int。此為整型升級。
第6.2.1.5節 尋常算數轉換
許多運算元型別為算術型別的 雙目運算子 會引發轉換,並以類似的方式產生結果型別。
-
浮點相關:
如果其中一個運算元型別是long double,那麼另一個運算元也被轉換為long double。其次如果一個運算元的型別是double,那麼另一個運算元也被轉換為double,再次,如果其中一個運算元的型別是float,那麼另一個運算元也被轉換為float。
-
整型相關:
否則兩個運算元進行整形升級,執行下面的規則:
如果其中一個運算元的型別是unsigned long int,那麼另一個運算元也被轉換成unsigned long int。其次,如果其中一個運算元型別是long int,而另一個運算元的型別是unsigned int,如果long int能夠完整表示unsigned int的所有值,那麼unsigned int 型別運算元運算元被轉換為long int,如果long int 不能完整表示unsigned int的所有值,那麼兩個運算元都被轉換為unsigned long int。再次,如果一個運算元的型別是long int,那麼另一個運算元被轉換為long int。再再次,如果一個運算元的型別是是unsigned int,那麼另一個運算元被轉換為unsigned int。如果所有以上情況都不屬於,那麼兩個運算元都為int。
採用通俗語言大意如下:
當執行算術運算時,運算元的型別如果不同,就會發生轉換。資料型別一般朝著浮點精度更高、長度更長的方向轉換,整型數如果轉換為signed不會丟失資訊,就轉換為signed,否則轉換為unsigned。
一個栗子:p24
int array[] = { 23, 34, 12, 17, 204, 99, 16 };
#define TOTAL_ELEMENTS (sizeof(array)/sizeof(array[0]))
int main()
{
int d = -1, x;
/*...*/
if (d <= TOTAL_ELEMENTS - 2)
{
x = array[d+1];
}
/*...*/
}
TOTAL_ELEMENTS所定義的值是unsigned int 型別(因為sizeof()的返回型別是無符號數)。
if語句在signed int和unsigned int 之間測試相等性,所以d被升級為unsigned int型別,
-1 轉換成unsigned int 的結果將是一個非常巨大的正整數。致使表示式的值為假。
修復這個bug:對TOTAL_ELEMENTS 強制型別轉換。
if (d < (int)TOTAL_ELEMENTS - 2)
對無符號型別的建議
- 儘量不要再你的程式碼中使用無符號型別,以免正價不必要的複雜性。尤其是,不要因為無符號數不存在負值(如年齡、國債)而用它來表示數量。
- 儘量使用像int那樣的有符號型別,這樣在涉及升級混合型別的複雜細節時,不必擔心邊界情況(如-1被翻譯為非常大的正數)
- 只有在使用位段和二進位制掩碼時,才可以用無符號數。應該在表示式中使用強制型別轉換,使運算元均為有符號數或者無符號數,這樣就不必由編譯器來選擇結果的型別。