请查看以下代码:

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

int main()
{
    uint8_t run = 1;

    /* Define variable of interest and set to random value */
    uint32_t dwTime = 0xdeadc0de;

    /* Define uint16_t pointer */
    uint16_t* tmpU16;
    /* Assign least 16 bits from dwTime to tmpU16 by downcasting from uint32_t to uint16_t */
    tmpU16 = (uint16_t*)&dwTime;

    /* Print content of tmpU16 */
    fprintf(stderr, "TEST: %04x\n", *tmpU16); /* Will print "TEST: c0de" here */

    /* This loop will run exactly once */
    while (run)
    {
        /* Print content of tmpU16 AGAIN (content of tmpU16 should not have changed here!) */
        /* Will print "TEST: 0000" here when optimization is enabled */
        /* Will print "TEST: c0de" here when optimization is disabled */
        fprintf(stderr, "TEST: %04x\n", *tmpU16);

        /* Increment tmpU16 pointer by 1 (yes, this does not make sense but it should not do any harm
        either, unless something gets written to its address AFTER incrementing */
        tmpU16++;
        run--;
    }

    return 0;
}

当使用GCC 11编译此代码并启用优化(例如-O2)时,它将产生以下输出:

TEST: c0de
TEST: 0000

当编译此代码时,没有进行优化(-O0),它将生成:

TEST: c0de
TEST: c0de

我假设在执行第一个fprintf之后,while循环中的下一个fprintf将立即执行.在这两个fprintf之间什么都不应该发生,因此tmpU16的内容保持不变.

然而,当我注释掉tmpU16++指针增量时,它会产生正确的优化输出.

为什么,这里发生了什么?

推荐答案

在这一行:

tmpU16 = (uint16_t*)&dwTime;

通过将一种类型的对象视为另一种不兼容的类型,您犯了严格的别名冲突.

C standard条中的第6.5p7节列出了可能出现混叠的条件:

对象的存储值只能由左值访问

  • 与对象的有效类型兼容的类型,
  • a qualified version of 与对象的有效类型兼容的类型,
  • 与对象的有效类型相对应的有符号或无符号类型,
  • 一种类型,它是与对象的有效类型的限定版本相对应的有符号或无符号类型,
  • 一种聚合或联合类型,其成员(递归地包括
  • 角色类型.

88)该列表的目的是指定在哪些情况下对象可能会或可能不会出现别名

使用指向uint16_t的指针访问uint32_t不属于这些类别.这样的违规行为会触发undefined behavior次.

特别是在gcc中,除非使用-O2或更大,或者显式使用-fstrict-aliasing,否则无法启用严格的别名规则.通过实施这一规则,编译器可以进行优化,否则就无法进行优化.

C相关问答推荐

为什么 memcpy() 随机不复制正确的值?

为什么启用优化时 GCC 11 编译器会产生奇怪的输出?

Gnuplot 和 C - 绘制不同的符号/ colored颜色

在 C 中使用数组而不是向量

幂函数给出的答案与 C 中的 math.pow 函数不同

确定在嵌入式 C 中运行时使用哪个变量

在编译时构建静态数组

判断由大括号组成的输入字符串是否格式正确

通过默认网关地址的硬件地址而不是以太网多播地址发送多播

为什么 malloc() 被认为是库调用而不是系统调用?

为什么使用 MOV 指令将 XOR 交换优化为普通交换?