쉼표 연산자에서 왼쪽 피연산자는 부작용이 없으면 실제로 실행되지 않는 것이 보장됩니까?
할 수 하려면 C 를 하지만 C++ 에서도()).struct
합니다.), 합니다.
저는 이 매크로를 생각해 냈습니다.
#define STR_MEMBER(S,X) (((struct S*)NULL)->X, #X)
입니다)을 입니다.const char*
의 struct
합니다.최소한의 사용 예:
#include <stdio.h>
struct a
{
int value;
};
int main(void)
{
printf("a.%s member really exists\n", STR_MEMBER(a, value));
return 0;
}
한다면value
다의 struct a
안 되고
쉼표 연산자는 왼쪽 피연산자를 평가한 다음 식의 결과(있는 경우)를 폐기해야 합니다. 그래서 보통 왼쪽 피연산자의 평가에 부작용이 있을 때 이 연산자를 사용하는 것으로 알고 있습니다.
그러나 이 경우에는 (의도된) 부작용이 없지만, 물론 컴파일러가 실제로 식을 평가하는 코드를 생성하지 않으면 작동합니다. 그렇지 않으면 a에 접근할 수 있기 때문입니다.struct
지:NULL
분할 오류가 발생합니다.
및는 Gcc/g++ 6.3 4.9.2 는를 하더라도 결코 .-O0
할 수 수 처럼 "" 입니다를 합니다 따라서 평가를 생략할 수도 있습니다.
volatile
매크로에서 (예를 들어 해당 메모리 주소에 액세스하는 것이 원하는 부작용이기 때문에) 지금까지 분할 오류를 트리거할 수 있는 유일한 방법이었습니다.
그래서 질문: 컴파일러가 평가에 부작용이 없다는 것을 확신할 수 있을 때 컴파일러가 항상 쉼표 연산자의 왼쪽 피연산자에 대한 실제 평가를 피할 것을 보장하는 C 및 C++ 언어 표준에 무엇이 있습니까?
메모 및 수정
저는 거시적인 부분을 그대로 판단하고 그것을 사용하거나 더 좋게 만들 기회를 달라는 것이 아닙니다.이 질문의 목적을 위해 매크로는 정의되지 않은 동작을 유발하는 경우에만 나쁜 상태가 됩니다. 즉, 컴파일러가 부작용이 없을 때에도 "평가 코드"를 생성할 수 있도록 허용되어 위험한 경우에만 그런 상태가 될 수 있습니다.
저는 이미 두 가지 분명한 해결책을 염두에 두고 있습니다.닝"을하기struct
것을 해서.offsetof
는 가장 큰 큰합니다.struct
우리는 의 첫번째 논쟁으로 사용합니다.STR_MEMBER
(예: 정적 결합이 가능할 수도 있습니다…).후자는 완벽하게 작동해야 합니다. 우리가 관심이 없는 오프셋을 제공하고 액세스 문제를 방지합니다. 실제로 gcc는 제가 사용하는 컴파일러이기 때문에(따그 때문에) gcc라고 가정합니다.offsetof
붙박이의 행동
더 offsetof
#define STR_MEMBER(S,X) (offsetof(struct S,X), #X)
volatile struct S
struct S
segfault다를
다른 가능한 "수정"에 대한 제안 또한 환영합니다.
주석추가
스토리지 의 C++ 의 C에 .struct
은 C 것 , 이를 시도하는 이는 C++에서는 괜찮은 것 같지만, 이 질문을 위해 끓인 코드 대신 원본에 가까운 코드로 C를 시도하는 순간, C가 그것에 전혀 만족하지 않는다는 것을 깨달았습니다.
error: initializer element is not constant
C는 컴파일 타임에 구조를 초기화할 수 있기를 원하며, 대신 C++는 괜찮습니다.
컴파일러가 항상 쉼표 연산자의 왼쪽 피연산자에 대한 실제 평가를 피할 것을 보장하는 C 및 C++ 언어 표준에 있습니까?
오히려 그 반대입니다.표준은 왼쪽 피연산자 IS가 평가된 것을 보장합니다(실제로는 예외가 없습니다).결과는 폐기됩니다.
참고: lvalue 식의 경우 "evaluate"는 "저장된 값에 액세스"한다는 의미가 아닙니다.대신 지정된 메모리 위치를 파악하라는 의미입니다.lvalue 식을 포함하는 다른 코드는 메모리 위치에 액세스할 수도 있고 그렇지 않을 수도 있습니다.메모리 위치에서 읽는 과정을 C에서는 "lvalue 변환"이라고 하고, C++에서는 "lvalue torvalue 변환"이라고 합니다.
된 값 : 쉼표 )은++ 에서(: ) 에만 l 값 torvalue 됩니다.volatile
또한 일부 다른 기준을 충족합니다(자세한 내용은 C++14 [expr]/11 참조).결과가 사용되지 않은 식에 대해 In Clvalue 변환이 발생합니다(C116.3.2.1/2).
예제에서는 l 값 변환이 발생하는지 여부를 확인할 수 없습니다.두 hX->Y
서,X
는 포인터이며 다음과 같이 정의됩니다.(*X).Y
에서 적용하는 ; C *
되지 않은 이작(C116.5.3/3)하고, C++.
연산자는 왼쪽 피연산자가 실제로 객체를 지정하는 경우에만 정의됩니다(C++14 [expr.ref]/4.2).
쉼표 연산자(C 문서, 매우 유사한 것을 말함)는 그러한 보장이 없습니다.
에서
E1, E2
결과가 식 됩니다. 식 E1 되고, 되고 E1 , 의됩니다 E2 .
관련성이 없는 정보 생략
.E1
컴파일러가 사이드 effects가 없다고 판단할 수 있다면 as-if 규칙에 의해 최적화할 수 있지만 평가됩니다.
Gcc/g++ 6.3 및 4.9.2는 -O0을 사용하더라도 평가에 부작용이 없으므로 건너뛸 수 있다는 것을 항상 "확인"할 수 있는 것처럼 위험한 코드를 생성한 적이 없습니다.
됩니다.-fsanitize=undefined
선택. . 은 코드가 되지 않은 하는 것으로 합니다.이것은 여러분의 질문에 대한 답이 되어야 합니다: 적어도 하나의 주요 구현의 개발자들은 코드가 정의되지 않은 동작을 가지고 있다고 분명히 생각합니다.그리고 그들이 옳습니다.
다른 가능한 "수정"에 대한 제안 또한 환영합니다.
저는 그 표현을 평가하지 않기 위해 확실한 것을 찾겠습니다.foffsetof
하지만,다와 같이 . 예를 들어 다음과 같이.X
이다.a.b
, .sizeof
표현이 평가되지 않도록 강요하는 것입니다.
네가 묻는다,
컴파일러가 평가에 부작용이 없다는 것을 확신할 수 있을 때 컴파일러가 쉼표 연산자의 왼쪽 피연산자에 대한 실제 평가를 항상 피할 것을 보장하는 C 및 C++ 언어 표준에 있습니까?
다른 사람들이 말했듯이 답은 "아니오"입니다.반대로, 표준은 무조건 쉼표 연산자의 왼쪽 피연산자를 평가하고 결과를 폐기한다고 명시합니다.
이는 물론 추상 머신의 실행 모델에 대한 설명입니다. 관찰 가능한 동작이 추상 머신의 동작이 생성하는 것과 동일한 한 구현은 다르게 작동하도록 허용됩니다.왼쪽 표현식을 실제로 평가해도 부작용이 없는 경우에는 완전히 생략할 수 있지만 어느 표준에도 생략할 것을 요구하는 내용이 없습니다.
이 문제를 해결하기 위해서는 다양한 옵션이 있으며, 그 중 일부는 지정한 두 언어 중 하나 또는 다른 언어에만 적용됩니다.나는 당신을 좋아하는 편입니다.offsetof()
사람들은 이지만 C++를은하는 했습니다.offsetof
적용할 수 없습니다.반면, C에서는 표준이 구조 유형에 적용되는 내용을 구체적으로 설명하고 있지만, 조합 유형에 대해서는 아무런 언급이 없습니다.노조 유형에 대한 행동은 기술적으로 정의되지 않은 것처럼 일관되고 자연스러울 가능성이 매우 높습니다.
C에서만 복합 리터럴을 사용하여 접근 방식에서 정의되지 않은 동작을 피할 수 있습니다.
#define HAS_MEMBER(T,X) (((T){0}).X, #X)
이는 구조 및 조합 유형에서도 동일하게 잘 작동합니다(이 버전에 대해 태그만 제공하는 것이 아니라 전체 유형 이름을 제공해야 함.지정된 유형에 해당 멤버가 있을 때 동작이 잘 정의됩니다.확장이 언어 제약 조건을 위반하므로 진단을 실행해야 합니다. 이 경우 유형에 해당 멤버가 없는 경우(구조 유형이나 결합 유형이 아닌 경우 포함).
를 할 수도 .sizeof
@alain 처럼,sizeof
식을 평가하고 피연산자를 평가하지 않습니다(단, C에서 피연산자가 가변적으로 수정된 유형을 사용하는 경우에는 사용자의 용도에 적용되지 않음).이 변형은 정의되지 않은 동작을 도입하지 않고 C와 C++에서 모두 작동할 것이라고 생각합니다.
#define HAS_MEMBER(T,X) (sizeof(((T *)NULL)->X), #X)
구조와 조합 모두에게 효과가 있도록 다시 한번 작성하였습니다.
쉼표 연산자의 왼쪽 피연산자가 폐기된 값 식입니다.
5
11다.이러한 식을 폐기된 값 식이라고 합니다.합니다. [...]
이름에서 알 수 있듯이 평가되지 않은 피연산자도 있습니다.
8 어떤 맥락에서는 평가되지 않은 피연산자가 나타납니다(5.2.8, 5.3.3, 5.3.7, 7.1.6.2).평가되지 않은 피연산자는 평가되지 않습니다.평가되지 않은 피연산자는 완전한 표현으로 간주됩니다. [...]
사용 사례에서 폐기된 값 식을 사용하는 것은 정의되지 않은 동작이지만 평가되지 않은 피연산자를 사용하는 것은 정의되지 않습니다.
으로.sizeof
예를 들어 UB는 평가되지 않은 피연산자를 사용하기 때문에 발생하지 않습니다.
#define STR_MEMBER(S,X) (sizeof(S::X), #X)
sizeof
.offsetof
,offsetof
표준이 아닌 정적 멤버 및 클래스에는 사용할 수 없습니다. layout:
18
의 은 이 4 형을 들어,원자)합니다.우제9항),다. [...]다. [...]다. [...]
언어는 "실전 실행"에 대해 아무 말도 할 필요가 없습니다. 왜냐하면 as-if 규칙 때문입니다.결국 부작용이 없는데 어떻게 그 표현이 평가되는지 알 수 있겠습니까?(어셈블리를 보거나 중단점을 설정하는 것은 중요하지 않으며, 프로그램 실행의 일부가 아니라 언어가 설명하는 전부입니다.)
반면, 널 포인터를 재참조하는 것은 정의되지 않은 행동이기 때문에, 이 언어는 무슨 일이 일어나는지 전혀 말하지 않습니다.다른 방식으로 가능한 구현 제한을 완화하는 것이고, 정의되지 않은 동작은 구현에 대한 모든 제한을 완화하는 것입니다.따라서 "이것은 부작용이 없기 때문에 무시할 수 있다"와 "이것은 정의되지 않은 행동이므로 코마귀" 사이에는 "갈등"이 없습니다. 그들은 같은 편입니다!
언급URL : https://stackoverflow.com/questions/46331625/in-the-comma-operator-is-the-left-operand-guaranteed-not-to-be-actually-execute
'programing' 카테고리의 다른 글
문자열에서 처음 100자 출력 (0) | 2023.10.08 |
---|---|
LLDB(Swift):원시 주소를 사용 가능한 유형으로 주조 (0) | 2023.10.03 |
파이썬의 요소를 얻으려면 어떻게 해야 합니까?XML 파일에 인쇄할 트리? (0) | 2023.10.03 |
프로그래밍 방식으로 백스택의 이전 프래그먼트로 돌아갑니다. (0) | 2023.10.03 |
여러 파일에서 컨트롤러를 정의하는 방법 - AngularJs (0) | 2023.10.03 |