簡體   English   中英

從32位到64位操作系統的無符號整數

[英]Unsigned int from 32 bit to 64bit OS

該代碼段摘自一本Linux書。 如果不適合在此處發布代碼段,請告訴我。 我將其刪除。 謝謝。

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
  char buf[30];
  char *p;
  int i;
  unsigned int index = 0;
  //unsigned long index = 0;
  printf("index-1 = %lx (sizeof %d)\n", index-1, sizeof(index-1));
  for(i = 'A'; i <= 'Z'; i++)
      buf[i - 'A'] = i;
  p  = &buf[1];
  printf("%c: buf=%p p=%p p[-1]=%p\n", p[index-1], buf, p, &p[index-1]);
  return 0;
}

在32位OS環境上:無論索引的數據類型是unsigned int還是unsigned long,此程序都可以正常工作。

在64位OS環境上:如果將index聲明為unsigned int,則同一程序將運行到“核心轉儲”中。 但是,如果僅將索引的數據類型從unsigned int更改為a)unsigned long或b)unsigned short,則該程序也可以正常工作。

書中的原因僅告訴我,由於非負數,64位將導致核心轉儲。 但是我完全不知道為什么unsigned long和unsigned short工作而unsigned int的原因。

我感到困惑的是

p + (0u -1) == p + UINT_MAX當索引為無符號整數時。

但,

p + (0ul - 1) == p[-1]當索引為無符號長p + (0ul - 1) == p[-1]時。

我被困在這里。

如果有人可以幫助您詳細說明,我們將不勝感激!

謝謝。

這是我的32位上的一些結果(RHEL5.10 / gcc版本4.1.2 20080704)

和64位計算機(RHEL6.3 / gcc版本4.4.6 20120305)

我不確定gcc版本在這里是否有任何區別。 因此,我也粘貼了信息。

在32位上:

我嘗試了兩個更改:

1)將unsigned int index = 0修改為unsigned short index = 0

2)將unsigned int index = 0修改為unsigned char index = 0

該程序可以正常運行。

index-1 = ffffffff (sizeof 4)

A: buf=0xbfbdd5da p=0xbfbdd5db p[-1]=0xbfbdd5da

似乎由於-1,索引的數據類型將提升為4個字節。

在64位上:

我嘗試了三個更改:

1)將unsigned int index = 0修改為unsigned char index = 0

  It works!

index-1 = ffffffff (sizeof 4)

A: buf=0x7fffef304ae0 p=0x7fffef304ae1 p[-1]=0x7fffef304ae0

2)將unsigned int index = 0修改為unsigned short index = 0

 It works!

index-1 = ffffffff (sizeof 4)

A: buf=0x7fff48233170 p=0x7fff48233171 p[-1]=0x7fff48233170

3)將unsigned int index = 0修改為unsigned long index = 0

 It works!

index-1 = ffffffff (sizeof 8)

A: buf=0x7fffb81d6c20 p=0x7fffb81d6c21 p[-1]=0x7fffb81d6c20

但只有

unsigned int index = 0在最后一個printf處運行到核心轉儲中。

index-1 = ffffffff (sizeof 4)

Segmentation fault (core dumped)

不要騙編譯器!

printf傳遞給需要long int%ld )的int是未定義的行為。
(創建指向任何有效對象外部(而不是僅在一個對象之后)的指針也是UB ...)

更正格式說明符和指針算法(在特殊情況下包括索引編制),一切正常。

UB包括“按預期方式運行”以及“災難性故障”。

順便說一句:如果您禮貌地向編譯器詢問所有警告,它將警告您。 使用-Wall -Wextra -pedantic或類似方法。

另一個問題是代碼在您的printf()

  printf("index-1 = %lx (sizeof %d)\n", index-1, sizeof(index-1));

讓我們簡化一下:

int i = 100;
print("%lx", i-1);

您告訴printflong但實際上您正在發送int clang確實會告訴您當前的警告(我認為gcc也應該吐出正確的警告)。 看到:

