使用iostream读取和签名字符时未定义的行为

我的问题与此类似,但更具体一点。 我正在写一个函数来读取使用little endian表示的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而不是charunsigned char必须能够表示范围在0到255之间的所有值。

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

编辑:我所指的未定义行为是由istream::readbuffer而不是从缓冲区转换为unsigned char字符造成的。 例如,如果它是一个符号+幅度机器,并且字符有符号,则0x80为负0,但负0和正0必须始终按照标准相等。 如果是这种情况,那么只有255个不同的符号字符,并且不能用char表示一个字节。 这些强制转换将会起作用,因为当将符号强制转换为无符号时,它总是会将UCHAR_MAX + 1加到负数(草案C ++ 11标准N3242的4.7节)中。


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

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

在非二进制补码系统中,signed char不适合访问对象的表示。 这是因为有两个可能的带符号的char表达式具有相同的值(+0和-0),或者一个表达式没有值(陷阱表示)。 在这两种情况下,这都会阻止你做对于对象表示可能做的最有意义的事情。 例如,如果您有16位无符号整数0x80ff,则其中一个或另一个字节(作为有符号字符)将陷入或比较等于0。

请注意,在这样的实现(非二进制补码)中,plain char需要被定义为无符号类型,以通过char访问对象的表示以正确工作。 虽然没有明确的要求,但我认为这是来自标准中其他要求的要求。

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

对于一般可复制类型T的任何对象(基类子对象除外),无论对象是否保存T类型的有效值,构成对象的基础字节(1.7)都可以复制到char或无符号字符。 如果将char或unsigned char数组的内容复制回对象中,则该对象将随后保持其原始值。

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

链接地址: http://www.djcxy.com/p/23471.html

上一篇: Undefined behaviour when using iostream read and signed char

下一篇: Getting IP address of Logstash