簡體   English   中英

C中的整數除法,無符號短整數

[英]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 (假設二進制補碼表示)。

在某些系統上, intshort的大小可能相同(盡管int必須至少與short一樣大); 在這里, currentAddr會被提升為unsigned int因為int無法容納相同大小的unsigned整數類型的所有值。 在這種情況下, ~currentAddr將評估為UINT_MAX 對於16位intshort必須至少為16位,因此intshort將具有相同的大小), ~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;

您期望currentAddr0xFFFF ,這是部分正確的。 但是,您可能會錯過的是整數提升規則,該規則使它成為0xFFFFFFFF ,它是-1十六進制表示形式。

現在,有一個簡單的數學(~currentAddr)/2+1(~currentAddr)/2+1只是0x011 ,當您~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 ,因此首先將值/類型提升為intunsigned 在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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM