![](/img/trans.png)
[英]unsigned int(32bit) to unsigned long long (64bit)
[英]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);
您告訴printf
的long
但實際上您正在發送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)-1
與UINT_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"
假設short
, int
和long
分別為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
提升為int
, index - 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 int
, hx
一個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 int
, unsigned short
或unsigned char
表達式的值。)
sizeof(index-1)
也可以寫為sizeof(+index)
,對表達式大小的唯一影響是通常的算術轉換,也由一元+
觸發。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.