程序員都不知道C語言中的這些小細節

既然題目都說瞭是小細節,一來就介紹細節多沒意思啊,先坑坑大傢再詳細介紹吧,嘿嘿.直接上7個題吧,看看你能做對幾個呢?


計算型細節

①:

#include <stdio.h>
int main()
{
	char a = 3;
	char b = 127;
	char c = a + b;
	printf("結果是:%d",c);
	return 0;
}

您想想這個題的答案是多少?先不要看後面的答案哦
答案是 -126, 嘿嘿,是不是答錯瞭呢?先不要著急,繼續看下面的題

②:

#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,嘿嘿,是不是又回答錯誤,先不要著急,再看看後面的題

表達式細節

①:

#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;
}

您想想這個題的答案是什麼呢? 先不要看後面的答案哦
答案有很多個,在不同的編譯器結果不同,也就是說還是不能確定結果,嘿嘿,是不是仍然回答錯誤瞭,先不要著急,再看看後面的題

③:

#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;
}

您想想這個題的答案是什麼呢? 先不要看後面的答案哦
答案是 不同編譯器不同結果 ,嘿嘿,是不是又回答錯誤,先不要著急,再看看後面的題

④:

#include <stdio.h>
int main()
{
	 int i = 1;
	 int ret = (++i) + (++i) + (++i);
	 printf("%d\n", ret);
	 printf("%d\n", i);
	 return 0;
}

這是最後一個題瞭,你目前答對瞭幾道瞭呢??請在評論回答試試.嘿嘿
這個題的答案是 不同編譯器不同結果

大傢回答對瞭幾道題?歡迎評論

 1.現在正式講解上面所有的題設計到的內容——–表達式求值

表達式求值的順序一部分是由操作符的優先級和結合性決定。
同樣,有些表達式的操作數在求值的過程中可能需要轉換為其他類型。


1.1隱式類型轉換 (整型截斷與提升)

什麼是隱式類型轉換,整型提升,整型截斷?

C的整型算術運算總是至少以滿足整型類型的精度來進行的。
為瞭獲得這個精度,表達式中若有charshort類型必須在使用之前轉換稱為整型,這個過程叫做 整型提升
.
一個較大數據類型存儲在較小數據類型中的過程叫做整型截斷,比如整型a = 500,但是a把他的值放到瞭字符型b中,b不能完全存放a,就會發生整型截斷
.
而這個轉換行為叫做 隱式類型轉換

1.1.1 第一題講解

#include <stdio.h>
int main()
{
	char a = 3;
	char b = 127;
	char c = a + b;
	printf("結果是:%d",c);
	return 0;
}

我們知道計算機中一切都是操作的補碼.所以這裡也是一樣,(不明白原碼補碼反碼的自己去百度下哦).
其中 正數的 原碼 反碼 補碼 一模一樣
… …負數的 補碼是反碼加一,反碼是原碼除瞭符號位所有位都按位取反

a隻有1個字節,他的補碼是00000011(二進制)
b隻有1個字節,他的補碼是01111111(二進制)
a加b,進行瞭算術操作,需要提升到4個字節.而整型提升有兩種方式

  • 算術提升:在最前面補符號位數字,直到32位
  • 邏輯提升:無論什麼都隻補0,直到32位

大多數計算器都是進行的算術提升,這裡便講解算術提升.

a算術提升後就是00000000000000000000000000000011
b算術提升後就是00000000000000000000000001111111
a+b的結果是——00000000000000000000000010000010
存進c裡面,c又隻有1個字節,所以發生整型截斷,隻存取最後8位
10000010
此時c存取的是補碼,且c是有符號型,記住瞭!!,打印顯示時候會還原位原碼的,該補碼對應的數字就是 -126

下面有張字符類型原碼補碼反碼的對應理解圖
在這裡插入圖片描述


所以有符號的范圍是 [-128,127]
無符號的范圍是[0,255]

變成圓圈更好理解,自己按照有無符號對應去找就行.



在這裡插入圖片描述



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中,發生截斷,得到1011 0110
0xb600的二進制是00000000 00000000 10110110 00000000
存進去b中發生截斷,得到10110110 00000000
0xb600000010110110 00000000 00000000 00000000
存進去c中剛剛好.

a此時存的是補碼,a又與0xb6比較,參與瞭運算,所以又整型提升為
11111111 11111111 11111111 10110110,

整型提升後的數字與0xb6的二進制不一樣,所以不等.

b此時存的是補碼,b又與0xb600比較,參與瞭運算,所以又整型提升為
11111111 11111111 10110110 00000000

整型提升後的數字與0xb600的二進制不一樣,所以不等

c此時存的是10110110 00000000 00000000 00000000,註意哦~,有人可能會問0xb6000000是正數啊,不!!!!!,字面常量隻要不帶符號就是默認int類型.所以此時0xb6000000的整數值是超過int的,就會認為它此時的二進制是負數的補碼.所以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,隻有一個字節,所以答案是1
+c,+是單目操作符,與c在一起發生瞭整型提升,變成瞭int,所以是4個字節,答案是 4
-c,同樣的道理,仍然是4

