访问带有char *的int可能具有未定义的行为吗?

时间:2018-02-01 19:14:43

标签: c casting language-lawyer undefined-behavior

下面用于测试字节顺序的代码应该具有实现定义的行为:

int is_little_endian(void) {
    int x = 1;
    char *p = (char*)&x;
    return *p == 1;
}

但它是否有可能在故意设计的架构上有不确定的行为?例如,具有值int(或另一个选择好的值)的1表示的第一个字节是char类型的陷阱值吗?

如评论中所述,类型unsigned char不会出现此问题,因为它不能包含陷阱值,但此问题特别涉及char类型。

3 个答案:

答案 0 :(得分:7)

Per C 2018 6.2.5 15,char表现为signed charunsigned char。假设它是signed char。 6.2.6.2 2讨论了有符号整数类型,包括signed char。在本段末尾,它说:

  

这些[符号和幅度两个补充一些补充]中的哪一个适用于实现定义,无论是符号位1的值和所有值位0(前两个),或符号位和所有值位1(对于补码),是陷阱表示或正常值。

因此,该段允许signed char具有陷阱表示。但是,标准中用于访问陷阱表示的段落可能具有未定义的行为,6.2.6.1 5,特别是排除了字符类型:

  

某些对象表示不需要表示对象类型的值。如果对象的存储值具有这样的表示并且由不具有字符类型的左值表达式读取,则行为是未定义的。如果这样的表示是由副作用产生的,该副作用通过不具有字符类型的左值表达式修改对象的全部或任何部分,则行为是未定义的。这种表示称为陷阱表示。

因此,虽然char可能有陷阱表示,但我们没有理由不能访问它。那么,如果我们在表达式中使用值,会出现什么问题?如果char具有陷阱表示,则它不表示值。因此,尝试将其与*p == 1中的1进行比较似乎没有明确的行为。

对于任何正常的C实现,int中的特定值1不会导致char中的陷阱表示,因为1将位于“最右边”(最低值)位int的某个字节,并且没有正常的C实现将char的符号位放在该位的位中。但是,C标准显然没有禁止这样的安排,因此,理论上,值为1的int可能在其字节之一中使用位00000001进行编码,这些位可能是{char的陷阱表示。 1}}。

答案 1 :(得分:4)

我不认为标准会禁止signed char使用符号数量级或实现符号格式的实现,并且试图加载代表&#的位模式34;负零"。它也不要求此类实现必须使char无符号。有可能设计一个代码可能具有任意行为的体系结构。还有一些需要注意的重要事项:

  1. 无法保证char中的位映射的顺序与int中的位相同。如果比特没有按顺序映射,则代码不会启动到UB-land,但结果不会很有意义。

  2. 据我所知,每个非人为的符合C99的实现都使用了两种补码格式;我认为任何人都不会这样做是值得怀疑的。

  3. 实现使char成为比位模式具有更少可表示值的类型将是愚蠢的。

  4. 如果存在一些可以按标准定义的方式处理的源文本,那么可以设计一个几乎可以对任何源文本执行任何操作的符合实现。

  5. 可以设想符合标志幅度的实现,其中整数值1将具有将编码有符号字符值的位模式"负零",并且将在尝试加载时捕获。人们甚至可以设计一个符合要求的实现方式(在" int"类型上有很多填充位,所有这些都在存储值时被设置" 1&#34 )。鉴于可以设计一个使用One Program规则的符合要求的实现来证明用上述源文本做任何事情都是合理的,无论它使用什么整数格式,我都不会想到奇怪的可能性{{1}类型应该是一个担心。

    请注意,顺便说一句,标准没有努力禁止愚蠢的实施;可以通过添加语言来改进它,char必须是没有陷阱表示的双补码类型或无符号类型,并且要求char或明确说明不需要。如果它认识到一类不能支持类似signed char的类型的实现,那么它也可能会得到改进[这将是一个36位的补充系统的主要绊脚石,并且可能是这种平台不存在符合C99实现的原因]。

答案 2 :(得分:1)

我从标准中找到了一个引用,证明没有对象表示是unsigned char的陷阱值:

6.2.6.2整数类型

  

1对于除 unsigned char之外的无符号整数类型,对象的位   表示应分为两组:值位和填充位(需要   不是后者中的任何一个)。如果有N个值位,则每个位应表示不同的值   2的功率在1和2N-1之间,因此该类型的物体应具备能力   使用纯二进制表示来表示从0到2N - 1的值;这应该是   称为价值表示。任何填充位的值都未指定.53)

前面说过unsigned char不能有任何填充位。

以下脚注表示填充位可用于陷阱表示。

  

53)填充位的某些组合可能会生成陷阱表示,例如,如果有一个填充   bit是奇偶校验位。无论如何,对有效值的算术运算都不会产生陷阱   除了作为溢出等异常情况的一部分之外的表示,这不会发生   使用无符号类型。填充比特的所有其他组合是替代对象表示   值位指定的值。

所以我猜答案是char不保证没有任何陷阱值,但unsigned char是。