这个问题的动机是我在C/C++中实现加密算法(例如SHA-1),编写可移植平台无关的代码,并彻底避免未定义的行为.
假设标准化的加密算法要求您实现此目的:
b = (a << 31) & 0xFFFFFFFF
Run Code Online (Sandbox Code Playgroud)
where a和b是无符号的32位整数.请注意,在结果中,我们丢弃高于最低32位的任何位.
作为第一个天真的近似,我们可以假设int在大多数平台上都是32位宽,所以我们写:
unsigned int a = (...);
unsigned int b = a << 31;
Run Code Online (Sandbox Code Playgroud)
我们知道这个代码无处不在,因为int在某些系统上是16位宽,在其他系统上是64位,甚至可能是36位.但是使用stdint.h,我们可以使用以下uint32_t类型改进此代码:
uint32_t a = (...);
uint32_t b = a << 31;
Run Code Online (Sandbox Code Playgroud)
所以我们完成了,对吧?这就是我多年来的想法.... 不完全的.假设在某个平台上,我们有:
// stdint.h
typedef unsigned short uint32_t;
Run Code Online (Sandbox Code Playgroud)
在C/C++中执行算术运算的规则是,如果类型(例如short)比类型更窄int,那么int如果所有值都适合,则它会变宽,unsigned int否则.
假设编译器定义short为32位(带符号)和int48位(带符号).然后这些代码行:
uint32_t a = (...);
uint32_t b = a << …Run Code Online (Sandbox Code Playgroud) 我有一个用于在C中实现的编程语言的VM.它支持在32位和64位架构以及C和C++下编译.
我正在尝试使用尽可能多的警告来干净地编译它.当我打开时CLANG_WARN_IMPLICIT_SIGN_CONVERSION,我会得到一连串的新警告.
我希望有一个很好的策略,可以使用何时使用int显式无符号类型和/或显式大小的类型.到目前为止,我无法确定该策略应该是什么.
将它们混合使用 - 主要int用于局部变量和参数之类的东西,并在结构中使用较窄的类型- 确实会导致许多隐式转换问题.
我喜欢为struct字段使用更具体的大小类型,因为我喜欢显式控制堆中对象的内存使用量的想法.此外,对于散列表,我在散列时依赖于无符号溢出,因此如果散列表的大小存储为,则很好uint32_t.
但是,如果我尝试在任何地方使用更具体的类型,我发现自己到处都是曲折的迷宫.
其他C项目有什么作用?
比方说,你正在使用<cstdint>和类型,如std::uint8_t和std::uint16_t,和想要做的操作,如+=和*=他们.你喜欢对这些数字进行算术运算,就像在C/C++中一样.这通常工作,你会发现与实验工作std::uint8_t,std::uint32_t和std::uint64_t,但不std::uint16_t.
具体而言,乘法std::uint16_t有时会失败,优化的构建会产生各种奇怪的结果.原因?由于有符号整数溢出导致的未定义行为.编译器基于未发生未定义行为的假设进行优化,因此开始从程序中修剪代码块.具体的未定义行为如下:
std::uint16_t x = UINT16_C(0xFFFF);
x *= x;
Run Code Online (Sandbox Code Playgroud)
原因在于C++的推广规则以及你和其他几乎所有人一样使用平台的事实std::numeric_limits<int>::digits == 31.也就是说,int是32位(digits计数位但不是符号位). 尽管是无符号的,但是x被提升为32位带符号算术的溢出.signed int0xFFFF * 0xFFFF
演示一般问题:
// Compile on a recent version of clang and run it:
// clang++ -std=c++11 -O3 -Wall -fsanitize=undefined stdint16.cpp -o stdint16
#include <cinttypes>
#include <cstdint>
#include <cstdio>
int main()
{
std::uint8_t a = UINT8_MAX; …Run Code Online (Sandbox Code Playgroud) 我认为2的补码的重点是对于有符号和无符号数字的操作可以采用相同的方式.维基百科甚至特别列出了多重作为其中一项有益的操作.那么为什么x86对每个都有单独的指令,mul并且imul?x86-64仍然如此吗?
对于像这样的固定宽度整数类型的整数文字是否有一些 c++ 建议?
// i's type is unsigned int
auto i = 10u;
// j's type is uint32_t
auto j = 10u32;
Run Code Online (Sandbox Code Playgroud) 在C99中,有一些(可选的)类型int8_t,int16_t例如,它们保证具有精确指定的宽度和没有填充位,并且表示二进制补码(7.18.1.1)中的数字.在6.2.6.2中,有符号整数溢出被提及为脚注44)和45),即它可能导致在填充位中捕获值.
由于intN_t没有任何填充位,并且它们保证是两个补码,这是否意味着它们的溢出不会产生任何未定义的行为?什么是例如溢出乘法的结果?添加怎么样?2^N对于无符号类型,结果是否减少了模数?
在64位读取32位无符号乘法后导致未定义的行为?关于StackOverflow的问题,我开始思考根据C99标准,对小型无符号类型的典型算术运算是否会导致未定义的行为.
例如,请使用以下代码:
#include <limits.h>
...
unsigned char x = UCHAR_MAX;
unsigned char y = x + 1;
Run Code Online (Sandbox Code Playgroud)
的x变量被初始化为最大量值unsigned char的数据类型.下一行是问题:值x + 1大于UCHAR_MAX且不能存储在unsigned char变量中y.
我相信以下是实际发生的事情.
x首先被提升为数据类型int(第6.3.1.1/2节),然后x + 1被评估为数据类型int.假设有一个实现在哪里INT_MAX并且UCHAR_MAX是相同的 - x + 1会导致有符号整数溢出.这是否意味着递增变量x,尽管是无符号整数类型,由于可能的有符号整数溢出会导致未定义的行为?