Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/63.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 如果未明显使用,编译器是否允许删除对结构成员的赋值?_C_Standards Compliance - Fatal编程技术网

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

C 如果未明显使用,编译器是否允许删除对结构成员的赋值?,c,standards-compliance,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

考虑以下代码:

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;
}
char缓冲区[256];
结构stX
{
INTA;
int b;
INTC;
};
void*my_memcpy(void*destination,const void*source,size\u t num);
int main()
{
结构stX;
x、 a=1;
x、 b=2;
x、 c=3;
my_memcpy(缓冲区和x.b,2*sizeof(int));
{
int i;
对于(i=0;i<2*sizeof(int);+i){
printf(“%d\n”,缓冲区[i]);
}
}
返回0;
}
一个特定的嵌入式系统编译器决定删除对x.A和x.c的赋值(因为它们从未被使用过(至少不是很明显)。这是c标准允许的优化吗

当然,将结构实例定义为包含在任务中的易失性线索

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


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

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

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

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


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

访问易失性对象、修改对象、修改文件或调用函数 也就是说,所有这些操作都是副作用,都是机体状态的变化 执行环境

一个 如果可以推断表达式的 未使用该值,且不会产生所需的副作用


GCC执行完全相同的优化(使用x86_64和-O3):


可以看出,
x.a=1
未执行。而且它可能还可以进一步优化。

是的。编译器可以对代码应用任何优化,假设变量的值不能“自行”更改

优化编译器将注意到,没有其他代码可能更改存储在foo中的值,并将假定它始终等于0。因此,编译器将用类似以下内容的无限循环替换函数体:

void bar(void) {
    foo = 0;

    while (true)
         ;
}
但是,foo可能表示一个位置,该位置可以随时被计算机系统的其他元素更改,例如连接到CPU的设备的硬件寄存器。上面的代码永远不会检测到这样的更改

为防止编译器如上所述优化代码,使用volatile关键字:

static volatile int foo;

void bar (void) {
    foo = 0;

    while (foo != 255)
        ;
}

初步的几点:

  • 您的
    main
    函数返回
    void
    ,这意味着就标准而言,您的程序行为没有定义。但我们假设您的实现明确允许从
    main
    返回
    void
    。另外,
    memcpy
    的范围内也没有原型,因为您没有包括
    string.h

  • buffer
    中的值从未使用过,因此实际上整个程序没有可观察的行为。因此,允许编译器删除任何或所有代码。就标准而言,一个什么都不做的程序和另一个程序一样好

我将假设您编写了一个程序,在
memcpy
之后从
buffer
打印
2*sizeof(int)
字节:

#include <string.h>
#include <stdio.h>

char buffer[256];

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

int main()
{
    struct stX x;
    x.a = 1;
    x.b = 2;
    x.c = 3;
    memcpy(buffer, &x.b, 2 * sizeof(int));
    {
        int i;
        for (i = 0; i < 2 * sizeof(int); ++i) {
            printf("%d\n", buffer[i]);
        }
    }
    return 0; /* just in case this is C89 */
}

也就是说,
x
(或者就此而言,
buffer
)不需要在物理内存中的任何位置“真正存在”,但是该标准确实要求打印出来的字节应该是正确的字节,这取决于
int
在实现中的表示方式以及
struct stX
在实现中的布局方式。

您是否意识到,memcpy不能保证实际得到x.b和x.c?你可能会吃一口填充物,你是对的。但这不是重点。@Vicky:我本来以为你是对的,但我想不出有填充字节的情况。Int应该与机器的字大小匹配。如果此对齐高于b(可能还有c),则适合对齐,因此不会得到填充。@LorenPechtel:编译器可以随意插入填充。是的,没有实际的编译器应该这样做,但是Vicky说“保证”,根据语言。@LorenPechtel:另外,你可以把“int应该匹配机器的字长”作为标准的建议,而不是硬性要求。它实际上说的是,
int
“具有执行环境架构所建议的自然大小”。没有提到任何所谓的“字号”,也没有具体要求编译器编写人员应该以何种方式进行建议。在实践中,有很多C实现具有64位字和32位
int
,这通常不是一个缺陷。如果CPU有32位的算术运算,可能这“暗示”一个32位的“自然”大小。不过,差别是显著的。gcc仍然会写入
c
,提问者的编译器也会这样做
static volatile int foo;

void bar (void) {
    foo = 0;

    while (foo != 255)
        ;
}
#include <string.h>
#include <stdio.h>

char buffer[256];

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

int main()
{
    struct stX x;
    x.a = 1;
    x.b = 2;
    x.c = 3;
    memcpy(buffer, &x.b, 2 * sizeof(int));
    {
        int i;
        for (i = 0; i < 2 * sizeof(int); ++i) {
            printf("%d\n", buffer[i]);
        }
    }
    return 0; /* just in case this is C89 */
}
int main() {
    ((int*)buffer)[0] = 2;
    ((int*)buffer)[1] = 3;
    ... same printf loop as before ...
}