程序員都不知道C語言中的這些小細節(jié)
既然題目都說了是小細節(jié),一來就介紹細節(jié)多沒意思啊,先坑坑大家再詳細介紹吧,嘿嘿.直接上7個題吧,看看你能做對幾個呢?
計算型細節(jié)
①:
#include <stdio.h> int main() { char a = 3; char b = 127; char c = a + b; printf("結(jié)果是:%d",c); return 0; }
您想想這個題的答案是多少?先不要看后面的答案哦
答案是 -126, 嘿嘿,是不是答錯了呢?先不要著急,繼續(xù)看下面的題
②:
#include <stdio.h> int main() { char a = 0xb6; short b = 0xb600; int c = 0xb6000000; if(a==0xb6) printf("a"); if(b==0xb600) printf("b"); if(c==0xb6000000) printf("c"); return 0; }
您想想這個題的答案是什么呢? 先不要看后面的答案哦
答案是 c,嘿嘿,是不是又回答錯誤,先不要著急,再看看后面的題
③:
#include <stdio.h> int main() { char c = 1; printf("%u\n", sizeof(c)); printf("%u\n", sizeof(+c)); printf("%u\n", sizeof(-c)); return 0; }
您想想這個題的答案是什么呢? 先不要看后面的答案哦
答案是1 4 4,嘿嘿,是不是又回答錯誤,先不要著急,再看看后面的題
表達式細節(jié)
①:
#include <stdio.h> int main() { int c = 3; int ret = c + --c; printf("%d",ret); return 0; }
您想想這個題的答案是什么呢? 先不要看后面的答案哦
答案是 5 或者 4,是不確定
的,嘿嘿,是不是又回答錯誤,先不要著急,再看看后
面的題
②:
int main() { int i = 10; i = i-- - --i * ( i = -3 ) * i++ + ++i; printf("i = %d\n", i); return 0; }
您想想這個題的答案是什么呢? 先不要看后面的答案哦
答案有很多個,在不同的編譯器結(jié)果不同,也就是說還是不能確定結(jié)果
,嘿嘿,是不是仍然回答錯誤了,先不要著急,再看看后面的題
③:
#include <stdio.h> int fun() { static int count = 1; return ++count; } int main() { int answer; answer = fun() - fun() * fun(); printf( "%d\n", answer);//輸出多少? return 0; }
您想想這個題的答案是什么呢? 先不要看后面的答案哦
答案是不同編譯器不同結(jié)果
,嘿嘿,是不是又回答錯誤,先不要著急,再看看后面的題
④:
#include <stdio.h> int main() { int i = 1; int ret = (++i) + (++i) + (++i); printf("%d\n", ret); printf("%d\n", i); return 0; }
這是最后一個題了,你目前答對了幾道了呢??請在評論回答試試.嘿嘿
這個題的答案是不同編譯器不同結(jié)果
大家回答對了幾道題?歡迎評論
1.現(xiàn)在正式講解上面所有的題設(shè)計到的內(nèi)容--------表達式求值
表達式求值的順序一部分是由操作符的優(yōu)先級和結(jié)合性決定。
同樣,有些表達式的操作數(shù)在求值的過程中可能需要轉(zhuǎn)換為其他類型。
1.1隱式類型轉(zhuǎn)換 (整型截斷與提升)
什么是隱式類型轉(zhuǎn)換,整型提升,整型截斷?
C的整型算術(shù)運算總是
至少以滿足整型類型的精度
來進行的。
為了獲得這個精度,表達式中若有char
和short
類型必須在使用之前轉(zhuǎn)換稱為整型,這個過程叫做整型提升
.
一個較大數(shù)據(jù)類型存儲在較小數(shù)據(jù)類型中的過程叫做整型截斷
,比如整型a = 500
,但是a把他的值放到了字符型b
中,b不能完全存放a,就會發(fā)生整型截斷
.
而這個轉(zhuǎn)換行為叫做隱式類型轉(zhuǎn)換
1.1.1 第一題講解
#include <stdio.h> int main() { char a = 3; char b = 127; char c = a + b; printf("結(jié)果是:%d",c); return 0; }
我們知道計算機中一切都是操作的補碼.所以這里也是一樣,(不明白原碼補碼反碼的自己去百度下哦).
其中 正數(shù)的 原碼 反碼 補碼 一模一樣
… …負數(shù)的 補碼是反碼加一,反碼是原碼除了符號位所有位都按位取反
a只有1個字節(jié),他的補碼是00000011
(二進制)
b只有1個字節(jié),他的補碼是01111111
(二進制)
a加b,進行了算術(shù)操作,需要提升到4個字節(jié).而整型提升有兩種方式
- 算術(shù)提升:在最前面補符號位數(shù)字,直到32位
- 邏輯提升:無論什么都只補0,直到32位
大多數(shù)計算器都是進行的算術(shù)提升
,這里便講解算術(shù)提升
.
a算術(shù)提升后就是00000000000000000000000000000011
b算術(shù)提升后就是00000000000000000000000001111111
a+b的結(jié)果是------00000000000000000000000010000010
存進c里面,c又只有1個字節(jié),所以發(fā)生整型截斷,只存取最后8位
10000010
此時c存取的是補碼,且c是有符號型,記住了!!
,打印顯示時候會還原位原碼的,該補碼對應(yīng)的數(shù)字就是 -126
下面有張字符類型原碼補碼反碼
的對應(yīng)理解圖
所以有符號的范圍是 [-128,127]
無符號的范圍是[0,255]
變成圓圈更好理解,自己按照有無符號對應(yīng)去找就行.
1.1.2 第二題講解
#include <stdio.h> int main() { char a = 0xb6; short b = 0xb600; int c = 0xb6000000; if(a==0xb6) printf("a"); if(b==0xb600) printf("b"); if(c==0xb6000000) printf("c"); return 0; }
0xb6的二進制是 00000000000000000000000010110110
存進去a中,發(fā)生截斷,得到1011 0110
0xb600的二進制是00000000 00000000 10110110 00000000
存進去b中發(fā)生截斷,得到10110110 00000000
0xb600000010110110 00000000 00000000 00000000
存進去c中剛剛好.
a此時存的是補碼,a又與0xb6比較,參與了運算,所以又整型提升為
11111111 11111111 11111111 10110110
,
整型提升后的數(shù)字與0xb6的二進制不一樣,所以不等.
b此時存的是補碼,b又與0xb600比較,參與了運算,所以又整型提升為
11111111 11111111 10110110 00000000
整型提升后的數(shù)字與0xb600的二進制不一樣,所以不等
c此時存的是10110110 00000000 00000000 00000000
,注意哦~,有人可能會問0xb6000000
是正數(shù)啊,不!!!!!
,字面常量只要不帶符號就是默認int類型.所以此時0xb6000000
的整數(shù)值是超過int的,就會認為它此時的二進制是負數(shù)的補碼.所以c與0xb6000000
是相等的.
1.1.3 第三題講解
#include <stdio.h> int main() { char c = 1; printf("%u\n", sizeof(c)); printf("%u\n", sizeof(+c)); printf("%u\n", sizeof(-c)); return 0; }
sizeof
測量的是類型屬性的值.
c
的類型是char
,只有一個字節(jié),所以答案是1
+c
,+
是單目操作符,與c在一起發(fā)生了整型提升,變成了int,所以是4個字節(jié),答案是 4
-c
,同樣的道理,仍然是4
1.2算術(shù)轉(zhuǎn)換
上面我們說到,如果操作數(shù)大小 小于int,會發(fā)生整型提升,但是如果都是大于等于int大小的類型參與算術(shù)運算呢?? 這里就會涉及到
算術(shù)轉(zhuǎn)換
.
什么是算術(shù)轉(zhuǎn)換呢? 比如下面的例子:
#include <stdio.h> int main() { int a = -127; unsigned int b = 129; if(a > b) { printf("a會大于b"); } return 0; }
結(jié)果會打印 a會大于b
因為這就是算術(shù)轉(zhuǎn)換,即還是需要滿足同類型運算,unsigned int 比int 高,所以a的值會默認是 無符號的,a就會比b大.
算術(shù)提升方向:
1.3 操作符屬性
復(fù)雜表達式的求值有三個影響的因素。
操作符的優(yōu)先級操作符的結(jié)合性是否控制求值順序。
1.3.1 什么是優(yōu)先級?
就是決定先計算什么.比如d = a + b*c
. 因為*
的優(yōu)先級高于+
,所以,先算b*c
,再算+
1.3.2 什么是結(jié)合性?
就是同樣優(yōu)先級,就決定從哪個方向計算.比如d = a * b * c
,因為連續(xù)的*
,優(yōu)先級已經(jīng)沒有用了,所以此時就是結(jié)合性,*
的結(jié)合性是從左到右.也就是說先計算a*b
然后計算*c
.
1.3.3 什么是求值順序?
就是只計算哪邊.c語言的操作符具有求值順序的只有寥寥幾個,比如||, && , !
那求值順序
到底什么意思呢?
比如a等于0,b等于2,c等于3,d = a && b && c
,d的值最后是0,但是在運算時候只到a就完結(jié)了,因為&&
是只要碰到假就是假,后面的真假已經(jīng)無關(guān),a為0,是假,所以后面不用再管.這就是求值順序.
下面有兩個關(guān)于求值順序的小練習(xí):
#include <stdio.h> int main() { int i = 0,a=0,b=2,c =3,d=4; i = a++ && ++b && d++; printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d); return 0; }
答案是 1 2 3 4
.理由: a是后置++,先使用a=0的值,一開始就遇到假了,后面不再執(zhí)行.但是a還是增加了的,因為后置加加是 先使用再加加
#include <stdio.h> int main() { int i = 0,a=0,b=2,c =3,d=4; i = ++a || ++b || d++; printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d); return 0; }
答案:1 2 3 4
,前置++,即先加加,a成了1,因為||
一遇到真就結(jié)束,不再管后面真假.所以只有a變化了.
這里有張操作符屬性的表:
其中優(yōu)先級從上往下逐漸降低
1.3.4 第四題講解
#include <stdio.h> int main() { int c = 3; int ret = c + --c; printf("%d",ret); return 0; }
ret = c + --c
中有兩個操作符號,先看優(yōu)先級
,--
的優(yōu)先級高于+
,所以決定了先算–c,但是+
號左邊的c是什么時候準(zhǔn)備的呢? 我們知道,c語言是編譯性語言,在代碼寫好以后是需要先進行編譯為機器語言,然后執(zhí)行的.那么在編譯時候,+號左邊的值是在--c
之前就已經(jīng)編譯好了呢,還是--c
之后編譯好了呢?這是不確定的.
- 在vs編譯器下答案是 4,他是在
--c
之后準(zhǔn)備好了c - 在gcc編譯器下答案是 5,他是在
--c
之前準(zhǔn)備好了c
所以: 這是問題代碼,我們以后不要寫這樣的垃圾代碼.
1.3.5 第五題講解
int main() { int i = 10; i = i-- - --i * ( i = -3 ) * i++ + ++i; printf("i = %d\n", i); return 0; }
這個是同樣的道理,雖然知道優(yōu)先級,但是結(jié)合性中的操作數(shù)什么時候準(zhǔn)備不確定,你看看在不同的編譯器操作的結(jié)果:
同樣是個垃圾代碼
1.3.6 第六題講解
#include <stdio.h> int fun() { static int count = 1; return ++count; } int main() { int answer; answer = fun() - fun() * fun(); printf( "%d\n", answer);//輸出多少? return 0; }
()
函數(shù)調(diào)用符號優(yōu)先級最高,但是這里有三個,到底先調(diào)用哪個呢??這又不確定了.
- vs編譯器是從左往右依次調(diào)用的,結(jié)果為-10.
- 但是其他編譯器呢??大家去試試gcc,codeblocks,Devc++,你一定會發(fā)現(xiàn)完全不一樣.
1.3.7 第七題講解
再說這個題之前,博主一定要批判某些高校,為什么呢??學(xué)校教大家++
符號時候是不是最喜歡用這種類型來考大家?在這明確告訴大家,這完全是在浪費時間!!!因為這種代碼根本沒有意義!!!.就是垃圾代碼
#include <stdio.h> int main() { int i = 1; int ret = (++i) + (++i) + (++i); printf("%d\n", ret); printf("%d\n", i); return 0; }
首先,()
括號的優(yōu)先級最高,但是有三個,到底先計算哪個?
其次,一個i
的變化,會不會影響另外的?這里也不確定.和第四題我們說的那個編譯之前c
到底什么時候準(zhǔn)備一樣.不確定.!!!
vs編譯器的值是 12 4gcc編譯器的值是 10 4
不同的編譯器不同的值,結(jié)果不一樣,這樣的代碼不配叫代碼.!!!
所以,以后看到這種題直接跳過,沒有意義.
總結(jié): 知道了什么是整型提升與截斷知道了什么是算術(shù)轉(zhuǎn)換知道了什么操作符的屬性,以后不能寫出這種類似的垃圾代碼.
以上就是程序員都不知道C語言中的這個小細節(jié)的詳細內(nèi)容,更多關(guān)于C語言小細節(jié)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C語言動態(tài)與靜態(tài)分別實現(xiàn)通訊錄詳細過程
這篇文章主要為大家介紹了C語言動態(tài)與靜態(tài)分別實現(xiàn)通訊錄,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-02-02C語言詳細分析結(jié)構(gòu)體的內(nèi)存對齊規(guī)則
C 數(shù)組允許定義可存儲相同類型數(shù)據(jù)項的變量,結(jié)構(gòu)是 C 編程中另一種用戶自定義的可用的數(shù)據(jù)類型,它允許你存儲不同類型的數(shù)據(jù)項,本篇讓我們來了解C 的結(jié)構(gòu)體內(nèi)存對齊2022-07-07