gpt4 book ai didi

c++ - 使用 iostream read 和 signed char 时的未定义行为

转载 作者:可可西里 更新时间:2023-11-01 18:38:29 26 4
gpt4 key购买 nike

我的问题类似于this但更具体一点。我正在编写一个函数来从使用小端表示的 istream 中读取一个 32 位无符号整数。在 C 中,这样的事情会起作用:

#include <stdio.h>
#include <inttypes.h>

uint_least32_t foo(FILE* file)
{
unsigned char buffer[4];
fread(buffer, sizeof(buffer), 1, file);

uint_least32_t ret = buffer[0];
ret |= (uint_least32_t) buffer[1] << 8;
ret |= (uint_least32_t) buffer[2] << 16;
ret |= (uint_least32_t) buffer[3] << 24;
return ret;
}

但是如果我尝试使用 istream 做类似的事情,我会遇到我认为未定义的行为

uint_least32_t bar(istream& file)
{
char buffer[4];
file.read(buffer, sizeof(buffer));

// The casts to unsigned char are to prevent sign extension on systems where
// char is signed.
uint_least32_t ret = (unsigned char) buffer[0];
ret |= (uint_least32_t) (unsigned char) buffer[1] << 8;
ret |= (uint_least32_t) (unsigned char) buffer[2] << 16;
ret |= (uint_least32_t) (unsigned char) buffer[3] << 24;
return ret;
}

在 char 有符号且没有二进制补码的系统上,这是未定义的行为,它不能表示数字 -128,因此它不能表示 256 个不同的字符。在 foo 中,即使 char 已签名,它也能正常工作,因为 C11 标准(草案 N1570)的第 7.21.8.1 节说 fread 使用 unsigned char not charunsigned char 必须能够表示 0 到 255(含)范围内的所有值。

当尝试读取数字 0x80 时,bar 是否真的会导致未定义的行为?如果是,是否还有仍然使用 std::istream 的解决方法>?

编辑: 我指的未定义行为是由 istream::readbuffer 而不是从 buffer 到 unsigned 的转换引起的字符。例如,如果它是一个 sign+magnitude 机器并且 char 是有符号的,那么 0x80 是负 0,但是根据标准负 0 和正 0 必须始终比较相等。如果是这种情况,那么只有 255 个不同的带符号字符,您不能用字符表示一个字节。转换会起作用,因为它总是将 UCHAR_MAX + 1 添加到负数(草案 C++11 标准 N3242 的第 4.7 节)时将有符号转换为无符号。

最佳答案

我想我有答案了:bar 不会导致未定义的行为。

在这个 question 的接受答案中, R.. 说:

On a non-twos-complement system, signed char will not be suitable for accessing the representation of an object. This is because either there are two possible signed char representations which have the same value (+0 and -0), or one representation that has no value (a trap representation). In either case, this prevents you from doing most meaningful things you might do with the representation of an object. For example, if you have a 16-bit unsigned integer 0x80ff, one or the other byte, as a signed char, is going to either trap or compare equal to 0.

Note that on such an implementation (non-twos-complement), plain char needs to be defined as an unsigned type for accessing the representations of objects via char to work correctly. While there's no explicit requirement, I see this as a requirement derived from other requirements in the standard.

这似乎是因为 C++11(草案 N3242)第 3.9 节第 2 段说:

For any object (other than a base-class subobject) of trivially copyable type T, whether or not the object holds a valid value of type T, the underlying bytes (1.7) making up the object can be copied into an array of char or unsigned char. If the content of the array of char or unsigned char is copied back into the object, the object shall subsequently hold its original value.

如果 char 被签名并且有多个对象表示某个值(例如 0 in sign+magnitude)然后如果对象被复制到一个 char 数组然后返回到对象中,它可能不会后记具有相同的值,因为 char 数组可以更改为不同的对象表示形式。这与上面的引用相矛盾,所以如果机器的 signed char 有多个对象表示相同的值表示,那么 char 必须是无符号的(例如,在 sign+value 机器上都是 0x80而 0x00 代表 0)。这意味着 bar 是已定义的行为,因为它是未定义行为的唯一情况是要求 char 已签名并且具有奇怪的表示形式,无法满足上述引用标准。

关于c++ - 使用 iostream read 和 signed char 时的未定义行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26967511/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com