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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

c类型转换

發布時間:2024/9/30 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 c类型转换 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

http://learn.akae.cn/media/ch15s03.html

在一個表達式中,凡是可以使用int或unsigned int類型做右值的地方也都可以使用有符號或無符號的char型、short型和Bit-field。如果原始類型的取值范圍都能用int型表示,則其類型被提升為int,如果原始類型的取值范圍用int型表示不了,則提升為unsigned int型,這稱為Integer Promotion。做Integer Promotion只影響上述幾種類型的值,對其它類型無影響。C99規定Integer Promotion適用于以下幾種情況:

1、如果一個函數的形參類型未知,例如使用了Old Style C風格的函數聲明(詳見第?2?節 “自定義函數”),或者函數的參數列表中有...,那么調用函數時要對相應的實參做Integer Promotion,此外,相應的實參如果是float型的也要被提升為double型,這條規則稱為Default Argument Promotion。我們知道printf的參數列表中有...,除了第一個形參之外,其它形參的類型都是未知的,比如有這樣的代碼:

char ch = 'A'; printf("%c", ch);

ch要被提升為int型之后再傳給printf

2、算術運算中的類型轉換。有符號或無符號的char型、short型和Bit-field在做算術運算之前首先要做Integer Promotion,然后才能參與計算。例如:

unsigned char c1 = 255, c2 = 2; int n = c1 + c2;

計算表達式c1 + c2的過程其實是先把c1和c2提升為int型然后再相加(unsigned char的取值范圍是0~255,完全可以用int表示,所以提升為int就可以了,不需要提升為unsigned int),整個表達式的值也是int型,最后的結果是257。假如沒有這個提升的過程,c1 + c2就溢出了,溢出會得到什么結果是Undefined,在大多數平臺上會把進位截掉,得到的結果應該是1。

除了+號之外還有哪些運算符在計算之前需要做Integer Promotion呢?我們在下一小節先介紹Usual Arithmetic Conversion規則,然后再解答這個問題。


3.2.?Usual Arithmetic Conversion?請點評

