繁体   English   中英

C99中的别名,类型修剪,并集,结构和位字段

[英]Aliasing, Type-punning, Unions, Structs and Bit Fields in C99

在收到以下对这个问题的回答的陈述后:

...您正在尝试覆盖valuebits ,并将数据填充到并集的一种替代方法中,然后将其从另一种方法中移除不确定的

我对在C99中允许进行类型修饰的内容(以及什么是审慎的内容)更加好奇。 环顾四周后,我发现在文章《通过C99中未指定的联合体进行类型操作》中有很多有用的信息。

此处发布的评论和答案都需要很多帮助。 为了清楚起见(以及进行完整性检查),我想基于对C99标准的理解来创建一个示例。 下面是我创建的示例代码,尽管它按预期运行,但我想确定我的断言是正确的。

以下代码在注释中包含了我的断言。 这是我对C99中的类型修饰的理解。 我的评论正确吗? 如果没有,请您解释一下原因?

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

#define NUM_BYTES   sizeof(uint32_t)
typedef union
{
    uint32_t fourByteValue;
    uint8_t  byteValue[NUM_BYTES];
    struct
    {
        unsigned int firstBitSpecified  :   1;
        unsigned int secondBitSpecified :   1;
        unsigned int thirdBitSpecified  :   1;
        unsigned int fourthBitSpecified :   1;
        unsigned int paddingBits        :   4;
        uint8_t  oneByteStructValue;
        uint16_t twoByteStructValue;
    };
} U;

int main (void)
{
    const char border[] = "==============================\n";
    U myUnion;
    uint8_t byte;
    uint32_t fourBytes;
    uint8_t i;

    myUnion.fourByteValue = 0x00FFFFFF;
    fourBytes = myUnion.fourByteValue;  /* 1. This is not type-punning. */
    printf("No type-punning fourByteValue:\n%s"
           "fourBytes\t= 0x%.4x\n\n", border, fourBytes);


    printf("Type-punning byteValue:\n%s", border);
    for (i = 0; i < NUM_BYTES; i++)
    {
        byte = myUnion.byteValue[i];   /* 2. Type-punning allowed by C99, 
                                             no unspecified values. */
        printf ("byte[%d]\t\t= 0x%.2x\n", i, byte);
    }
    printf("\n");

    myUnion.byteValue[3] = 0xff;
    fourBytes = myUnion.fourByteValue; /* 3. Type-punning allowed by C99 
                                             but all other 'byteValue's
                                             are now unspecified values. */
    printf("Type-punning fourByteValue:\n%s"
           "fourBytes\t= 0x%.4x\n\n", border, fourBytes);

    myUnion.firstBitSpecified = 0;
    myUnion.thirdBitSpecified = 0;
    fourBytes = myUnion.fourByteValue; /* 4. Again, this would be allowed, but 
                                             the bit that was just assigned
                                             a value of 0 is implementation
                                             defined AND all other bits are
                                             unspecified values. */
    printf("Type-punning firstBitSpecified:\n%s"
           "fourBytes\t= 0x%.4x\n\n", border, fourBytes);

    myUnion.fourByteValue = 0x00000001;
    fourBytes = myUnion.firstBitSpecified; /* 5. Type-punning allowed, although
                                                 which bit you get is implementation
                                                 specific. */
    printf("No type-punning, firstBitSpecified:\n%s"
           "fourBytes\t= 0x%.4x\n\n", border, fourBytes);
    fourBytes = myUnion.secondBitSpecified;
    printf("No type-punning, secondBitSpecified:\n%s"
           "fourBytes\t= 0x%.4x\n\n", border, fourBytes);

    return (EXIT_SUCCESS);
}

上面的代码是在64位Windows 7计算机上使用mingw32-gcc.exe -Wall -g -std=c99编译的。 运行代码后,我收到以下输出:

No type-punning fourByteValue:
==============================
fourBytes       = 0xffffff

Type-punning byteValue:
==============================
byte[0]         = 0xff
byte[1]         = 0xff
byte[2]         = 0xff
byte[3]         = 0x00

Type-punning fourByteValue:
==============================
fourBytes       = 0xffffffff

Type-punning firstBitSpecified:
==============================
fourBytes       = 0xfffffffa

No type-punning, firstBitSpecified:
==============================
fourBytes       = 0x0001

No type-punning, secondBitSpecified:
==============================
fourBytes       = 0x0000

我对那篇文章中链接的脚注的阅读是, 从未指定通过联合进行类型拼写。 从去 ,标准说:

除了一个例外,如果在将值存储到对象的其他成员之后访问并集对象的成员,则该行为是实现定义的。

脚注不会改变这一点。 出现这种情况的原因是,C不能保证(a)数字类型的字节顺序或(b) struct成员的内存顺序,除非第一个成员必须是字节对齐的到了“开始” struct (这样你可以做的排序铸造,他们在做GTK实现多态)。

有问题的脚注针对这一行:

当值存储在联合类型的对象的成员中时,与该成员不对应但与其他成员相对应的对象表示形式的字节采用未指定的值,但联合对象的值不应因此成为陷阱表示

它说:

78a如果用于访问并集对象内容的成员与上次用于在对象中存储值的成员不同,则该值的对象表示的适当部分将重新解释为新类型的对象表示如6.2.6(有时称为“类型校正”的过程)所述。 这可能是陷阱表示。

“重新解释为新类型中的对象表示形式”是由实现定义的(因为在逐字节级别上对所有类型的解释始终是实现的定义,同时考虑了字节序等)。 脚注只是增加了更多细节,以指出当您通过联合对类型系统进行弄乱时,可能会发生令人惊讶的事情,包括引起陷阱表示。 在这里寻找“陷阱表示形式”的定义:

陷阱表示是一组位,当将其解释为特定类型的值时,将导致未定义的行为。 陷阱表示形式最常见于浮点数和指针值,但从理论上讲,几乎任何类型都可以使用陷阱表示形式。 未初始化的对象可能包含陷阱表示。 这具有与旧规则相同的行为:对未初始化对象的访问将产生未定义行为。

该标准提供的有关访问未初始化数据的唯一保证是, 无符号字符类型没有陷阱表示,并且填充没有陷阱表示。

因此,通过在帖子中用unsigned char替换uint_8 ,可以避免未定义的行为,并最终实现特定于行为。 但是,如现在所写,该标准并不禁止UB。

在您链接的帖子中的引号中明确指出了这一点:

最后,从C90到C99的更改之一是,消除了在最后一个存储库移到另一个存储库时访问工会的一个成员的任何限制。 理由是行为将取决于值的表示形式。

根据定义,底层表示从未由标准定义。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM