"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 & i
i
그리고.i | i
i
않은 합니다; 이은또한 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 = i
C코드로따라서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
'source' 카테고리의 다른 글
관리자 전체에서 사용할 수 있도록 장고 앱에 자세한 이름을 지정할 수 있습니까? (0) | 2023.07.18 |
---|---|
ASP.NET 유효성 검사기를 사용한 날짜 유효성 검사 (0) | 2023.07.18 |
Python에서 체인 메서드 호출 들여쓰기 스타일 (0) | 2023.07.18 |
ORACLE/SQL Server의 마이너스와 예외 차이 (0) | 2023.07.18 |
Git: 구체적인 약속을 기반으로 하는 방법은 무엇입니까? (0) | 2023.07.18 |