相关疑难解决方法(0)

在C/C++中无符号左移之前掩盖是否过于偏执?

这个问题的动机是我在C/C++中实现加密算法(例如SHA-1),编写可移植平台无关的代码,并彻底避免未定义的行为.

假设标准化的加密算法要求您实现此目的:

b = (a << 31) & 0xFFFFFFFF
Run Code Online (Sandbox Code Playgroud)

where ab是无符号的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 c++ undefined-behavior language-lawyer integer-arithmetic

72
推荐指数
4
解决办法
4844
查看次数

32位无符号乘以64位导致未定义的行为?

所以我有这个代码:

uint32_t s1 = 0xFFFFFFFFU;
uint32_t s2 = 0xFFFFFFFFU;
uint32_t v;
...
v = s1 * s2; /* Only need the low 32 bits of the result */
Run Code Online (Sandbox Code Playgroud)

在以下所有内容中,我假设编译器不能对s1s2仅用于上述示例的初始化器的范围有任何先入之见.

如果我在一个整数大小为32位的编译器上编译它(例如编译x86时),没问题.编译器只是简单地使用s1s2作为uint32_t类型化的值(不能进一步推广它们),并且乘法将简单地给出结果,如注释所示(模数UINT_MAX + 1在这种情况下为0x100000000).

但是,如果我在具有64位整数大小的编译器(例如x86-64)上编译它,则可能会从C标准中推断出未定义的行为.整数提升会看到uint32_t可以提升为int(64位有符号),然后乘法会尝试乘以2 int,如果它们恰好具有示例中显示的值,则会导致整数溢出,这是未定义的行为.

我对此是否正确,如果是这样,你会如何以理智的方式避免它?

我发现了这个类似的问题,但涵盖了C++:什么是最好的C++方式来模块化地安全地无符号整数?.在这里,我想得到一个适用于C的答案(最好是兼容C89).我不会考虑让一台糟糕的32位机器可能执行64位乘法,但这是一个可接受的答案(通常在代码中,这会引起关注,32位性能可能更为关键,因为通常那些机器速度较慢).

注意,当使用具有32位int大小的编译器编译时,同样的问题可以应用于16位无符号整数,或者当使用具有16位int大小的编译器编译时,同样的问题可以应用于无符号字符(后者可能与8位CPU的编译器相同) :C标准要求整数至少为16位,因此符合标准的编译器可能会受到影响).

c undefined-behavior

41
推荐指数
2
解决办法
1993
查看次数

将模块化安全地乘以无符号整数的最佳C++方法是什么?

比方说,你正在使用<cstdint>和类型,如std::uint8_tstd::uint16_t,和想要做的操作,如+=*=他们.你喜欢对这些数字进行算术运算,就像在C/C++中一样.这通常工作,你会发现与实验工作std::uint8_t,std::uint32_tstd::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)

c++ portability multiplication undefined-behavior

30
推荐指数
3
解决办法
2945
查看次数

当我使用 int32_t 循环变量时,为什么我的 for 循环不会停止?

i在我的程序中,我发现当is时循环无法正确退出int32_t。看起来像是整数溢出,并且远大于10,并且循环不会停止。请告诉我发生了什么以及如何在大型项目中避免此错误。

#include <iostream>
#include <stdint.h>
int f(int n){

    for (int32_t i = 0; i < 10; ++i)
    {
        int64_t time = 4500000000 +  (i) * 500000000;
        std::cout << time<< " i: " << i << std::endl;

    }
    return 0;
}

int main ()
{
    return f(10);
}
Run Code Online (Sandbox Code Playgroud)

代码链接

c++

13
推荐指数
1
解决办法
1239
查看次数

C++中的固定宽度整数文字?

C++ 11首先引入了通过用户定义的文字将新文本定义为C++的支持.C++ 11或更高版本是否也为类型中的固定宽度整数文字预定义后缀<cstdint>

c++ language-lawyer user-defined-literals c++11 c++14

11
推荐指数
1
解决办法
1672
查看次数