source

"j = ++(i | i); 및 j = ++(i & i);" 식은 값 오류여야 합니까?

nicesource 2023. 7. 18. 21:48
반응형

"j = ++(i | i); 및 j = ++(i & i);" 식은 값 오류여야 합니까?

나는 내 코드에서 그것을 예상했습니다:

#include<stdio.h> 
int main(){
    int i = 10; 
    int j = 10;

    j = ++(i | i);
    printf("%d %d\n", j, i);

    j = ++(i & i);
    printf("%d %d\n", j, i);

    return 1;
}

j = ++(i | i);그리고.j = ++(i & i);다음과 같은 lvalue 오류가 발생합니다.

x.c: In function ‘main’:
x.c:6: error: lvalue required as increment operand
x.c:9: error: lvalue required as increment operand   

하지만 아래와 같이 위의 코드가 성공적으로 컴파일되었다는 것에 놀랐습니다.

~$ gcc x.c -Wall
~$ ./a.out 
11 11
12 12   

의 코드가 올바르게 작동하는지 확인합니다.

다른 연산자는 오류를 생성하는 반면(제가 이해하는 바와 같습니다).짝수 비트 연산자 XOR가 오류의 원인임j = ++(i ^ i);(다른 연산자가 컴파일 시 l 값 오류를 생성하는지 확인합니다.)

이유가 뭐야?이것이 지정되지 않았거나 정의되지 않은 것입니까? 아니면 비트 단위의 ORAND 연산자가 다릅니까?

컴파일러 버전:

gcc version 4.4.5 (Ubuntu/Linaro 4.4.4-14ubuntu5)

하지만 저는 컴파일러 버전이 획일적이지 않은 동작을 이유로 해서는 안 된다고 생각합니다.한다면^되지 않은 그때컴되지않은일|그리고.&또한 아닙니다.그렇지 않으면 모두에게 효과가 있을 것입니다.

오류가 : c99 모드이의오아류닙다니가러.gcc x.c -Wall -std=c99.

컴파일하지 않는 것이 좋으며 대부분의 컴파일러에서는 컴파일하지 않습니다.
하지 않는

나는 단지 컴파일러가 다음과 같은 정체를 알고 있다는 가설을 세울 수 있을 뿐입니다.(i | i) == i그리고.(i & i) == i그리고 그 아이덴티티를 사용하여 표현을 최적화하고, 변수를 뒤로하고 있습니다.i.

이것은 단지 추측일 뿐이지만, 저에게는 매우 이치에 맞습니다.

이것은 최신 GCC 버전에서 해결된 버그입니다.

가 최적화를 하기 i & ii그리고.i | ii않은 합니다; 이은또한 x 또는 연산가작지않설다명니합이유를은동.i ^ i 최적화될 것입니다.0수정 가능한 l 값이 아닙니다.

§ 및 C11 (n1570), § 6.5.3.1 사 증 및 감 연 산 자
접두사 증분 또는 감소 연산자의 피연산자는 원자형, 한정형 또는 무정형 실수 또는 포인터형이어야 하며 수정 가능한 l 값이어야 합니다.

C11 (n1570), § 6.3.2.1 L 값, 배열 및 함수 지정자
수정 가능한 l 값은 배열 유형이 없고, 불완전한 유형이 없으며, 정규화된 유형이 없으며, 구조체 또는 유니언인 경우에는 정규화된 유형을 가진 멤버(재귀적으로 포함된 모든 Aggregate 또는 유니언의 멤버 또는 요소 포함)가 없는 l 값입니다.

C11 (n1570), § 6.3.2.1 L 값, 배열 및 함수 지정자
l 값은 다음과 같은 개체 유형을 가진 식입니다.void 할 수 있습니다. 개체를 지정할 수 있습니다.

§ 및 C11(n1570), § 3. 어, 정
파일: 으로, 그 이 값을 있습니다.

