關於《C和指標》的學習筆記

來源:互聯網
上載者:User

  有了之前的基礎,此文只是把一些以前沒有注意到的和值得學習的知識做一個記錄。

第一章

  作者認為使用#if 0 .... #endif比用/*和*/好,因為後者不能嵌套。但是對於//並沒有說明。

第二章  

  三字母詞,用兩個問號加一個符號表示另一個符號,比較類似於逸出字元。查閱了一些資料,它的使用與編譯器有關,瞭解即可,防止字串常量被錯誤的解釋。

複製代碼 代碼如下:??( ==> [   ??< ==> {   ??= ==> #

??) ==> ]   ??> ==> }   ??/ ==> \

??! ==> |   ??' ==> ^   ??- ==> ~

  對於嵌套較深的函數,作者建議把它分成幾個函數來實現,不至於使用Tab縮排過多。

第三章  資料

  對於static的複雜用法,當它用於函數定義或代碼塊之外的變數聲明時,static用於修改標識符的串連屬性,從external改為internal,但標識符的儲存類型和範圍不受影響。用這種方式聲明的函數或變數只能在聲明它們的源檔案中訪問。當它用於代碼塊內部的變數聲明時,static用於修改變數的儲存類型,從自動變數修改為靜態變數,但變數的連結屬性和範圍不受影響。用這種方式聲明的變數在程式執行之前建立,並在程式的整個執行期間一直存在,而不是每次在代碼塊開始執行時建立,在代碼塊執行完畢後銷毀。

第五章  操作符和運算式

  移位操作,當移動的位元為負值時,具體的結果與編譯器有關或者是未定義的,比如a<<-5可能是左移27位。

  形如a+=1的操作效率比a=a+1高,等價的a[ 2 * (y - 6*f(x)) ]=a[ 2 * (y- 6*f(x))] + 1與a[ 2 * (y- 6*f(x))] += 1相比,後者不用重複計算下標。

  sizeof x的形式是允許的。sizeof()並不對錶達式求值,因此sizeof(a=b+1)中的a沒有賦值。

  訪問指向結構的指標的成員時只用->。

第六章  指標

  未初始化的指標會導致錯誤。int *a; *a = 12,這使a指向的地址的內容被修改,結果是無法預料的。

  作者認為在諸如搜尋元素而未找到時傳回值為NULL指標雖然是C的常用技巧,但違背了軟體工程的原則:“用一個單一的值表示兩種不同的意思是件危險的事,因為將來很容易無法弄清哪個才是它的真正用意”。安全的策略是返回兩個值,表示是否成功的狀態值和尋找成功時所尋找到的元素值。

第七章  函數

  無參數的函數原型聲明應該寫作這樣:int func(void); 目的是不與舊式風格聲明混淆。

  遞迴解決問題比非遞迴更為清晰,對於一個複雜問題,難以用迭代形式實現時,遞迴實現的簡潔性可以補償它所帶來的開銷。Fibonacci是一個常見的遞迴的例子,但冗餘計算很多,開銷太大,實際上並不如迭代實現。

複製代碼 代碼如下:Fibonacci的迭代實現

long fibonacci(int n)
{
long result;
long previous_result;
long next_older_result;

result = previous_result = 1;

while( n > 2) {
n -= 1;
next_older_result = previous_result;
previous_result = result;
result = previous_result + next_older_result;
}
return result;
}

  可變參數列表的使用:標頭檔stdarg.h,其中聲明了一個類型va_list和三個宏va_start、va_arg、va_end。通過聲明va_list類型的變數與這幾個宏配合使用,訪問參數的值。函式宣告了一個var_arg的變數用於訪問參數列表未確定部分,它通過va_start初始化。第1個參數是va_list變數的名字,第2個參數是省略符號前最後一個有名字的參數。初始化過程把var_arg變數指向可變參數部分第1個參數。va_arg接受兩個參數:va_list和參數列表的下一個參數的類型。va_arg返回參數的值並使var_arg指向下一個可變參數。訪問完畢調用va_end。複製代碼 代碼如下:#include <stdarg.h>

float average( int n_values, ... )
{
va_list var_arg;
int count;
float sum = 0;

/*準備訪問可變參數*/
va_start( var_arg, n_values) ;

/*添加取自可變參數列表的值*/
for (count = 0; count < n_values; count += 1) {
sum += va_arg( var_arg, int);
}

/*完成處理可變參數*/
va_end(var_arg);
return sum/n_values;
}

  可變參數的宏並不能判斷參數數量和參數類型,而後者可能會造成預設參數類型的提升。解決這兩個問題的方法是使用具名引數,也就是可變參數列表中總有一個有名字的參數的原因。

第八章  數組

  int array[10];int *ap =array+2;在這之後,ap[0]在C裡是合法的,它等同於array[2],ap[-1]同樣是合法的,即array[1]。

  指標比數組更有效率的場合:for迴圈的ap++比迴圈體中的array[a] = 0有效率,前者的乘法計算只有一次,用於1與資料類型長度相乘,而後者每次都需要進行計算。

