[英]Integer division in C with unsigned short
我覺得自己是最血腥的初學者-以下內容為何無效:
// declarations
unsigned short currentAddr= 0x0000;
unsigned short addr[20] = {1, 0};
// main
addr[1] = (~currentAddr)/2+1;
printf("addr[1] wert: %hu\n", addr[1]); // equals 1, expecte 0x8000
addr[1] = ~currentAddr>>1;
printf("addr[1] wert: %hu\n", addr[1]); // equals 65535, expected 0x7FFF
在printf以及調試器的監視列表中,addr [1]的值與預期的不同。 我的目標是使變量的最大值減半,這里為0x8000。 信息:我正在〜currentAddr以獲取最大值。 如果短,則0xFFFF在我的嵌入式平台上的長度與在我的PC上的長度不同。
干杯,斯特凡
整數提升對一元~
的操作數執行。
在許多系統上, int
大於short
。 在此類系統上,對於unsigned short currentAddr = 0
,首先在表達式~currentAddr
中將currentAddr
的值提升為int
。 然后~currentAddr
值~currentAddr
-1
(假設二進制補碼表示)。
在某些系統上, int
和short
的大小可能相同(盡管int
必須至少與short
一樣大); 在這里, currentAddr
會被提升為unsigned int
因為int
無法容納相同大小的unsigned
整數類型的所有值。 在這種情況下, ~currentAddr
將評估為UINT_MAX
。 對於16位int
( short
必須至少為16位,因此int
和short
將具有相同的大小), ~currentAddr
的結果將為65,535。
OP的系統必須具有大於short
int
。 在addr[1] = (~currentAddr)/2+1;
這變成addr[1] = (-1)/2+1;
計算結果為1
。
在第二種情況下, addr[1] = ~currentAddr>>1;
計算結果為addr[1] = (-1)>>1;
。 在此,右移負值的結果是實現定義的。 在當前情況下,結果似乎是INT_MAX
,在對addr[1]
的賦值INT_MAX
其轉換為unsigned short
在轉換中采用值USHRT_MAX
。 在OP的系統上,該值為65,535。
為了清楚,可靠地獲取標准整數類型的最大值和最小值,請使用limits.h
的宏,而不要嘗試進行位操作。 此方法不會令人失望:
#include <stdio.h>
#include <limits.h>
int main(void)
{
unsigned short val;
val = (USHRT_MAX / 2) + 1;
printf("(USHRT_MAX / 2) + 1: %#hx\n", val);
val = USHRT_MAX >> 1;
printf(" USHRT_MAX >> 1: %#hx\n", val);
return 0;
}
程序輸出:
(USHRT_MAX / 2) + 1: 0x8000
USHRT_MAX >> 1: 0x7fff
問題出在這里:
addr[1] = (~currentAddr)/2+1;
您期望currentAddr
為0xFFFF
,這是部分正確的。 但是,您可能會錯過的是整數提升規則,該規則使它成為0xFFFFFFFF
,它是-1
十六進制表示形式。
現在,有一個簡單的數學(~currentAddr)/2+1
: (~currentAddr)/2+1
只是0x01
或1
,當您~currentAddr>>1;
進行此移位,它將再次變為-1
。
從
我的目標是使變量的最大值減半,這里為
0x8000
如果我正確理解您的意思,那么您要嘗試獲得的值等於(無符號短整數的最大值)/ 2。 如果是這樣,正確的做法是使用USHRT_MAX
。 當然,您需要在源代碼中包含limits.h
文件。
更新:
參考您對David答案的評論,以下更改將按預期進行。 (您已經測試過,但我還沒有)
unsigned short c;
c = ~currentAddr;
unsigned short c_z = sizeof (c);
unsigned short ci;
ci = (c >> 1) + 1;
unsigned short ci_z = sizeof (ci);
addr[1] = ci;
現在,為什么不將其提升為整數,而不是以前的情況
c = ~currentAddr;
它被提升了,但是卻產生了預期的結果,因為正如chux所解釋的那樣(我做不到),它在運行過程中被(暫時)提升為int
,但是當它被解析為unsigned short
時又被解析為int
。存儲在分配給c
內存中。
C標准回答了這個問題:
根據C99標准:6.5.16.1簡單分配
在簡單賦值(=)中,將右操作數的值轉換為賦值表達式的類型,並替換存儲在由左操作數指定的對象中的值。 在您的情況下,由於LHS和RHS屬於同一類型,因此無需進行任何轉換。
另外,它說:
賦值表達式的類型是左值轉換后左操作數將具有的類型。
由C11 6.5.16.1/2指定相同的內容:
在簡單賦值(=)中,將右操作數的值轉換為賦值表達式的類型,並替換存儲在由左操作數指定的對象中的值。
自己嘗試一下:
int main(void)
{
unsigned short c;
unsigned short currentAddr= 0x0000;
c = ~currentAddr;
printf("\n0x%x", c);
printf("\n0x%x", (~currentAddr));
return 0;
}
這應該打印:
0xffff
0xffffffff
addr[1] = (~currentAddr)/2+1;
讓我們分解一下: currentAddr
是計算中涉及的unsigned short
,因此首先將值/類型提升為int
或unsigned
。 在C語言中,這是整數提升 。
如果
int
可以表示原始類型...的所有值,則該值將轉換為int; 否則,將其轉換為unsigned int
。 這些稱為整數促銷 。 整數促銷未更改所有其他類型。 C11dr§6.3.1.12
當USHRT_MAX <= INT_MAX
時(例如16位short int / unsigned,32位int / unsigned),代碼如下。 當currentAddr == 0
且典型的2的補碼行為為currentAddr == 0
> -1和addr[1]
-> 1。
int tmp = currentAddr;
addr[1] = (~tmp)/2+1;
當USHRT_MAX > INT_MAX
時(例如16位short int / unsigned,16位int / unsigned),代碼如下所示。 在currentAddr == 0
且無符號行為的情況下, currentAddr == 0
> 0xFFFF和addr[1]
-> 0x8000。
unsigned tmp = currentAddr;
addr[1] = (~tmp)/2+1;
我的目標是使變量的最大值減半
獲得最大unsigned short
的最佳方法是使用SHRT_MAX
並跳過~
代碼。 無論unsigned short, int, unsigned
范圍如何,它都將按預期工作。 它還可以更好地記錄代碼意圖。
#include <limits.h>
addr[1] = USHRT_MAX/2+1;
因為數字2是int並且int可以保留unsigned short,所以實際的操作是addr[1] = (unsigned short)(((int)(~currentAddr)/2)+1)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.