제가 아는 한, 잠재적으로 "존재할 수 있지만 아직 존재하지 않음"을 의미합니다.그렇지만(i | i)실행 환경의 데이터 저장소 영역을 참조할 수 없습니다.따라서 이 값은 al 값이 아닙니다.이것은 그 이후로 수정된 오래된 gcc 버전의 버그인 것 같습니다.컴파일러를 업데이트합니다!

제 질문에 대한 후속 조치입니다. 도움이 될 수 있도록 자세한 답변을 추가했습니다.

표현식에서 내드코에서식현j = ++(i | i);그리고.j = ++(i & i);lvalue 오류로 인한 것이 아닙니까?

@abelenky가 대답한 것처럼 컴파일러 최적화 때문입니다.(i | i) == i그리고.(i & i) == i그것은 정확히 맞습니다.

컴파일러에서 내컴러서에일(gcc version 4.4.5)단일 변수와 결과를 포함하는 모든 식은 변경되지 않으며, 단일 변수(식이 아닌 것)로 최적화됩니다.

예:

j = i | i      ==> j = i
j = i & i      ==> j = i
j = i * 1      ==> j = i
j = i - i + i  ==> j = i 

==>은 단입니다.optimized to

C 코드를 ▁with▁and▁to다▁a▁dis▁c▁written니▁code▁observe로 그것을 분해했습니다.gcc -S.

C-Code: (댓글 읽기)

#include<stdio.h>
int main(){
    int i = 10; 
    int j = 10;
    j = i | i;      //==> j = i
        printf("%d %d", j, i);
    j = i & i;      //==> j = i
        printf("%d %d", j, i);
    j = i * 1;      //==> j = i
    printf("%d %d", j, i);
    j = i - i + i;  //==> j = i
    printf("%d %d", j, i);
}

어셈블리 출력: (주석 읽기)

main:
    pushl   %ebp
    movl    %esp, %ebp
    andl    $-16, %esp
    subl    $32, %esp
    movl    $10, 28(%esp)   // i 
    movl    $10, 24(%esp)   // j

    movl    28(%esp), %eax  //j = i
    movl    %eax, 24(%esp)

    movl    $.LC0, %eax
    movl    28(%esp), %edx
    movl    %edx, 8(%esp)
    movl    24(%esp), %edx
    movl    %edx, 4(%esp)
    movl    %eax, (%esp)
    call    printf

    movl    28(%esp), %eax  //j = i
    movl    %eax, 24(%esp)

    movl    $.LC0, %eax
    movl    28(%esp), %edx
    movl    %edx, 8(%esp)
    movl    24(%esp), %edx
    movl    %edx, 4(%esp)
    movl    %eax, (%esp)
    call    printf

    movl    28(%esp), %eax  //j = i
    movl    %eax, 24(%esp)

    movl    $.LC0, %eax
    movl    28(%esp), %edx
    movl    %edx, 8(%esp)
    movl    24(%esp), %edx
    movl    %edx, 4(%esp)
    movl    %eax, (%esp)
    call    printf

    movl    28(%esp), %eax  //j = i
    movl    %eax, 24(%esp)

    movl    $.LC0, %eax
    movl    28(%esp), %edx
    movl    %edx, 8(%esp)
    movl    24(%esp), %edx
    movl    %edx, 4(%esp)
    movl    %eax, (%esp)
    call    printf  

위 어셈블리 코드에서 모든 식을 다음 코드로 변환합니다.

movl    28(%esp), %eax  
movl    %eax, 24(%esp)

은 와동한등에 합니다.j = iC코드로따라서j = ++(i | i); 리고그j = ++(i & i); 되어 있습니다.j = ++i.

j = (i | i)는 식으로 표현하는 문장입니다.(i | i) 가 아님() 님아문

따라서 코드를 성공적으로 컴파일할 수 있습니다.

ㅠㅠj = ++(i ^ i);또는j = ++(i * i);,j = ++(i | k);컴파일러에서 lvalue 오류를 생성하시겠습니까?

두 식 중 하나의 값이 상수이거나 수정할 수 없는 l 값(최적화되지 않은 식)이기 때문입니다.

을 사용하여 관찰할 수 있습니다.asm

