2017-05-01 99 views
0

sizeof運算符的結果似乎是size_t類型,它在Windows 64位上定義爲無符號long long。類型升級/轉換

考慮下面的一段(僞)代碼:

int func(unsigned long arg) 
{ 
    /* ... */ 
} 

/* ... */ 

unsigned long herp = 3, 
       derp = 5, 
       durr = 7; 

wchar_t wut; 

func(/* ... */); 

當func被稱爲如下什麼實際發生在這裏型推廣/換算的?

func((herp + derp) * durr * sizeof wut); // 1st example 

是HERP,DERP和杜爾的值第一晉升爲unsigned long long然後再對結果進行評估後,結果被轉換回爲unsigned long?

而是,如下所示調用func時,是唯一發生類型轉換爲無符號long的轉換?

func((herp + derp) * durr * (unsigned long)sizeof wut); // 2nd example 

考慮到類型轉換/提升,什麼是最適合/正確的方式來調用func?

+0

'(herp + derp)'對'size_t'一無所知,所以不被提升。但算術計算的值在計算之前會提升爲本地整數類型(如果更短)。 –

回答

2

當遇到算術運算符的參數大小不同時,編譯器應用"Usual Arithmetic Conversions",它們基本上使兩個操作數都是相同的類型。

這是貫穿一個表達式,一次一個操作符執行的。根據C的語法將表達式分解爲單獨的操作;編譯器不允許修改解析,除非它能證明結果沒有區別(在這種情況下,修改或多或少是不相關的)。

因此,考慮你的表達:

func((herp + derp) * durr * sizeof wut); 

這句法等同於下面的一系列操作中,哪些類型和轉換都省略(現在):

temp1 = herp + derp; 
temp2 = temp1 * durr; 
temp3 = sizeof wut 
temp4 = temp2 * temp3; 
temp5 = (unsigned long) temp4; 
func(temp5) 

前四臨時對象會自動鍵入每個運算符的結果類型(這是該運算符的常用算術轉換生成的類型)。

  1. temp1 = herp + derp;

    herpderp均爲unsigned long;沒有轉換是必要的;結果類型是unsigned long

  2. temp2 = temp1 * durr;

    temp1durr均爲unsigned long;沒有轉換是必要的;結果類型是unsigned long

  3. temp3 = sizeof wut;

    sizeof總是返回size_t,所以這是結果的類型。

  4. temp4 = temp2 * temp3

    temp2unsigned longtemp3size_t,其示例平臺上是unsigned long long。這需要將temp2轉換爲unsigned long long,之後結果類型爲unsigned long long

所以我們可以插入類型和轉換到上面的示例代碼:

unsigned long temp1 = herp + derp; 
unsigned long temp2 = temp1 * durr; 
unsigned long long temp3 = sizeof wut 
unsigned long long temp4 = (unsigned long long)temp2 * temp3; 
temp5 = (unsigned long)temp4; 
func(temp5) 

關鍵外賣這裏要說的是事實,結果將被轉換爲其他類型都有沒有其計算效果爲。編譯器不能決定不應用通常的算術轉換,或者應用不尋常的算術轉換(可能縮小參數而不是擴大另一個參數),除非它可以證明最終結果在所有情況下都與標準強制執行一項。 (在這裏,「在所有情況下」實際上是指「在所有情況下不會出現未定義的行爲」,但由於無符號算術的定義很明確,除以0除外,所以這個細節在這個例子中是不相關的。)

如果表達式涉及除法運算符,那麼溢出實際上很重要。考慮的

(a + b) * c * (sizeof x)/(sizeof y) 

其中abc是情況下,所有相同類型的,這是窄於size_t

與上述邏輯一樣,(a + b) * c在普通類型a,bc中評估。然後將結果提升爲size_t,以便它可以乘以x的大小。但在轉換之前(a + b) * c肯定會溢出,導致最終結果無效。堅持使用size_t操作數來執行整個計算會更安全。這可以通過添加一個明確的轉換來完成:

((size_t)a + b) * c * (sizeof x)/(sizeof y) 
+0

感謝您花時間回答我的問題。您的解釋非常感謝,並幫助我更好地理解算術轉換。 – treintje