複製代碼 代碼如下:/* 使用數組 */
int array[10], a;
for ( a = 0 ; a< 10; a +=1 )
array[a] = 0;

/* 使用指標 */
int array[10], *ap;
for ( ap = array ; ap< array + 10; ap ++ )
*ap = 0;

  數組特別是龐大的數組的初始化時間可能非常可觀,因此當數組的初始化局部於一個函數或代碼塊時,應當考慮程式每次都對其進行重新初始化是否值得。若否,把數組聲明為static。

  使用指標訪問多維陣列的方法,例如對於數組int matrix[3][10],聲明int *mp = matrix是錯誤的,因為matrix並非一個指向整型的指標,而是一個指向整型數組的指標。int (*p)[10] = matrix是可以的,p指向matrix第一行,實現對數組的逐行訪問。如果需要逐個訪問,則使用int *pi = &matrix[0][0]或int *pi = matrix[0],使它指向第一個元素。而 int (*p)[] = matrix;是不正確的,它的值根據空數組的長度調整,這一錯誤有的編譯器不能捕捉到。函數傳參數類似。

  多維陣列顯式初始化,只有第一維能夠推算出,其他維不能省略。

第九章  字串、字元和位元組

  無符號數的謹慎使用:strlen返回無符號數,因此if(strlen(x) - strlen(y)>=0) ...永遠是真。這種情況下應該寫為if(strlen(x)>=strlen(y)) ...或者採用強制類型轉換把其轉為int。

  strtok儲存它所處理的函數的局部狀態資訊,因此不能用它同時解析兩個字串。

  字串函數遇到NULL位元組結束操作,想要處理非字串資料時不受到這個限制,可以使用另一組相關的函數:memcpy、memmove、memcmp、memchr、memset。

第十章  結構和聯合

  參數為結構的函數,傳遞指標比傳值調用更高效,這是因為後者需要建立一份結構的拷貝。f(type_struct *s){s->x};調用即為f(&s)。如果對這個結構的成員訪問次數超過3次,聲明為寄存器變數會更加有效。為了避免不適當的修改,可以把參數聲明為const,將傳回值賦給原結構(或它的一個成員)。

  位段只是進行了簡單的瞭解,它是一種指定了成員長度的特殊結構。

第十三章  進階指標話題

  回呼函數的使用可以解決類似於比較不明類型資料的問題,這裡也是第一次系統地認識回呼函數。

第十四章  前置處理器

  消除多重包含的危險的方法,在每個標頭檔寫入以下內容:

複製代碼 代碼如下:#ifndef _HEADRNAME_H
#define _HEADRNAME_H 1
/* All the stuff that you want in the header file*/
#endif

第一次被包含時將_HEADRNAME_H定義為1,再次被包含時將被忽略。即使把它寫做#define _HEADRNAME_H都是可以的。但是仍應該儘可能避免多重包含。

第十五章  輸入\輸出函數

  freopen用於開啟(或重新開啟)一個特定的檔案流,原型:FILE *freopen(char const *filename,char const *mode, FILE *stream),其中最後一個參數就是需要開啟的流。它首先試圖關閉這個流,然後用指定的檔案和模式重新開啟這個流,失敗返回NULL,成功返回第三個參數。

  ungetc把先前讀入的字元返回到流中,使它可以以後被重新讀入。《C程式設計語言》上有一個字元處理的例子用到了它,在此複習一下。當fseek、fsetpos或rewind改變流的位置時,所有退回的字元都將被丟棄。

  gets和puts與fgets和fputs的區別在於,gets讀取一行輸入時,並不在緩衝區儲存結尾的分行符號,puts寫入一個字串時,它在字串寫入後向輸出再添加一個分行符號。另外gets不判斷緩衝區長度,而這會造成危險。

  feof判斷流是否處於檔案尾,ferror報告流的錯誤狀態,clearerr對指定流的錯誤標誌進行重設。

  tmpfile建立一個臨時檔案儲存資料,程式結束時就被刪除。臨時檔案的名字由tmpnam建立。

第十六章  標準函數庫

  volatile是類型修飾符,被設計用來修飾被不同線程訪問和修改的變數,阻止編譯器以一種可能修改程式含義的方式“最佳化”程式。

  vprintf、vfprintf、vsprintf用於列印可變參數列表,功能類似於對應的prinft等函數,但參數是一個可變參數列表arg。

  getenv擷取環境變數,如果找到就用指標返回,否則返回NULL。

  locale是一組特定的參數,每個國家可能不同,目的是增強C的世界範圍內的通用性,不詳細記述。

  對於十七章經典抽象資料類型和第十八章運行時環境,前者已經比較熟悉,後者與彙編結合緊密,只是粗略瀏覽了一下,這本書姑且算是看完了。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.