标签: undefined-behavior

(为什么)使用未初始化的变量未定义行为?

如果我有:

unsigned int x;
x -= x;
Run Code Online (Sandbox Code Playgroud)

很明显,x 应该这样表达后是零,但到处看,他们说的行为,这种代码是不确定的,而不是仅仅值x(直到减法之前).

两个问题:

  • 这段代码的行为确实未定义吗?
    (例如,代码在兼容系统上崩溃[或更糟]?)

  • 如果是这样,为什么 C表示行为是未定义的,当非常清楚x这里应该为零时?

    即不在此定义行为给出的优势是什么?

很明显,编译器可以简单地使用它在变量中认为"方便"的任何垃圾值,并且它可以按预期工作......这种方法有什么问题?

c initialization undefined-behavior

77
推荐指数
6
解决办法
1万
查看次数

检测C/C++中的带符号溢出

乍一看,这个问题看起来像是如何检测整数溢出的重复然而,它实际上是显着不同的.

我发现虽然检测无符号整数溢出非常简单,但在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)

c c++ signed integer-overflow undefined-behavior

75
推荐指数
6
解决办法
2万
查看次数

为什么 long long 2147483647 + 1 = -2147483648?

为什么此代码不打印相同的数字?:

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 c++ integer-overflow undefined-behavior twos-complement

75
推荐指数
3
解决办法
7690
查看次数

在C++中有符号整数溢出仍然是未定义的行为吗?

众所周知,有符号整数溢出是未定义的行为.但是C++ 11 cstdint文档中有一些有趣的东西:

有符号整数类型,宽度分别为8,16,32和64位,没有填充位,负值使用2的补码(仅当实现直接支持该类型时提供)

见链接

这里是我的问题:由于标准明确地说,对int8_t,int16_t,int32_tint64_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位.

c++ integer-overflow undefined-behavior c++11

74
推荐指数
2
解决办法
2万
查看次数

如果a未初始化,是^ a还是aa未定义的行为?

考虑这个程序:

#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,至少我认为是这样的.有可能有某种方式来证明行为是明确定义的吗?

c undefined-behavior

74
推荐指数
2
解决办法
3332
查看次数

C++编译错误?

我有以下代码:

#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++ gcc undefined-behavior

74
推荐指数
4
解决办法
5433
查看次数

在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
查看次数

使用“memcpy”复制二维数组在技术上是未定义的行为吗?

在对这个最近问题的评论中出现了一个有趣的讨论:现在,虽然那里的语言是C,但讨论已经转向C++标准所指定的内容,即使用 a 访问多维数组的元素时构成未定义行为的内容。功能类似于std::memcpy.

\n

首先,这是该问题的代码,已转换为 C++ 并const尽可能使用:

\n
#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

70
推荐指数
4
解决办法
6066
查看次数

在 C++ 中,用自身初始化全局变量是否具有未定义的行为?

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 吗?

c++ undefined-behavior language-lawyer

64
推荐指数
2
解决办法
4029
查看次数

编译器优化可能会导致整数溢出。可以吗?

我有一个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

64
推荐指数
4
解决办法
7744
查看次数