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

安德烈·拉宁(Andrey Lanin)

有一个从CARM借来的示例(CA参考手册,Samuel P. Harbison III,Guy L. Steele Jr.,2002,Prentice Hall),第218-219页。我将所有三个代码块都写在一个来源中:

#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功能无法正常运行:

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

但是当我重写它时:

#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函数可以正常工作:

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

为什么呢 f2函数的这两个变体在i的后缀/中缀增量(在复合文字中)返回的值方面有所不同。在第一种情况下,它是临时值。后缀增量运算符的结果不是左值。前缀递增运算符的结果也不是左值(根据CARM的第226页)。请帮我理解。(对不起我的英语不好)。

内特·艾德雷奇

我不认为这是有关左值和临时性的问题;但在H&S的示例中却是无关的错误。

在声明中p[i]=(int [1]){i++};,存在一个棘手的问题,即在后面是否有一个序列点i++,这似乎取决于是否i++完整表达式在C17中明确声明,不属于复合文字的初始化程序是一个完整表达式,这似乎暗示该i++不是完整表达式并且没有序列点。在这种情况下,所讨论的语句将是未定义的行为,就像p[i]=(int [1]){++i};您的版本中的一样。

但是,C99似乎没有“不是复合文字的一部分”异常,因此我不确定那是什么情况。但是i++,据我所知,即使在的副作用之后有一个序列点=也未指定的左侧和右侧的评估顺序因此,如果编译器选择首先评估右侧(包括其副作用),则该语句将变为未初始化p[1] = (int [1]){0};并留下p[0]未初始化的状态,从而在取消引用时会导致未定义的行为。同样,p[5] = (int [1]){4}由于p长度为5 ,最后一条语句也变为UB

对于始终选择该顺序的编译器,您的代码将起作用。但是对于选择其他顺序的编译器,您的代码可能也变成p[-1] = (int [1]){0}UB。因此,我也不认为您的版本完全正确。

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++;

然后代码将是正确的,并会做他们说:p[0], ..., p[5]包含五个不同的指针,所有指向int(胡)的寿命继续通过printf循环,并使得*(p[0]) == 0*(p[1]) == 1等等。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

在作业中使用后缀/后缀运算符

来自分类Dev

C#中的前缀和后缀运算符重载

来自分类Dev

C#中的前缀和后缀运算符重载

来自分类Dev

前缀和后缀运算符C ++

来自分类Dev

前缀增量运算符错误C ++

来自分类Dev

在C中使用?:运算符的复合if语句

来自分类Dev

为什么在int.parse(“ 1”)之后不允许使用后缀增量(++)运算符?

来自分类Dev

难以理解Javascript中的后缀增量运算符

来自分类Dev

Java中前缀和后缀++运算符之间的区别

来自分类Dev

如何调用后缀运算符++?

来自分类Dev

C ++中后缀增量运算符中int变量的含义

来自分类Dev

C和Java中的后缀和前缀运算符产生不同的结果

来自分类Dev

前缀和后缀运算符Java

来自分类Dev

C中的数组增量运算符

来自分类Dev

了解C中的增量运算符

来自分类Dev

在elasticsearch中使用NOT运算符排除复合词

来自分类Dev

为什么javac在类上使用后缀“ ++”运算符创建未使用的变量?

来自分类Dev

如何在React中使用增量运算符

来自分类Dev

编译器如何使用后缀运算符处理return语句?

来自分类Dev

编译器如何使用后缀运算符处理return语句?

来自分类Dev

在C#中的if运算符中使用like运算符

来自分类Dev

C ++ 14中std :: string的运算符后缀

来自分类Dev

C ++ 14中std :: string的运算符后缀

来自分类Dev

带后缀增量的三元运算符分配

来自分类Dev

C ++后缀/前缀运算符重载为非成员函数

来自分类Dev

后缀和前缀运算符的行为(Java)

来自分类Dev

澄清迭代器上的后缀/前缀运算符

来自分类Dev

后缀和前缀运算符的行为(Java)

来自分类Dev

通过前缀和后缀运算符递增数字

Related 相关文章

热门标签

归档