#include<stdio.h> 
int main(){
    int i = 10; 
    int j = 10;
    j = i ^ i;
    printf("%d %d\n", j, i);
    j = i - i;
    printf("%d %d\n", j, i);
    j =  i * i;
    printf("%d %d\n", j, i);
    j =  i + i;
    printf("%d %d\n", j, i);        
    return 1;
}

어셈블리 코드: (댓글 읽기)

main:
    pushl   %ebp
    movl    %esp, %ebp
    andl    $-16, %esp
    subl    $32, %esp
    movl    $10, 28(%esp)      // i
    movl    $10, 24(%esp)      // j

    movl    $0, 24(%esp)       // j = i ^ i;
                               // optimized expression i^i = 0
    movl    $.LC0, %eax
    movl    28(%esp), %edx
    movl    %edx, 8(%esp)
    movl    24(%esp), %edx
    movl    %edx, 4(%esp)
    movl    %eax, (%esp)
    call    printf

    movl    $0, 24(%esp)      //j = i - i;
                              // optimized expression i - i = 0
    movl    $.LC0, %eax
    movl    28(%esp), %edx
    movl    %edx, 8(%esp)
    movl    24(%esp), %edx
    movl    %edx, 4(%esp)
    movl    %eax, (%esp)
    call    printf

    movl    28(%esp), %eax    //j =  i * i;
    imull   28(%esp), %eax
    movl    %eax, 24(%esp)

    movl    $.LC0, %eax
    movl    28(%esp), %edx
    movl    %edx, 8(%esp)
    movl    24(%esp), %edx
    movl    %edx, 4(%esp)
    movl    %eax, (%esp)
    call    printf

    movl    28(%esp), %eax   // j =  i + i;
    addl    %eax, %eax
    movl    %eax, 24(%esp)

    movl    $.LC0, %eax
    movl    28(%esp), %edx
    movl    %edx, 8(%esp)
    movl    24(%esp), %edx
    movl    %edx, 4(%esp)
    movl    %eax, (%esp)
    call    printf

    movl    $1, %eax
    leave

그러므로 이것은 생산합니다.lvalue error피연산자가 수정 가능한 l 값이 아니기 때문입니다.그리고 불균일한 동작은 gcc-4.4의 컴파일러 최적화 때문입니다.

새로운 gcc complier(또는 대부분의 컴파일러)가 lvalue 오류를 생성하는 이유는 무엇입니까?

왜하면표현의평가가냐평가가▁because▁evaluation.++(i | i)그리고.++(i & i)증분(+) 연산자의 실제 정의를 금지합니다.

Dennis M에 따르면.리치의 책 "C 프로그래밍 언어" 섹션 "2.8 증분 및 감소 연산자" 44페이지

증분 및 감소 연산자는 변수에만 적용할 수 있습니다. (i+j)+와 같은 식은 잘못되었습니다.피연산자는 산술 또는 포인터 유형의 수정 가능한 l 값이어야 합니다.

새로운 gcc 컴파일러 4.47에서 테스트를 해보니 예상했던 대로 오류가 발생합니다.저는 tcc 컴파일러에서도 테스트를 했습니다.

이에 대한 피드백/의견을 주시면 감사하겠습니다.

저는 이것이 최적화 오류라고 전혀 생각하지 않습니다. 만약 그랬다면, 애초에 어떤 오류도 없어야 하기 때문입니다.한다면++(i | i) 되어 있습니다.++(i) 합니다. 어합없니다면어왜, 하냐그러야.(i)는 l 값입니다.

가 IMHO를 보는 것 .(i | i)하지만, 연산자 식 으 로 분 명 히 은 서 지 만 그 연 출 하 자 산 력 증 분 값 을 출 것 력 연 자 산 ▁as 증 ▁r ▁operator ,++모든 값이 변경될 것으로 예상되므로 오류가 발생합니다.

언급URL : https://stackoverflow.com/questions/14860189/expressions-j-i-i-and-j-i-i-should-be-a-lvalue-error

반응형