1.2算術轉換

上面我們說到,如果操作數大小 小於int,會發生整型提升,但是如果都是大於等於int大小的類型參與算術運算呢?? 這裡就會涉及到 算術轉換.
什麼是算術轉換呢? 比如下面的例子:

#include <stdio.h>
int main()
{
	int a = -127;
	unsigned int b =  129;
	if(a > b)
	{
		printf("a會大於b");
	}
	return 0;
}

結果會打印 a會大於b
因為這就是算術轉換,即還是需要滿足同類型運算,unsigned int 比int 高,所以a的值會默認是 無符號的,a就會比b大.

算術提升方向:
在這裡插入圖片描述


1.3 操作符屬性

復雜表達式的求值有三個影響的因素。

操作符的優先級操作符的結合性是否控制求值順序。


1.3.1 什麼是優先級?

就是決定先計算什麼.比如d = a + b*c. 因為*的優先級高於+,所以,先算b*c,再算+

1.3.2 什麼是結合性?

就是同樣優先級,就決定從哪個方向計算.比如d = a * b * c ,因為連續的*,優先級已經沒有用瞭,所以此時就是結合性,*的結合性是從左到右.也就是說先計算a*b 然後計算*c.

1.3.3 什麼是求值順序?

就是隻計算哪邊.c語言的操作符具有求值順序的隻有寥寥幾個,比如||, && , !
求值順序到底什麼意思呢?
比如a等於0,b等於2,c等於3,d = a && b && c,d的值最後是0,但是在運算時候隻到a就完結瞭,因為&&是隻要碰到假就是假,後面的真假已經無關,a為0,是假,所以後面不用再管.這就是求值順序.

下面有兩個關於求值順序的小練習:

#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的值,一開始就遇到假瞭,後面不再執行.但是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,因為||一遇到真就結束,不再管後面真假.所以隻有a變化瞭.


這裡有張操作符屬性的表:

在這裡插入圖片描述
其中優先級從上往下逐漸降低

1.3.4 第四題講解

#include <stdio.h>
int main()
{
	 int c = 3;
	 int ret = c + --c;
	 printf("%d",ret);  
	 return 0;
}

ret = c + --c中有兩個操作符號,先看優先級,--的優先級高於+,所以決定瞭先算–c,但是+號左邊的c是什麼時候準備的呢? 我們知道,c語言是編譯性語言,在代碼寫好以後是需要先進行編譯為機器語言,然後執行的.那麼在編譯時候,+號左邊的值是在--c之前就已經編譯好瞭呢,還是--c之後編譯好瞭呢?這是不確定的.

  • 在vs編譯器下答案是 4,他是在--c之後準備好瞭c
  • 在gcc編譯器下答案是 5,他是在--c之前準備好瞭c

所以: 這是問題代碼,我們以後不要寫這樣的垃圾代碼.

1.3.5 第五題講解

int main()
{
	 int i = 10;
	 i = i-- - --i * ( i = -3 ) * i++ + ++i;
	 printf("i = %d\n", i);
	 return 0;
}

這個是同樣的道理,雖然知道優先級,但是結合性中的操作數什麼時候準備不確定,你看看在不同的編譯器操作的結果:
在這裡插入圖片描述
同樣是個垃圾代碼

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;
}

()函數調用符號優先級最高,但是這裡有三個,到底先調用哪個呢??這又不確定瞭.

  • vs編譯器是從左往右依次調用的,結果為-10.
  • 但是其他編譯器呢??大傢去試試gcc,codeblocks,Devc++,你一定會發現完全不一樣.

1.3.7 第七題講解

再說這個題之前,博主一定要批判某些高校,為什麼呢??學校教大傢++符號時候是不是最喜歡用這種類型來考大傢?在這明確告訴大傢,這完全是在浪費時間!!!因為這種代碼根本沒有意義!!!.就是垃圾代碼

#include <stdio.h>
int main()
{
	 int i = 1;
	 int ret = (++i) + (++i) + (++i);
	 printf("%d\n", ret);
	 printf("%d\n", i);
	 return 0;
}

首先,()括號的優先級最高,但是有三個,到底先計算哪個?
其次,一個i的變化,會不會影響另外的?這裡也不確定.和第四題我們說的那個編譯之前c到底什麼時候準備一樣.不確定.!!!

vs編譯器的值是 12 4gcc編譯器的值是 10 4
不同的編譯器不同的值,結果不一樣,這樣的代碼不配叫代碼.!!!

所以,以後看到這種題直接跳過,沒有意義.

總結: 知道瞭什麼是整型提升與截斷知道瞭什麼是算術轉換知道瞭什麼操作符的屬性,以後不能寫出這種類似的垃圾代碼.

以上就是程序員都不知道C語言中的這個小細節的詳細內容,更多關於C語言小細節的資料請關註WalkonNet其它相關文章!

推薦閱讀: