簡體   English   中英

在 C99 中的復合文字中使用后綴/前綴增量運算符

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

有一個從 CARM 借來的示例(CA Reference Manual, 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 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

但是當我重寫它時:

#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 工作正常:

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 function 的這兩個變體在 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} ,它也是 UB,因為p的長度為 5。

對於始終選擇該順序的編譯器,您的代碼將起作用; 但是對於選擇其他順序的編譯器,您的代碼可能變為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[4]包含五個不同的指針,所有指針都指向int s,其生命周期繼續通過printf循環,這樣*(p[0]) == 0 , *(p[1]) == 1等。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM