作者:cggwz
來源:https://blog.csdn.net/cggwz/article/details/103740713?utm_medium=distribute.pc_relevant.none-task-blog-utm_term-2&spm=1001.2101.3001.4242
文章目錄
- 運算符相關
-
- 自增(減)前字尾
- 邏輯運算符的短路問題
- 指派運算符的傳回值問題
- 運算符優先級的問題
- sizeof()的傳回值問題
- 函數相關
-
- 函數聲明問題
- mian函數參數問題
- 變量相關
-
- 用字元串初始化字元數組
- 賦初值問題
- 轉義字元問題
- 常用ASCII碼記憶
- 字元串常量占用記憶體問題
- 字元變量值的問題
- 變量類型的範圍計算問題
- 字元串常量拼接問題
- 數組相關
-
- 數組下标越界
- 二維數組中行列計算問題
- 字元數組指派問題
- 結構體相關
-
- 結構體所包含的變量不可初始化
- 檢視結構體内部變量所占記憶體大小
- 庫函數相關
-
- 絕對值函數
- scanf()和printf()的傳回值問題
- fclose()的傳回值問題
- 預編譯相關
-
- 注意宏定義的替換原則
- 後記
現在不少大學都把C語言作為一門必學的程式設計語言。C語言考試呢,并不能決定你的實踐能力怎麼樣,他考的很多點,我們不知道,也可以在寫代碼時避免,我們舉個最簡單的例子,運算符優先級,這恐怕是必考的内容了。但實際上我們寫代碼并不需要背下這個,我們完全可以通過多加幾個括号解決問題。但是考試它并不會管這些,考試是别人出的題,是以代碼也是它的。而我們也需要一個較高的分數,那麼我們應該怎麼辦呢?
沒錯,這篇部落格就是為了這個而誕生的。從cggwz個人的刷題經曆,總結出易錯的一些點,提醒大家在考試中注意,那麼廢話不多說,我們開始吧!
(再強調一遍:這裡大部分是出卷人可能的設誤點,而不一定是你自己寫代碼的易錯點)
運算符相關
自增(減)前字尾
我們以自增為例,字首和字尾的差别有兩點:
- 優先級不同,字尾優先級更高
- 傳回值不同
雖說優先級不同,但是在兩者之間也就邏輯非和按位取反,貌似不會有太大影響。
而重點是傳回值,也就是說,
++x
傳回的是x+1,而
x++
傳回的是原來的x。
而x本身的值都會在傳回值以後立刻改變,也就是執行這個表達式後立即改變,比如:
#include
int main(){
int x=0,y=2,z=1;
x=z++ + z;
printf("%d",x);
return 0;
}
這個輸出結果就是3,而不是2.
邏輯運算符的短路問題
C語言在計算邏輯運算符的時候是采取短路機制的,也就是說,如果已經可以判斷這個表達式的最終結果,那麼就不再判斷接下來的表達式,相應地,那些表達式也不會被執行。而出題人則喜歡通過放置一些指派語句在後面,某些學生會因為忽略了短路機制,而誤以為會執行,進而出錯,我們看個例子:
這裡輸出是2嗎?
并不是,是3.
因為
--z
傳回的值是0,而後面的邏輯運算符是&&,也就是說無論後面一個語句真假與否,這個表達式都是假,是以就會觸發短路機制,不再計算後面的
++x
,進而x的值不會改變。
我們來看個變式:
相信你現在應該能知道結果了,輸出就是2.中間的那個–x并不會因為它在中間而被執行。
指派運算符的傳回值問題
我們知道C語言中的指派運算符是有傳回值的。
它們的傳回值是指派後被指派變量的值,而不是指派符号右邊的表達式的值。
比如
int a=10;a+=3;
這裡的+=傳回的就不是3,而是13.
這也适用于類型轉換問題,比如
double a;a=2;
這裡這個指派語句的傳回值就是2.0,而不是2.
運算符優先級的問題
考試前必須要掌握的内容之一。
但是顯然,我這裡不會把一大張表放出來,要是需要看看課本就好了。
這裡我是想總結一下這張表,讓大家更容易的“記住”這張表。
我們也會順帶地總結一下結合方向。
現在請翻開你的教材或是百度找到一張表,我們準備開車了。
首先第一梯隊,優先級最高的,是一些指代所屬關系的運算符,比如[]、->、.。在加上一個圓括号。這個應該很好了解。圓括号如同一個老大哥,它在改變運算順序這方面具有絕對的話語權,自然會在第一梯隊,而這個老大哥也是懂人情的,->、.這類符号就好像是調用父對象裡的一個子對象,把人家父子關系拆散恐怕不是一個老大哥應該做的,是以它們也屬于第一梯隊。
它們的優先級是最高的。
接下來第二梯隊主要是我們的單目運算符。比如正負号、指針符号、取位址、自加自減之類。它們通常是緊緊貼着它們作用的對象的(單目),那關系親密地如同情侶。雖然狗糧不好吃,但是不能因為嫉妒就拆散人家呀(誰說的,我就覺得可以),是以它們的優先級也是比較高的,屬于第二梯隊。
第三梯隊就是我們的四大金剛啦——加減乘除,哦不,我們不能忘了我們除法的好兄弟——取餘。是以它們五個就是我們的第三梯隊,我們可以把它們叫做算術運算符。這五大金剛可是從國小就開始陪伴我們了,自然在考慮完父子情、情侶情之後要考慮它們幾個了。
第四梯隊是位運算的左移和右移運算符。這兩個家夥雖然名氣沒有上面五大金剛大,但是它們也是可以改變變量的值呢,也可以算得上小金剛了。是以,第四梯隊,當之無愧。
第五梯隊是關系運算符,也就是表示大于小于不等這類符号。它們雖然也很早就開始陪伴我們,名氣也不小,但是畢竟是比較運算的結果,自然要等前兩個梯隊運算完,才能比較,那就隻好屈居它們後面啦。
第六梯隊是位運算的按位與、或、異或。理論上它們也是改變數值的呀,為什麼不在前面的梯隊呢?我們可以看看它們的樣子:&、|,正好是我們下面一個梯隊的一半,二者的關系情同手足,成功固然美好,但是一份兄弟情,哪是成功可以換來的?它們自然願意留在兄弟身旁,相依相助。
第七梯隊是邏輯運算符,加上唯一一個三目運算符——條件運算符。它們連接配接的多半是關系運算符的結果,自然就得在關系運算符後面啦!
第八梯隊可是一個大部隊,指派運算符。它們當然得殿後啦,它們可是把前面的運算結果儲存下來的必經之路。它們就如同慈愛的父母,默默地收拾前面這些孩子算出來的各種資料,并把它們認真地儲存下來。我們的父母在我們小時候,不也是這樣的嗎?
咦?我們好像還漏一個,沒錯,逗号運算符。存在感太低啦!就放在最後一個吧!(我想你們大部分人也很少用它)。
這樣我們的優先級就徹底理清楚啦!
總結一下:
分量、下标運算符、圓括号>單目運算符>算數運算符(五大金剛加兩個小金剛)>關系運算符>位運算符(與或異或)>邏輯運算符(含條件運算符)>指派運算符>逗号運算符
這個總結并不嚴謹,因為有些運算符并不是在一起的,是以要注意對我說的故事的了解,再結合表看看背背就OK了。
下面再說一下結合方向。
這個還是很好總結的,除了字尾自加自減的所有單目運算符、條件運算符、指派運算符是右結合,其他的符号全是左結合。
這個還是很好了解的,那些單目運算符通常寫在作用對象的左邊,自然是從右向左結合,指派符号也是把右邊的數值賦給左邊,是以也是從右向左,條件運算符……這麼特殊的一個,記一下就好了。
這樣我們就成功地解決了這張令人頭疼的表。怎麼樣?不困難吧?
sizeof()的傳回值問題
首先強調一下,這個不是函數,雖然它看起來很像。
它運算得到的值是括号内的東西所占的記憶體大小,它是一個int型的值。是以sizeof()的表達式是int型表達式。
函數相關
函數聲明問題
我們知道C語言裡是需要函數聲明的,如果函數放在主調函數的後面,就需要在主調函數前進行原型聲明。
但是不知道有沒有像我一樣,由于長期把函數放在主調函數的前面(少打幾行字),而在考試時容易忘記聲明這件事。其實網上有不少資訊學競賽題的代碼,它們絕大多數都是把函數放在主調函數前面,是以這裡也算是一個提醒吧。
mian函數參數問題
我們知道main函數有三個參數argc,argv,envp
我們主要提示一下前面兩個。
現在如果我們在指令行執行如下語句:
D:\cgg.exe how are you!
這麼執行後,argv是4,而不是3,因為
D:\cgg.exe
也是一個參數,而它就儲存在argv[0]中。
這一點千萬不能忘記。
變量相關
用字元串初始化字元數組
主要是長度的問題,我們知道C中的字元串是以一個空字元作為字元串的結尾标志。是以,我們在指派時,要注意字元數組的長度要比字元串的長度多1。比如下面這段代碼是無法通過編譯的:
#include
int main(){
char a[4]="abcd";
return 0;
}
而我們隻需要把a的長度改為5就好了。
賦初值問題
這是一個老生常談的問題,但是還是很容易被忽略。是以在這裡再說一下。當遇到一些計算求值類的題,一定要注意把變量初始化,賦初值,比如0.
轉義字元問題
我們知道C語言中是有轉義字元的。轉義字元以\開頭,是以,我們是不能在一對單引号中間直接放置右斜杠的,還有就是注意,斜杠後如果是十六進制的表示碼,開頭是x或X,而不是0x或0X
常用ASCII碼記憶
常用的無非是大小寫字母和數字,這時一定需要記住的(除非你是上機考試)。
字元 | ASCII值 |
---|---|
A | 65 |
a | 97 |
48 | |
CR(回車) | 13 |
空格 | 32 |
字元串常量占用記憶體問題
我們知道C中的字元串一定是以空字元‘\0’結尾,而這個空字元是不顯示的。是以有的出題老師會在這裡設坑,比如:"abcde"占的記憶體大小是多少位元組?雖然是五個字元,但是實際上還有一個空字元,總共是6位元組。
字元變量值的問題
我們經常會遇到這樣的題,問你某個字元的值是什麼。
這時我們需要尤其小心,尤其是選擇題。
我們舉個例子來說:
char a='C'
a的值是什麼?
是C嗎?
如果你說是,那就錯了。
我們隻能說a的值是‘C’或者67.
想必你應該能明白我想說什麼了。
單引号是不能省去的,這一點尤其重要,缺少了單引号,它就不再是一個字元了。
這一點很容易忽略,尤其是選擇題,看到C這個選項,一激動就選上了。那就慘了。
變量類型的範圍計算問題
這裡隻提醒一點,計算範圍的時候不要忘記對于有符号的數,有一位符号位。
字元串常量拼接問題
在C語言中是允許進行字元串拼接的。
也就是說,如果兩對引号包含的字元串放在一起,則會被解釋成一個字元串。
比如:
#include
int main(){
char a[15]="hello""world";
char b[15]="hel""lo""world";
printf("%s",b);
return 0;
}
這兩個字元串都是合法的,且和“helloworld”等價。
數組相關
數組下标越界
這個……恐怕是個程式員都會出過的bug,從你初學程式設計語言到你退休。
考試也可能會出現這個問題,多半是在for循環裡出現。
二維數組中行列計算問題
我們可能會經常看到這樣的題,就是另一個指針等于二維數組中的某個位址單元,然後對指針進行加一之類的操作,然後問輸出的值。
而我們知道這裡會有兩種不同的效果,一種是行計算,一種是列計算,二者的含義是,行計算,每次加一,位址會移動一行;而列計算,每次加一,位址隻會移動一格。
比如:
這裡輸出的就是第二個字元串I
具體的分類為:
類型 | 計算方式 |
---|---|
a | 計算行 |
&a[0][0] | 計算列 |
a[0] | 計算列 |
*a | 計算列 |
字元數組指派問題
我們知道我們可以這樣聲明一個字元數組:
但是,字元數組在被定義之後,是不被允許這樣直接指派的。
比如下面這段代碼是不會通過編譯的:
a="helloworld!";
結構體相關
結構體所包含的變量不可初始化
這一點在C++中是被允許的,但是C中是不允許的。
是以習慣了寫C++的同學尤其需要注意。
不要忘記了。
檢視結構體内部變量所占記憶體大小
這裡我們隻舉個例子,比如我們有ABC這個結構體,它有變量x,
則
sizeof(((struct ABC*)0)->x)
得到的就是x占用的記憶體大小。
這裡的0,隻要是int型的量就可以。
庫函數相關
絕對值函數
注意math.h中有兩個絕對值函數,一個是abs(),另一個是fabs()
其中前者是處理整型變量的,而後者是處理浮點型變量的
scanf()和printf()的傳回值問題
我們通常會把它們當作輸入輸出語句使用,單獨成行,總是自動忽略它們的傳回值,但其實它們是有傳回值的。
那麼它們的傳回值是什麼呢?
printf的傳回值是輸出的資料個數,而scanf的傳回值則是讀入的資料個數。
我們可以來看個例題:
#include
int main(){
int x;
printf("2:%d",printf("1:%d,",scanf("%d",&x)));
return 0;
}
請問它的輸出結果是什麼?
答案是
1:1,2:4
為什麼是4?不知道你是不是想這麼問。
printf中所說的資料個數,其實是把輸出的每個字元都看成一個單獨的資料,也就是有多少字元加上我們傳入的資料,就是它的傳回值。
fclose()的傳回值問題
和scanf、printf一樣,我們經常忽略fclose的傳回值,其實他也是有傳回值的。
如果成功關閉流,那麼傳回0,如果未成功關閉則傳回-1。
預編譯相關
注意宏定義的替換原則
我們知道宏定義實際上隻是字元串的替換,并不是函數一樣的東西,是以我們在做題時也需要嚴格替換,替換完成之前,不要進行任何計算。
我們舉個例子:
答案是多少?54?那就錯了。實際上應該是48.
這裡我們不能把5+1算出來6再帶入,而是直接代入,這樣你就會發現,5和1不是直接加在一起的。
免責聲明:本文系網絡轉載,版權歸原作者所有。如涉及作品版權問題,請我聯系,感謝!
往期推薦好文
★C語言程式設計工具的選擇
★求1000以内的水仙花數
★i++和++i有什麼差別?
★「建議收藏」C語言程式設計判斷回文數
★看完你就明白:什麼情況下該用int main(int argc, char *argv[])而不是int main(void)
★C語言程式設計:列印一個圓形
★福利:50本C語言電子書免費送了!
★C語言程式設計求解:1到1000之間所有的素數
★[必讀]C語言學習者常見問題及回答