왜 C 컴파일러는 같은 변수에 대한 두 개의 포인터가 불법/UB일 것이라고 가정하고 const pointer의 값을 변경하는 것을 최적화할 수 없는가?
최근 Rust와 C의 비교에 실패했는데, 다음 코드를 사용하고 있습니다.
bool f(int* a, const int* b) {
*a = 2;
int ret = *b;
*a = 3;
return ret != 0;
}
Rust(동일한 코드, 그러나 Rust 구문을 사용하는 경우)에서는 다음과 같은 어셈블러 코드가 생성됩니다.
cmp dword ptr [rsi], 0
mov dword ptr [rdi], 3
setne al
ret
gcc를 사용하면 다음과 같은 결과가 나타납니다.
mov DWORD PTR [rdi], 2
mov eax, DWORD PTR [rsi]
mov DWORD PTR [rdi], 3
test eax, eax
setne al
ret
텍스트는 C 함수가 첫 번째 줄의 위치를 최적화할 수 없다고 주장합니다.a
그리고.b
같은 번호를 가리킬 수 있습니다.Rust에서는 이것이 허용되지 않기 때문에 컴파일러는 이를 최적화하여 제거할 수 있습니다.
이제 질문 드리겠습니다.
이 함수는const int*
const int에 대한 포인터입니다.이 질문을 읽었는데 포인터로 const int를 수정하면 컴파일러 경고와 UB에서 최악의 캐스트가 발생한다고 되어 있습니다.
같은 정수에 대한 포인터 2개로 호출하면 이 함수가 UB가 될 수 있습니까?
왜 C 컴파일러는 같은 변수에 대한 두 개의 포인터가 불법/UB일 것이라는 가정 하에 첫 번째 줄 떨어진 곳을 최적화할 수 없는가?
왜 C 컴파일러는 같은 변수에 대한 두 개의 포인터가 불법/UB일 것이라는 가정 하에 첫 번째 줄의 컴파일러를 최적화할 수 없는가?
C 컴파일러에 지시하지 않았기 때문에 C 컴파일러는 그러한 가정을 할 수 있습니다.
C에는 정확히 이 에 대한 유형 한정자가 있습니다.restrict
즉, 이 포인터는 다른 포인터와 겹치지 않습니다(정확하지는 않지만 따라 재생합니다).
의 어셈블리 출력
bool f(int* restrict a, const int* b) {
*a = 2;
int ret = *b;
*a = 3;
return ret != 0;
}
이
mov eax, DWORD PTR [rsi]
mov DWORD PTR [rdi], 3
test eax, eax
setne al
ret
...할당을삭제/삭제합니다.*a = 2
https://en.wikipedia.org/wiki/Restrict 에서
C 프로그래밍 언어에서 restrict는 포인터 선언에서 사용할 수 있는 키워드입니다.이 타입 한정자를 추가함으로써 프로그래머는 포인터의 수명 동안 포인터 자체 또는 포인터로부터 직접 파생된 값(예를 들어 포인터 + 1)만이 포인터가 가리키는 오브젝트에 액세스하기 위해 사용될 것임을 컴파일러에 암시합니다.
함수int f(int *a, const int *b);
내용을 바꾸지 않겠다는 약속b
포인터를 통해...이 프로그램은 이 시스템을 통한 변수 접근에 대한 어떠한 약속도 하지 않습니다.a
포인터
한다면a
그리고.b
같은 오브젝트를 포인트 하는 것은 합법입니다(기본 오브젝트를 변경할 수 있는 경우).
예:
int val = 0;
f(&val, &val);
다른 답변은 C쪽을 언급하고 있지만, Rust쪽을 살펴볼 필요가 있습니다.Rust를 사용하는 코드는 다음과 같습니다.
fn f(a:&mut i32, b:&i32)->bool{
*a = 2;
let ret = *b;
*a = 3;
return ret != 0;
}
이 함수는 두 개의 참조를 사용합니다. 하나는 가변이고 하나는 비변환입니다.참조는 읽기에 유효함을 보증하는 포인터이며, 가변 참조도 고유함을 보증하기 때문에 다음과 같이 최적화됩니다.
cmp dword ptr [rsi], 0
mov dword ptr [rdi], 3
setne al
ret
그러나 Rust는 또한 C의 포인터와 동등한 원시 포인터를 가지고 있으며 그러한 보증을 하지 않습니다.다음 함수는 원시 포인터를 사용합니다.
unsafe fn g(a:*mut i32, b:*const i32)->bool{
*a = 2;
let ret = *b;
*a = 3;
return ret != 0;
}
는 최적화를 생략하고 다음과 같이 컴파일합니다.
mov dword ptr [rdi], 2
cmp dword ptr [rsi], 0
mov dword ptr [rdi], 3
setne al
ret
이 함수는
const int*
const int에 대한 포인터입니다.
아니요, const int에 대한 포인터가 아닙니다.그런 말을 하는 사람은 누구나 착각하고 있다.
int*
확실히 일정하지 않은 int에 대한 포인터입니다.const int*
는 알 수 없는 항상성의 int에 대한 포인터입니다.확실히 일정한 int에 대한 포인터의 개념을 표현할 방법은 없습니다.
만약 C가 더 잘 디자인된 언어라면,const int *
경찰서에 대한 포인터가 될 수 있습니다.mutable int *
(키워드를 C++에서 차용)는 비정수 int로의 포인터가 됩니다.int *
알 수 없는 항상성의 int에 대한 포인터가 될 수 있습니다.예선 탈락(즉, 포인트 투 타입에 대한 무언가를 잊어버리는 것)은 안전할 것입니다. 즉, 실제 C가 추가되는 것과는 반대입니다.const
한정자는 안전합니다.Rust를 사용한 적은 없습니다만, 다른 회답의 예에서는, 그러한 구문을 사용하고 있는 것 같습니다.
Bjarne Stroustrup, 는 다음과 같이 소개했습니다.const
, 원래 이름은 이었습니다.readonly
그것은 실제의 의미에 훨씬 더 가깝다. int readonly*
포인터가 읽기 전용이라는 걸 분명히 했을 거야 가리키는 물체가 아니라로의 이름const
프로그래머 세대들을 혼란스럽게 했습니다.
내가 선택할 수 있을 때, 나는 항상 글을 쓴다.foo const*
,것은 아니다.const foo*
차선책으로서readonly*
.
이 질문에서는 다음 항목에 대한 최적화에 대해 설명한다는 점에 주의해 주십시오.-Ofast
어떻게 그런지도 모르니까요
기본적으로 함수의 C 컴파일러는 여러 변환 유닛에서 함수를 호출할 수 있기 때문에 링크 타임/런타임이 될 때까지 전달될 수 있는 완전한 이산 주소 집합을 알지 못합니다.따라서 C 컴파일러는 다음과 같은 법적 주소를 처리하는 것을 고려합니다.a
★★★★★★★★★★★★★★★★★」b
물론 두 가지가 겹치는 경우를 가리키기도 합니다.
때문에 이렇게 '아까', '아까', '아까', '아까', '아까'를 사용해야 합니다.restrict
a
때문에 상수에서 수 는 값을 하지 않습니다b
있습니다되어야 하기 에, 「0」에 격납되어 있습니다.이것은 0과의 비교에 포함할 필요가 있습니다.따라서 스토어는a
이는 비교를 진행하기 전에 발생하지만 녹이 슬었을 경우 기본 가정은 제한적입니다. 이 는 이 함수에 알고 있습니다.*a
is is is is is와 *(a+1-1)
스토어는 2개의 스토어는 .a
★★★★★★★★★★★★★★★★★」b
다
언급URL : https://stackoverflow.com/questions/65993616/why-cant-the-c-compiler-optimize-changing-the-value-of-a-const-pointer-assuming
'programing' 카테고리의 다른 글
Symfony 3.3에서 프런트 엔드 스택을 처리하는 방법 (0) | 2022.08.10 |
---|---|
vue 템플릿에서 구분되지 않는 공간이 일반 공간으로 렌더링됩니다. (0) | 2022.08.10 |
비동기 함수의 디바운스 사용방법 (0) | 2022.08.10 |
Vuex를 사용할 때 잘못된 점은 무엇입니까?가게? (0) | 2022.08.10 |
Vue의 v-for 루프 내에서 항목을 console.log하는 방법 (0) | 2022.08.10 |