1. 程式人生 > >讀書筆記===C專家程式設計[1]

讀書筆記===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)

對無符號型別的建議

  1. 儘量不要再你的程式碼中使用無符號型別,以免正價不必要的複雜性。尤其是,不要因為無符號數不存在負值(如年齡、國債)而用它來表示數量。
  2. 儘量使用像int那樣的有符號型別,這樣在涉及升級混合型別的複雜細節時,不必擔心邊界情況(如-1被翻譯為非常大的正數)
  3. 只有在使用位段和二進位制掩碼時,才可以用無符號數。應該在表示式中使用強制型別轉換,使運算元均為有符號數或者無符號數,這樣就不必由編譯器來選擇結果的型別。