是否允许编译器删除结构成员的赋值,如果它没有明显使用?

use*_*348 6 c standards-compliance

请考虑以下代码:

char buffer[256];

struct stX
{
    int a;
    int b;
    int c;
};

void * my_memcpy ( void * destination, const void * source, size_t num );

int main()
{
    struct stX x;
    x.a = 1;
    x.b = 2;
    x.c = 3;
    my_memcpy(buffer, &x.b, 2 * sizeof(int));
    {
        int i;
        for (i = 0; i < 2 * sizeof(int); ++i) {
            printf("%d\n", buffer[i]);
        }
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

嵌入式系统的特定编译器决定删除对xa和xc的赋值(因为它们从未使用过(至少不是很明显)).这是c标准允许的优化吗?

当然,在包含的赋值中将结构实例定义为volatile.

gcc和msvc不执行此优化(但这不是一个真正的推理).

更新:正如一些答案(正确)假设的那样,编译器可以根据已知的memcpy定义进行优化,但是,这不是我的特定实现所做的.(它保留了堆栈结构的内存,只是不执行赋值.)假设我们用一个编译器没有定义的函数替换了memcpy.另外,假设在函数调用之后使用缓冲区.我相应地更新了上面的示例代码.

Oli*_*rth 3

是的,只要应用程序的可观察行为不改变,编译器就可以自由地执行任何操作。*

但这假设您有一个符合要求的程序,即具有明确定义的行为的程序。您的代码没有表现出明确定义的行为;x.c通过取消引用指针来访问是无效的x.b(这是您隐式要求memcpy做的)。

更新:以上段落可能不正确;请参阅评论中的讨论...


* 更严格的定义参见C99标准第5.1.2.3节:

访问易失性对象、修改对象、修改文件或调用执行任何这些操作的函数都是副作用,即执行环境状态的变化。

...

如果实际的实现可以推断出其值未被使用并且不会产生所需的副作用,则不需要计算表达式的一部分

  • 我不同意。提问者要求“memcpy”访问从“xb”地址开始的“2*sizeof(int)”字节。传递给 memcpy 的 void* 的派生并不重要,重要的是它指向对象 x 的中间(以及对象的开头 xb )。我认为这是明确定义的,只要完整的对象“x”中至少有那么多字节...... (2认同)
  • ...有哪些,因为“x”是 POD(标准布局),因此它的数据成员必须按照它们定义的顺序出现。这取决于“memcpy”是否读取填充或“c”的实现,但它必须读取某些内容,并且我认为如果没有填充,它*必须*读取“c”。因此,除非“sizeof(stX) &gt; sizeof(int)*3”(我对此表示怀疑),否则我认为提问者的编译器不符合要求。我可能错了。 (2认同)