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

Why is GCC 11 compiler producing weird output when optimization is enabled?

请看一下这段代码:

#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 节列出了可能出现混叠的情况:

An object shall have its stored value accessed only by an lvalue expression that has one of the following types:88)

  • a type compatible with the effective type of the object,
  • a qualified version of a type compatible with the effective type of the object,
  • a type that is the signed or unsigned type corresponding to the effective type of the object,
  • a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
  • an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
  • a character type.

88 ) The intent of this list is to specify those circumstances in which an object may or may not be aliased

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

特别是对于 gcc,除非您使用 -O2 或更高版本,或者明确使用 -fstrict-aliasing,否则不会启用严格别名规则的实施。通过强制执行此规则,编译器能够进行其他方式无法进行的优化。