<input id="0qass"><u id="0qass"></u></input>
  • <input id="0qass"><u id="0qass"></u></input>
  • <menu id="0qass"><u id="0qass"></u></menu>

    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. 操作符的優先級
    2. 操作符的結合性
    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 4
    • gcc編譯器的值是 10 4
      不同的編譯器不同的值,結果不一樣,這樣的代碼不配叫代碼.!!!

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

    總結:

    • 知道了什么是整型提升與截斷
    • 知道了什么是算術轉換
    • 知道了什么操作符的屬性,以后不能寫出這種類似的垃圾代碼.
    相關推薦
    ??2020 CSDN 皮膚主題: 技術黑板 設計師:CSDN官方博客 返回首頁
    多乐彩