简体   繁体   English

在 C99 中的复合文字中使用后缀/前缀增量运算符

[英]Using postfix/prefix increment operators in compound literals in C99

There is an example borrowed from CARM (CA Reference Manual, Samuel P. Harbison III, Guy L. Steele Jr., 2002, Prentice Hall), page 218-219.有一个从 CARM 借来的示例(CA Reference Manual, Samuel P. Harbison III, Guy L. Steele Jr., 2002, Prentice Hall),第 218-219 页。 I write all of three code-chunk in one source:我在一个源中编写了所有三个代码块:

#include <stdio.h>

void f1(){
    int *p[5];
    int i=0;
    m:
    p[i]=(int [1]){i};
    if(++i<5)goto m;
    printf("f1: ");
    for(i=0;i<5;++i)
        printf("p[%d]=%d ",i,*(p[i]));
    printf("\n");
    fflush(stdout);
}

void f2(){
    int *p[5];
    int i=0;
    p[i]=(int [1]){i++};
    p[i]=(int [1]){i++};
    p[i]=(int [1]){i++};
    p[i]=(int [1]){i++};
    p[i]=(int [1]){i++};
    printf("f2: ");
    for(i=0;i<5;++i)
        printf("p[%d]=%d ",i,*(p[i]));
    printf("\n");
    fflush(stdout);
}

void f3(){
    int *p[5];
    int i;
    for(i=0;i<5;i++){
        p[i]=(int [1]){i};
    }
    printf("f3: ");
    for(i=0;i<5;++i)
        printf("p[%d]=%d ",i,*(p[i]));
    printf("\n");
    fflush(stdout);
}

int main(){ f1(); f2(); f3(); }

f2 function doesn't work properly: f2 function 不能正常工作:

user@debian:~/test7$ gcc -std=c11 ./carm_1.c -o carm_1
user@debian:~/test7$ ./carm_1
f1: p[0]=4 p[1]=4 p[2]=4 p[3]=4 p[4]=4 
f2: p[0]=-2106668384 p[1]=-2106668408 p[2]=32765 p[3]=2 p[4]=3 
f3: p[0]=4 p[1]=4 p[2]=4 p[3]=4 p[4]=4

But when i rewrote it:但是当我重写它时:

#include <stdio.h>

void f1(){
    int *p[5];
    int i=0;
    m:
    p[i]=(int [1]){i};
    if(++i<5)goto m;
    printf("f1: ");
    for(i=0;i<5;++i)
        printf("p[%d]=%d ",i,*(p[i]));
    printf("\n");
    fflush(stdout);
}

void f2(){
    int *p[5];
    int i=-1;
    p[i]=(int [1]){++i};
    p[i]=(int [1]){++i};
    p[i]=(int [1]){++i};
    p[i]=(int [1]){++i};
    p[i]=(int [1]){++i};
    printf("f2: ");
    for(i=0;i<5;++i)
        printf("p[%d]=%d ",i,*(p[i]));
    printf("\n");
    fflush(stdout);
}

void f3(){
    int *p[5];
    int i;
    for(i=0;i<5;i++){
        p[i]=(int [1]){i};
    }
    printf("f3: ");
    for(i=0;i<5;++i)
        printf("p[%d]=%d ",i,*(p[i]));
    printf("\n");
    fflush(stdout);
}

int main(){ f1(); f2(); f3(); }

f2 function works fine: f2 function 工作正常:

user@debian:~/test7$ gcc -std=c11 ./carm_2.c -o carm_2
user@debian:~/test7$ ./carm_2
f1: p[0]=4 p[1]=4 p[2]=4 p[3]=4 p[4]=4 
f2: p[0]=0 p[1]=1 p[2]=2 p[3]=3 p[4]=4 
f3: p[0]=4 p[1]=4 p[2]=4 p[3]=4 p[4]=4