兩個算術類型的操作數做算術運算,比如a + b,如果兩邊操作數的類型不同,編譯器會自動做類型轉換,使兩邊類型相同之后才做運算,這稱為Usual Arithmetic Conversion。轉換規則如下:

  • 如果有一邊的類型是long double,則把另一邊也轉成long double。

  • 否則,如果有一邊的類型是double,則把另一邊也轉成double。

  • 否則,如果有一邊的類型是float,則把另一邊也轉成float。

  • 否則,兩邊應該都是整型,首先按上一小節講過的規則對a和b做Integer Promotion,然后如果類型仍不相同,則需要繼續轉換。首先我們規定char、short、int、long、long long的轉換級別(Integer Conversion Rank)一個比一個高,同一類型的有符號和無符號數具有相同的Rank。轉換規則如下:

  • 如果兩邊都是有符號數,或者都是無符號數,那么較低Rank的類型轉換成較高Rank的類型。例如unsigned int和unsigned long做算術運算時都轉成unsigned long。

  • 否則,如果一邊是無符號數另一邊是有符號數,無符號數的Rank不低于有符號數的Rank,則把有符號數轉成另一邊的無符號類型。例如unsigned long和int做算術運算時都轉成unsigned long,unsigned long和long做算術運算時也都轉成unsigned long。

  • 剩下的情況是:一邊有符號另一邊無符號,并且無符號數的Rank低于有符號數的Rank。這時又分為兩種情況,如果這個有符號數類型能夠覆蓋這個無符號數類型的取值范圍,則把無符號數轉成另一邊的有符號類型。例如遵循LP64的平臺上unsigned int和long在做算術運算時都轉成long。

  • 否則,也就是這個有符號數類型不足以覆蓋這個無符號數類型的取值范圍,則把兩邊都轉成有符號數的Rank對應的無符號類型。例如在遵循ILP32的平臺上unsigned int和long在做算術運算時都轉成unsigned long。

    unsigned int 如果轉為long,因為在x86下,long和unsigned int 的字長一樣,而unsigned int的范圍要大于long,所以都要轉為unsigned long

  • 可見有符號和無符號整數的轉換規則是十分復雜的,雖然這是有明確規定的,不屬于陰暗角落,但為了程序的可讀性不應該依賴這些規則來寫代碼。我講這些規則,不是為了讓你用,而是為了讓你了解有符號數和無符號數混用會非常麻煩,從而避免觸及這些規則,并且在程序出錯時記得往這上面找原因。所以這些規則不需要牢記,但要知道有這么回事,以便在用到的時候能找到我書上的這一段。

    到目前為止我們學過的+ - * / % > < >= <= == !=運算符都需要做Usual Arithmetic Conversion,因為都要求兩邊操作數的類型一致,在下一章會介紹幾種新的運算符也需要做Usual Arithmetic Conversion。單目運算符+ - ~只有一個操作數,移位運算符<< >>兩邊的操作數類型不要求一致,這些運算不需要做Usual Arithmetic Conversion,但也需要做Integer Promotion,運算符~ << >>將在下一章介紹。

    3.3.?由賦值產生的類型轉換?請點評

    如果賦值或初始化時等號兩邊的類型不相同,則編譯器會把等號右邊的類型轉換成等號左邊的類型再做賦值。例如int c = 3.14;,編譯器會把右邊的double型轉成int型再賦給變量c。

    我們知道,函數調用傳參的過程相當于定義形參并且用實參對其做初始化,函數返回的過程相當于定義一個臨時變量并且用return的表達式對其做初始化,所以由賦值產生的類型轉換也適用于這兩種情況。例如一個函數的原型是int foo(int, int);,則調用foo(3.1, 4.2)時會自動把兩個double型的實參轉成int型賦給形參,如果這個函數定義中有返回語句return 1.2;,則返回值1.2會自動轉成int型再返回。

    在函數調用和返回過程中發生的類型轉換往往容易被忽視,因為函數原型和函數調用并沒有寫在一起。例如char c = getchar();,看到這一句往往會想當然地認為getchar的返回值是char型,而事實上getchar的返回值是int型,這樣賦值會引起類型轉換,可能產生Bug,我們在第?2.5?節 “以字節為單位的I/O函數”詳細討論這個問題。

    3.4.?強制類型轉換?請點評

    以上三種情況通稱為隱式類型轉換(Implicit Conversion,或者叫Coercion),編譯器根據它自己的一套規則將一種類型自動轉換成另一種類型。除此之外,程序員也可以通過類型轉換運算符(Cast Operator)自己規定某個表達式要轉換成何種類型,這稱為顯式類型轉換(Explicit Conversion)或強制類型轉換(Type Cast)。例如計算表達式(double)3 + i,首先將整數3強制轉換成double型(值為3.0),然后和整型變量i相加,這時適用Usual Arithmetic Conversion規則,首先把i也轉成double型,然后兩者相加,最后整個表達式也是double型的。這里的(double)就是一個類型轉換運算符,這種運算符由一個類型名套()括號組成,屬于單目運算符,后面的3是這個運算符的操作數。注意操作數的類型必須是標量類型,轉換之后的類型必須是標量類型或者void型。

    3.5.?編譯器如何處理類型轉換?請點評

    以上幾小節介紹了哪些情況下會發生類型轉換,并且明確了每種情況下會把什么類型轉成什么類型,本節介紹編譯器如何處理任意兩種類型之間的轉換。現在要把一個M位的類型(值為X)轉換成一個N位的類型,所有可能的情況如下表所示。

    表?15.3.?如何做類型轉換

    待轉換的類型 M > N的情況 M == N的情況 M < N的情況
    signed integer to signed integer 如果X在目標類型的取值范圍內則值不變,否則Implementation-defined 值不變 值不變
    unsigned integer to signed integer 如果X在目標類型的取值范圍內則值不變,否則Implementation-defined 如果X在目標類型的取值范圍內則值不變,否則Implementation-defined 值不變
    signed integer to unsigned integer X % 2N X % 2N X % 2N
    unsigned integer to unsigned integer X % 2N 值不變 值不變
    floating-point to signed or unsigned integer Truncate toward Zero,如果X的整數部分超出目標類型的取值范圍則Undefined
    signed or unsigned integer to floating-point 如果X在目標類型的取值范圍內則值不變,但有可能損失精度,如果X超出目標類型的取值范圍則Undefined
    floating-point to floating-point 如果X在目標類型的取值范圍內則值不變,但有可能損失精度,如果X超出目標類型的取值范圍則Undefined 值不變 值不變

    注意上表中的“X % 2N”,我想表達的意思是“把X加上或者減去2N的整數倍,使結果落入[0, 2N-1]的范圍內”,當X是負數時運算結果也得是正數,即運算結果和除數同號而不是和被除數同號,這不同于C語言%運算的定義。寫程序時不要故意用上表中的規則,尤其不要觸碰Implementation-defined和Undefined的情況,但程序出錯時可以借助上表分析錯誤原因。

    下面舉幾個例子說明上表的用法。比如把double型轉換成short型,對應表中的“floating-point to signed or unsigned integer”,如果原值在(-32769.0, 32768.0)之間則截掉小數部分得到轉換結果,否則產生溢出,結果是Undefined,例如對于short s = 32768.4;這個語句gcc會報警告。

    比如把int型轉換成unsigned short型,對應表中的“signed integer to unsigned integer”,如果原值是正的,則把它除以216取模,其實就是取它的低16位,如果原值是負的,則加上216的整數倍,使結果落在[0, 65535]之間。

    比如把int類型轉換成short類型,對應表中的“signed integer to signed integer”,如果原值在[-32768, 32767]之間則值不變,否則產生溢出,結果是Implementation-defined,例如對于short s = -32769;這個語句gcc會報警告。

    最后一個例子,把short型轉換成int型,對應表中的“signed integer to signed integer”,轉換之后應該值不變。那怎么維持值不變呢?是不是在高位補16個0就行了呢?如果原值是-1,十六進制表示就是ffff,要轉成int型的-1需要變成ffffffff,因此需要在高位補16個1而不是16個0。換句話說,要維持值不變,在高位補1還是補0取決于原來的符號位,這稱為符號擴展(Sign Extension)。



    ?? ?unsigned char a = 0xa5;
    ?? ?unsigned char b = ~a>>4+1;
    ?? ?unsigned char c = 4,e = 1;


    ?? ?unsigned char d = ~a>>c+e;
    ?? ?unsigned char f = ~a ;
    ?? ?cout<<sizeof(a)<<" " <<sizeof(a+c)<<" "<<sizeof(~a)<<endl;
    ???
    ?? ?printf("%x %x %x %x\n",a,b,d,f);


    output:

    1 4 4
    a5 fa fa 5a




    總結

    以上是生活随笔為你收集整理的c类型转换的全部內容,希望文章能夠幫你解決所遇到的問題。

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