2011-10-30 45 views
5

這個問題首先是由該碼的(不期望的)的結果的啓發:C標準和bitshifts

uint16_t t16 = 0; 
uint8_t  t8 = 0x80; 
uint8_t t8_res; 

t16 = (t8 << 1); 
t8_res = (t8 << 1); 

printf("t16: %x\n", t16); // Expect 0, get 0x100 
printf(" t8: %x\n", t8_res); // Expect 0, get 0 

但事實證明,這是有意義的:

6.5.7按位移位運算符

約束

每個操作數應具有整數類型

因此最初困惑線等同於:

t16 = (uint16_t) (((int) t8) << 1); 

小非直觀IMHO,但至少明確定義。

好,太好了,但我們這樣做:

{ 
uint64_t t64 = 1; 
t64 <<= 31; 
printf("t64: %lx\n", t64); // Expect 0x80000000, get 0x80000000 
t64 <<= 31; 
printf("t64: %lx\n", t64); // Expect 0x0, get 0x4000000000000000 
} 

//編輯:按照同樣的文字參數如上,下面應該是等價的:

t64 = (uint64_t) (((int) t64) << 31); 

//所以我的困惑/ expectation [end_edit]

現在,我們得到了直觀的結果,但不是從我的(文字)閱讀標準中得出的。這種「進一步自動式促銷」何時/如何發生?還是有侷限性的其他地方,一個類型不能被降級(這將使有意義嗎?),在這種情況下,怎麼做推廣規則適用於:

uint32_t << uint64_t 

由於標準不說,這兩個參數都提升到INT;這兩個參數是否應該在這裏被提升到相同的類型?

//編輯:

更具體地說,應該怎樣的結果:

uint32_t t32 = 1; 
uint64_t t64_one = 1; 
uint64_t t64_res; 

t64_res = t32 << t64_one; 

//結束編輯

,當我們認識到,規範的回答上面的問題得到解決並不要求促銷int具體,而是要求integer type,其中uint64_t具有資格。

//澄清編輯:

好了,但現在我很迷茫一次。具體而言,如果uint8_t是整數類型,那麼它爲什麼完全被提升爲int?它似乎並不如下面的練習說明了將相關的整型1:

{ 
uint16_t t16 = 0; 
uint8_t t8 = 0x80; 
uint8_t t8_one = 1; 
uint8_t t8_res; 

t16 = (t8 << t8_one); 
t8_res = (t8 << t8_one); 

printf("t16: %x\n", t16); 
printf(" t8: %x\n", t8_res); 
} 

t16: 100 
t8: 0 

爲什麼要升級(T8 < < t8_one)的表達,如果uint8_t是一個整數類型? -

僅供參考,我是從ISO/IEC 9899工作:TC9,WG14/N1124 5月6日,2005年如果這是過時,有人也可以提供一個鏈接到一個更新的副本,那也是值得讚賞的。

+3

我不明白你爲什麼說在第二次移位後留下31位...... // –

+0

我很困惑,因爲格雷格是,但預計類型永遠不會自動降級(縮小),只提升(加寬)。 –

+0

通過閱讀標準,t64 =(uint64_t)(((int)t64)<< 31)是一個等價線,在這種情況下,set bit應該丟失爲int截斷。 – Pat

回答

5

§6.5.7中的約束條件「每個操作數應具有整數類型」。是一個約束條件,意味着您不能在非整數類型(如浮點值或指針)上使用按位移運算符。它不會導致你注意到的效果。

確實原因效果在接下來的段落中的部分:

  3.整數優惠在每個操作數的執行。結果的類型是提升的左操作數的類型。

整數優惠在§6.3.1.1中描述:

  2。以下可以在表達式中使用的任何地方可使用intunsigned int

  • 的物體或表達的整數類型,其整數轉換秩小於或等於的intunsigned int秩。
  • 類型_Bool,int,signed intunsigned int的位字段。

如果int可以表示原始類型的所有值,該值 被轉換爲int;否則,它將轉換爲unsigned int。這些被稱爲整數促銷。所有其他類型均爲 ,不受整數升級的影響。

uint8_t具有比int較小等級,因此該值被轉換爲int(因爲我們知道,一個int必須能夠表示的uint8_t所有值,鑑於這兩種類型的範圍的要求)。

排名規則很複雜,但他們保證排名較高的類型不會有較低的精度。這意味着,實際上,這些類型不能通過整數促銷而被「降級」爲精度較低的類型(如果該類型的範圍至少爲uint64_tunsigned int或, uint64_t)。

uint32_t << uint64_t的情況下,引入的規則是「結果的類型是升級的左操作數的類型」。因此,我們有幾種可能性:

  • 如果int至少是33位,那麼uint32_t將晉升爲int,其結果將是int;
  • 如果int小於33位,且unsigned int至少爲32位,則uint32_t將被提升爲unsigned int,結果將爲unsigned int;
  • 如果unsigned int小於32位,則uint32_t將保持不變,結果將爲uint32_t

在今天的普通臺式機和服務器實現,intunsigned int通常是32位的,所以會出現第二種可能性(uint32_t被提升爲unsigned int)。在過去,int/unsigned int通常爲16位,並且第三種可能性會發生(uint32_t未啓用)。

的例子的結果:

uint32_t t32 = 1; 
uint64_t t64_one = 1; 
uint64_t t64_res; 

t64_res = t32 << t64_one; 

將存入t64_res2。不過,請注意,這不是受事實表達式的結果是不uint64_t - 和例如表達的那影響是:

uint32_t t32 = 0xFF000; 
uint64_t t64_shift = 16; 
uint64_t t64_res; 

t64_res = t32 << t64_shift; 

這裏的結果是0xf0000000

注意,雖然細節是相當複雜的,你可以煮一切歸因於一個相當簡單的規則,你應該記住:

在C,算術從來沒有在類型比int窄完成/ unsigned int

+0

啊....第6.3.1.1秒2中的最後一位真的給我密封了。以及您的最終總結規則。我的C心理模型更多的是「按需推進」(因此最終提出了uint8_t問題)。這個答案很有意義,謝謝你的時間。 -pat – Pat

7

我覺得你的問題的根源可能是以下兩個語句是相當於:

  • 每個操作數應具有整數類型
  • 每個操作數的應有int

uint64_t是一個整數類型。

+2

事實上,這是OP的混淆之源。 –

+0

太棒了!謝謝。 – Pat

+1

@Pat:請注意,'uint8_t'也是一個整數類型,所以這個規則不負責你在第一個例子中看到的行爲。作爲[Jens Gustedt](http://stackoverflow.com/questions/7947982/c-standard-and-bitshifts/7948145#7948145)說,這是「通常的算術轉換」,它會導致類型比'int'更窄的值(被提升爲「int」或「unsigned」)。 – caf

3

您在標準中找到了錯誤的規則:(相關內容類似於「通常的整數類型促銷應用」,這是第一個例子中的內容,如果像uint8_t這樣的整數類型的排名較小比int它提升爲intuint64_t還沒有一個排名是小於intunsigned所以不進行推廣和<<運營商應用到uint64_t變量

編輯:比int所有整數類型。被提升爲算術。這只是一個生活的事實:)無論是否促銷uint32_t取決於平臺,因爲它可能具有相同的等級或高於int(未晉升)或較低等級(晉升)。

關於<<運算符,右操作數的類型並不重要,左位(具有上述規則)的位數是多少。更重要的是它的價值。它不是負數或超過(提升的)左操作數的寬度。

+0

因爲換行符不被允許在評論中修改主要問題,請看一下嗎? – Pat