如果我有:
unsigned int x;
x -= x;
Run Code Online (Sandbox Code Playgroud)
很明显,x 应该这样表达后是零,但到处看,他们说的行为,这种代码是不确定的,而不是仅仅值x(直到减法之前).
两个问题:
这段代码的行为确实未定义吗?
(例如,代码在兼容系统上崩溃[或更糟]?)
如果是这样,为什么 C表示行为是未定义的,当非常清楚x这里应该为零时?
即不在此定义行为给出的优势是什么?
很明显,编译器可以简单地使用它在变量中认为"方便"的任何垃圾值,并且它可以按预期工作......这种方法有什么问题?
乍一看,这个问题看起来像是如何检测整数溢出的重复?然而,它实际上是显着不同的.
我发现虽然检测无符号整数溢出非常简单,但在C/C++中检测带符号的溢出实际上比大多数人想象的要困难.
最明显但又天真的方式是这样的:
int add(int lhs, int rhs)
{
int sum = lhs + rhs;
if ((lhs >= 0 && sum < rhs) || (lhs < 0 && sum > rhs)) {
/* an overflow has occurred */
abort();
}
return sum;
}
Run Code Online (Sandbox Code Playgroud)
这个问题是根据C标准,有符号整数溢出是未定义的行为. 换句话说,根据标准,只要您甚至导致签名溢出,您的程序就像取消引用空指针一样无效.因此,您不能导致未定义的行为,然后尝试在事后检测溢出,如上面的后置条件检查示例.
尽管上面的检查很可能适用于许多编译器,但你不能指望它.实际上,因为C标准说未定义有符号整数溢出,所以一些编译器(如GCC)将在设置优化标志时优化上述检查,因为编译器假定有符号溢出是不可能的.这完全打破了检查溢出的尝试.
因此,检查溢出的另一种可能方法是:
int add(int lhs, int rhs)
{
if (lhs >= 0 && rhs >= 0) {
if (INT_MAX - lhs <= rhs) {
/* overflow …Run Code Online (Sandbox Code Playgroud) 为什么此代码不打印相同的数字?:
long long a, b;
a = 2147483647 + 1;
b = 2147483648;
printf("%lld\n", a);
printf("%lld\n", b);
Run Code Online (Sandbox Code Playgroud)
我知道 int 变量的最大数量是 2147483647,因为 int 变量是 4 字节。但据我所知,long long 变量是 8 字节,但为什么这段代码会这样呢?
众所周知,有符号整数溢出是未定义的行为.但是C++ 11 cstdint文档中有一些有趣的东西:
有符号整数类型,宽度分别为8,16,32和64位,没有填充位,负值使用2的补码(仅当实现直接支持该类型时提供)
这里是我的问题:由于标准明确地说,对int8_t,int16_t,int32_t和int64_t负数是2的补,仍然是这些类型的未定义行为的泛滥?
编辑我检查了C++ 11和C11标准,这是我发现的:
C++ 11,§18.4.1:
标题定义了所有函数,类型和宏,与C标准中的7.20相同.
C11,§7.20.1.1:
typedef名称
intN_t指定有符号整数类型,其宽度为N,无填充位和二进制补码表示.因此,int8_t表示这样的带符号整数类型,其宽度恰好为8位.
考虑这个程序:
#include <stdio.h>
int main(void)
{
unsigned int a;
printf("%u %u\n", a^a, a-a);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
是不确定的行为?
从表面上看,它a是一个未初始化的变量.所以这指向未定义的行为.但是a^a并且a-a等于0所有的价值a,至少我认为是这样的.有可能有某种方式来证明行为是明确定义的吗?
我有以下代码:
#include <iostream>
#include <complex>
using namespace std;
int main() {
complex<int> delta;
complex<int> mc[4] = {0};
for(int di = 0; di < 4; di++, delta = mc[di]) {
cout << di << endl;
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我希望它输出"0,1,2,3"并停止,但它会输出一系列无穷无尽的"0,1,2,3,4,5 ......"
看起来比较di<4效果不好并且总是返回true.
如果我只是评论出来,delta=mc[di],我会像往常一样得到"0,1,2,3".无辜任务有什么问题?
我正在使用带有-O2选项的Ideone.com g ++ C++ 14.
这个问题的动机是我在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,但讨论已经转向C++标准所指定的内容,即使用 a 访问多维数组的元素时构成未定义行为的内容。功能类似于std::memcpy.
首先,这是该问题的代码,已转换为 C++ 并const尽可能使用:
#include <iostream>\n#include <cstring>\n\nvoid print(const int arr[][3], int n)\n{\n for (int r = 0; r < 3; ++r) {\n for (int c = 0; c < n; ++c) {\n std::cout << arr[r][c] << " ";\n }\n std::cout << std::endl;\n }\n}\n\nint main()\n{\n const int arr[3][3] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };\n int arr_copy[3][3];\n print(arr, 3);\n std::memcpy(arr_copy, arr, sizeof arr);\n …Run Code Online (Sandbox Code Playgroud) c c++ multidimensional-array undefined-behavior language-lawyer
int i = i;
int main() {
int a = a;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
int a = a肯定有未定义的行为 (UB),关于它的更多细节在
读取未初始化的值总是未定义的行为吗?或者有例外吗?.
但是呢int i = i?在 C++ 中,我们可以为全局变量分配非常量值。i在遇到声明之前声明并零初始化(因为它具有文件范围)。在这种情况下,我们将0在定义的后面分配给它。可以说这没有 UB 吗?
我有一个int x。为简单起见,假设ints 占据范围 -2^31 到 2^31-1。我想计算2*x-1. 我允许x为任何值 0 <= x<= 2^30。如果我计算 2*(2^30),我会得到 2^31,这是整数溢出。
一种解决方案是计算2*(x-1)+1. 比我想要的多了一项减法,但这不应该溢出。但是,编译器会将其优化为2*x-1. 这是源代码的问题吗?这是可执行文件的问题吗?
这是 Godbolt 的输出2*x-1:
func(int): # @func(int)
lea eax, [rdi + rdi]
dec eax
ret
Run Code Online (Sandbox Code Playgroud)
这是 Godbolt 的输出2*(x-1)+1:
func(int): # @func(int)
lea eax, [rdi + rdi]
dec eax
ret
Run Code Online (Sandbox Code Playgroud) c++ integer-overflow compiler-optimization undefined-behavior integer-arithmetic