日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

《C Traps and Pitfalls》 笔记

發布時間:2023/12/10 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 《C Traps and Pitfalls》 笔记 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
這本書短短的100多頁,很象是一篇文章。但是指出的很多問題的確容易出現在筆試的改錯題中

--------------------------------------------------------------------
第1章 詞法陷阱

1.1 = 和 ==

1.3 詞法分析的"貪心法則"
編譯器從左到右讀入字符,每個符號包含盡可能多的字符,直到不是字符為止

如:
a---b? 等價于? a-- - b

a/*b?? 并不是 a/(*b), 而是/*當作注釋符


1.4 整型常量

0開頭的整數為8進制

如:
014? 為8進制, 不要誤看作10進制

1.5 字符和字符串

單引號 - ASCII字符,實際上是一個整數
雙引號 - 指向匿名的字符數組起始字符的指針,該數組以引用的字符和額外的'\0'初始化

另外,大多數編譯器允許一個字符常量中包含多個字符。Borland C++中只取第一個字符,而VC 6.0和GCC中則后面的字符依次覆蓋前面的字符,最后得到的是最后一個字符。

如:(GCC 3.5)
char ch='yes';
cout << ch;//output: s, but with warning

練習題:
1-1。某些C編譯器允許嵌套注釋。請寫一個測試程序,要求:無論對是否允許嵌套注釋的編譯器,該程序都能正常通過編譯(無錯誤消息),但是這兩種情況下程序執行的結果卻不同。

1-3. n-->0的含義?
(n--) > 0???? 貪心法則

1-4 a+++++b的含義?
((a++) ++) + b
但是值得一提的是:現代編譯器中,此式子是非法的

為什么?
你可以查看operator++(int)的原型: const int operator++(int)
返回的為const型,且為a的臨時拷貝。之所以返回為const型,就是為了將返回作為左值,也就防止出現這類式子
總結:a++不能做左值

---------------------------------------------------------------------------------------
第2章 語法陷阱

2.1 理解函數聲明
換個角度理解聲明語句(declaration)
聲明語句構成:類型 + 一組類似表達式的聲明符(declarator)

float f, g; //表達式f, g求值為浮點數,即f, g為浮點型
float ff(); //表達式ff()求值是一個浮點數,即ff是一個返回類型為浮點數的函數
float *pf; //*pf求值是一個浮點數,即pf是指向浮點型的指針
更復雜的,
float *g(), (*h)();
依據上述并且()優先級大于*,很容易知道g是一個函數,返回類型為浮點指針(float *)
h為一個函數指針,函數返回類型為float
(float (*h)()) 是一個類型轉換符

再看:
(*(void(*)())0)()
實質上是:
void (*fp)(); //declare a function pointer
typedef void (*fp_type)();// for simplifying, otherwise always need void(*)()
conv_0 = (fp_type)0; // converse function 0 into function?conv_o
(*conv_0)();//using the conversed function

再讓我們看看<signal.h>中聲明的signal函數
void (*signal(int, void(*)(int)))(int)
首先,用typedef簡化,
typedef void (*handler_type)(int)
得,void (*signal(int, handler_type))(int)
進一步 handler_type signal(int, handler_type);

2.3 作為語句結束的分號
1)多寫了分號
if(x[i] > big);
?? big = x[i]
這還是很容易辨識
再看
2)漏寫了分號
if(n?<?3)
????
return
logrec
.date?=?x[0];
logrec
.time?=?x[1];
logrec
.code?=?x[2]; 看出問題來了沒?

繼續看下面一個經典的:
struct?logrec
{
????
int?date;
????
int?time;
????
int?code;
}

main()
{
????
//
}
注:這個問題筆試題已經出現過

2.4 swith語句
這個估計是老生常談了

也就是case后的break有無的問題了

首先要搞清楚一件事:你可以把(case:)當作語句的標號,就好像匯編中的標號一樣。switch之后徑直跳到匹配的case處順序執行下去,以后再碰到case則無視

當然,程序設計中有意不要break的除外

2.6 “空懸”else引發的問題

看下面代碼:
if(x?==?0)
????
if(y?==?0)?error();
else{
????z?
=?x?+?y;
????f(
&z);
}
這段代碼可能與你的本意大相徑庭,因為else與最近的if匹配

防止這類問題很簡單,只要每次使用if,else都用{}

---------------------------------------------------------
第3章 語義陷阱

3.1 指針和數組

C語言數組需要注意:
1)C語言只有一維數組,數組大小必須在編譯期確定為常數。二維數組是通過數組元素也為數組的一維數組實現
2)對于一個數組,只能做2件事情:確定數組大小,取得指向數組首元素的指針。其他相關操作,如下標運算, 都是通過指針進行

int a[3]; //數組元素為int型
struct
{
??? int p[4];
??? double x;
}b[17]; //數組元素為結構體
int calendar[12][31];
//12個元素的數組,每個元素又是31元素的數組
//并非:31個元素的數組,每個元素是12個元素的數組


記住:數組名是指向該數組首元素的指針
如,int a[11];? //那么a的類型為 (int *)
int *ptr;
ptr = a;
但是ptr = &a;是非法的,這里&a的類型為int (*)[],即指向數組的指針,大多數編譯期對這種操作,或者視為非法,或者讓其等于a

在C中,a+i和i+a的含義是一樣的,但后者不推薦

下面看多維數組:
int calendar[12][31];
int *p;
int i;

我們很容易知道calendar[4]表示什么含義:calendar[4]表示calendar數組的第5個元素,是12個有31個元素的數組之一。
sizeof(calendar[4])結果為31×sizeof(int)

此例中,calendar名字轉換為一個指向數組的指針,其類型為int (*)[31]
于是p=calendar; 是非法的

int (*monthp)[31];
monthp = calendar;//OK


calendar[month][day] = 0;
等價于
*(*(calendar+month)+day) = 0;
怎樣分析這個呢?
首先,calendar+month是指向12個元素之一的指針,對其解引用得到就是其元素(而元素是數組),所以*(calendar+month)是指向含31個元素的數組首元素的指針,再偏移然后解引用即得到最終的int型元素

總結:
1)數組名表示指向首元素的指針,類型為元素類型的指針
2) 對數組名取地址,為指向數組的指針,類型為數組的指針

3.2 非數組的指針 - 字符串

字符串常量:代表一塊包含字符串中所有字符加上額外一個空字符('\0')的內存區的地址。

一般字符串常量用字符數組保存的,且是只讀的。

字符串操作函數:
size_t strlen(char *);//計算字符串長度,直到遇到'\0'.且不包括'\0'
int strcpy(char * dest, const char *src);
int strcat(char *dest, char *src);

注意其中的輸出參數dest必須是預先分配好,且有足夠的空間能容納


3.3 數組作為函數參數

自動轉換成指針

3.6 邊界計算與不對稱邊界

這個主題值得探討

3.7 求值順序

C中只有四個運算符(&&, ||, ? :和,)規定了求值順序,對于其他運算符不要錯誤的假設求值順序,他們求值順序是未定義的。

如:
i = 0;
while(i < n)
??? y[i] = x[i++];

這里y[i]的地址在i自增前被求值是沒有任何保證的


3.9 整數溢出

C語言中存在2類整數算術運算:有符號運算與無符號運算。

兩個無符號數運算不存在溢出。

算術運算中一個是有符號數,另一個是無符號數,則有符號數會轉換為無符號數,運算時溢出也不可能發生。

兩個有符號數運算,溢出有可能發生。并且溢出發生時,溢出結果是未定義的。

那么如何檢測是否發生溢出呢?
看下面的方式:
int a, b;
if(a + b < 0)
??? //do something

這種方式是不可靠的,因為對溢出結果做的任何假設都是不可靠的

正確的方式:
#include <limits.h>
int a, b;
if((unsigned)a + (unsigned)b > INT_MAX)
??? //...
或者
if(a > INT_MAX - b)
??? //...

--------------------------------------------------------------------------------------
第4章 連接

4.2 聲明與定義

下面聲明語句:
int a;
如果出現在所有函數體(包括main函數)之外, 它被成為外部對象a的定義,并且其初始值默認為0

下面聲明語句:
int a = 7;
定義a的同時指定了初始值

下面聲明語句:
extern int a;
并不是a的定義,說明a是一個外部整型變量,它的存儲空間在程序的其他地方分配

典型情況:
//file1.c
int a = 7;

//file2.c
int a = 9;

這種情況一般在連接時會報錯,因為定義只能一次,聲明卻可以很多

4.3 命名沖突與static修飾符

static將變量或函數的作用域限定在一個源文件中了

4.5 檢查外部變量

轉載于:https://www.cnblogs.com/chio/archive/2007/10/26/938064.html

總結

以上是生活随笔為你收集整理的《C Traps and Pitfalls》 笔记的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。