Why?为什么? These two variants of f2 function differs in value returned by postfix/infix increment of i (in compound literal). f2 function 的这两个变体在 i 的后缀/中缀增量返回的值(在复合文字中)不同。 In the first case it is temporary value.在第一种情况下,它是临时值。 The result of postfix increment operator is not lvalue.后缀自增运算符的结果不是左值。 And the result of prefix increment operator is also not lvalue (in according to page 226 of CARM).并且前缀增量运算符的结果也不是左值(根据 CARM 的第 226 页)。 Please, help me to understand.请帮我理解。 (sorry for my english). (对不起我的英语不好)。

I don't think this is an issue about lvalues and temporaries;我不认为这是关于左值和临时值的问题。 but rather an unrelated bug in H&S's example.而是 H&S 示例中的一个不相关的错误。

In the statement p[i]=(int [1]){i++};在语句p[i]=(int [1]){i++}; , there is a tricky question as to whether there is a sequence point after the i++ , which seems to hinge on whether i++ is a full expression . ,有一个棘手的问题是i++之后是否有一个序列点,这似乎取决于i++是否是一个完整的表达式 In C17 it is explicitly stated that an initializer that is not part of a compound literal is a full expression, which would seem to imply that the i++ is not a full expression and that there is no sequence point.在 C17 中明确指出,不属于复合文字一部分的初始化程序是完整表达式,这似乎暗示i++不是完整表达式并且没有序列点。 In that case, the statement in question would be undefined behavior, as would the p[i]=(int [1]){++i};在这种情况下,有问题的语句将是未定义的行为, p[i]=(int [1]){++i};也是如此。 in your version.在你的版本中。

However, C99 doesn't seem to have had the "not part of a compound literal" exception, so I'm not quite sure what the situation was there.但是,C99 似乎没有“不是复合文字的一部分”异常,所以我不太确定那里的情况。 But even if there is a sequence point after the side effect of i++ , still as far as I know, the order of evaluation of the left and right sides of = is unspecified.但是即使在i++的副作用之后有一个序列点,据我所知, =的左右两边的求值顺序是未指定的。 So if the compiler chooses to evaluate the right side first (including its side effects), the statement becomes p[1] = (int [1]){0};因此,如果编译器选择先评估右侧(包括其副作用),则语句变为p[1] = (int [1]){0}; and leaves p[0] uninitialized, causing undefined behavior when it is dereferenced.并使p[0]未初始化,从而在取消引用时导致未定义的行为。 By the same token, the last statement becomes p[5] = (int [1]){4} which is also UB since p is of length 5.同样的道理,最后一条语句变成了p[5] = (int [1]){4} ,它也是 UB,因为p的长度为 5。

For a compiler that consistently chooses that ordering, your code would work;对于始终选择该顺序的编译器,您的代码将起作用; but for a compiler that chose the other order, your code could become p[-1] = (int [1]){0} which is likewise UB.但是对于选择其他顺序的编译器,您的代码可能变为p[-1] = (int [1]){0} ,这同样是 UB。 So I don't think your version is strictly correct either.所以我不认为你的版本是严格正确的。

H&S probably should not have tried to be so clever, and just written H&S 可能不应该这么聪明,只是写了

int i=0;
p[i] = (int [1]){i};
i++;
p[i] = (int [1]){i};
i++;
p[i] = (int [1]){i};
i++;
p[i] = (int [1]){i};
i++;
p[i] = (int [1]){i};
i++;

Then the code would be correct and would do what they say: p[0], ..., p[4] contain five different pointers, all pointing to int s whose lifetimes continue through the printf loop, and such that *(p[0]) == 0 , *(p[1]) == 1 , etc.然后代码将是正确的,并且会按照他们所说的去做: p[0], ..., p[4]包含五个不同的指针,所有指针都指向int s,其生命周期继续通过printf循环,这样*(p[0]) == 0 , *(p[1]) == 1等。

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

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