test1.c:6:19: warning: format specifies type 'unsigned long' but the argument has type 'int' [-Wformat]
printf("%lx", i - 100);
        ~~~   ^~~~~~~
        %x   
1 warning generated.

解決方案很簡單:您需要將long傳遞給printf或告訴printf打印int

printf("%lx", (long)(i-100) );
printf("%x", i-100);

您在32bit上運氣不錯,您的應用程序沒有崩潰。 將其移植到64位后,您的代碼中發現了一個錯誤,現在您可以對其進行修復。

對於無符號值,總是按照環繞方式定義算術。 例如(unsigned)-1UINT_MAX相同。 所以像

p + (0u-1)

相當於

p + UINT_MAX

&p[0u-1]等同於&*(p + (0u-1))p + (0u-1) )。

如果我們將指針替換為無符號整數類型,也許這更容易理解。 考慮:

uint32_t p32; // say, this is a 32-bit "pointer"
uint64_t p64; // a 64-bit "pointer"

假設shortintlong分別為16、32和64位(在同一行上的條目相等):

p32 + (unsigned short)-1    p32 + USHRT_MAX     p32 + (UINT_MAX>>16)
p32 + (0u-1)                p32 + UINT_MAX      p32 - 1
p32 + (0ul-1)               p32 + ULONG_MAX     p32 + UINT_MAX          p32 - 1

p64 + (0u-1)                p64 + UINT_MAX
p64 + (0ul-1)               p64 + ULONG_MAX     p64 - 1

您始終可以用最大值+ 1取的等價來替換無符號類型的加,減和乘操作數。例如,

-1☰ffffffff 十六進制 mod 2 32

十六進制 ffffffff為2 32 -1或UINT_MAX ),以及

ffffffffffffffff 十六進制 ☰ffffffff 十六進制 mod 2 32

(對於32位無符號類型,您始終可以將其截斷為最低有效的8個十六進制數字)。

您的示例:

32位

  • unsigned short index = 0;

index - 1 ,索引升為int 結果具有int類型和值-1(為負)。 unsigned char相同。

64位

  • unsigned char index = 0;
  • unsigned short index = 0;

與32位相同。 index提升為intindex - 1為負。

  • unsigned long index = 0;

輸出

index-1 = ffffffff (sizeof 8)

很奇怪,這是%lx唯一正確用法,但看起來已經用%x打印(預期為4個字節); 在我的64位計算機(具有64位long )和%lx我得到:

index-1 = ffffffffffffffff (sizeof 8)

ffffffffffffffff 十六進制是-1模2 64

  • unsigned index = 0;

一個int不能持有unsigned int可以擁有的任何值,因此在index - 1什么都沒有提升為int ,結果的類型為unsigned int且值為-1(這是肯定的 ,與UINT_MAX或ffffffff hex相同,因為該類型為unsigned )。 對於32位地址,添加此值與減去1相同:

    bfbdd5db            bfbdd5db
+   ffffffff          -        1
=  1bfbdd5da
=   bfbdd5da          = bfbdd5da

(請注意環繞/截斷。)但是,對於64位地址:

    00007fff b81d6c21
+            ffffffff
=   00008000 b81d6c20

沒有回繞。 這試圖訪問無效的地址,因此您會遇到段錯誤。

也許可以在Wikipedia上查看2的補碼


在我的64位Linux上,使用指定符來期望32位值同時傳遞64位類型(反之亦然)似乎“可行”,只能讀取32個最低有效位。 但是請使用正確的。 lx期望一個unsigned long ,一個未修改的x一個unsigned inthx一個unsigned short (由於默認的參數提升 ,當傳遞給printf時, unsigned short被提升為int (作為變量參數傳遞))。 size_t的長度修飾符為z ,如%zu

printf("index-1 = %lx (sizeof %zu)\n", (unsigned long)(index-1), sizeof(index-1));

(到unsigned long的轉換不會更改unsigned intunsigned shortunsigned char表達式的值。)

sizeof(index-1)也可以寫為sizeof(+index) ,對表達式大小的唯一影響是通常的算術轉換,也由一元+觸發。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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