C기준은 임의의 값을 포인터에 할당하여 증가시키는 것을 허용합니까?
이 코드의 동작이 잘 정의되어 있습니까?
#include <stdio.h>
#include <stdint.h>
int main(void)
{
void *ptr = (char *)0x01;
size_t val;
ptr = (char *)ptr + 1;
val = (size_t)(uintptr_t)ptr;
printf("%zu\n", val);
return 0;
}
내 말은, 포인터에 고정된 숫자를 할당해서 랜덤 주소를 가리켜도 늘릴 수 있을까?(참조할 수 없다는 것을 알고 있습니다)
과제:
void *ptr = (char *)0x01;
정수를 포인터로 변환하기 때문에 구현에서 정의된 동작입니다.이는 포인터와 관련하여 C 표준의 섹션 6.3.2.3에 자세히 설명되어 있습니다.
5 정수는 임의의 포인터 타입으로 변환할 수 있다.이전에 지정한 경우를 제외하고 결과는 구현 정의이며 올바르게 정렬되지 않을 수 있으며 참조된 유형의 엔티티를 가리키지 않을 수 있으며 트랩 표현일 수 있습니다.
후속 포인터 산술에 대해:
ptr = (char *)ptr + 1;
이것은 몇 가지에 따라 달라집니다.
인 '' '' '' '''ptr
는 위의 6.3.2.3에 따른 트랩 표현일 수 있습니다.이 경우 동작은 정의되지 않습니다.
다음, '있다'는 것이냐의 입니다.0x1
유효한 개체를 가리킵니다.포인터와 정수를 추가하는 것은 포인터 오퍼랜드와 결과 모두 배열 객체의 요소(단일 객체가 크기 1의 배열로 카운트됨) 또는 배열 객체 뒤의 요소 하나를 가리키는 경우에만 유효합니다. 6.5.6에 자세히 설명되어 있습니다.
7 이러한 연산자의 목적상 배열의 요소가 아닌 객체에 대한 포인터는 객체 유형을 요소 유형으로 하여 길이 1의 배열의 첫 번째 요소에 대한 포인터와 동일하게 동작합니다.
8 정수형을 가진 식을 포인터에 추가하거나 포인터에서 빼면 결과에는 포인터 피연산자의 유형이 포함됩니다.포인터 오퍼랜드가 배열 객체의 요소를 가리키고 배열이 충분히 클 경우, 결과는 결과 및 원본 배열 요소의 첨자 차이가 정수식과 같도록 원래 요소로부터의 오프셋을 가리킵니다.즉, 식 P가 배열 객체의 i번째 요소를 가리킬 경우 식 P+N(동등 N+P) 및 (P)-N(여기서 N은 값 n을 가진다)이 각각 제공된 배열 객체의 i+n번째 및 i-n번째 요소를 가리킬 수 있다.또한 식 P가 배열 객체의 마지막 요소를 가리키면 식 (P)+1은 배열 객체의 마지막 요소를 가리키고 식 Q가 배열 객체의 마지막 요소를 가리키면 식 (Q)-1은 배열 객체의 마지막 요소를 가리키고 있다.포인터 오퍼랜드와 결과 모두 동일한 배열 객체의 요소를 가리킬 경우 또는 배열 객체의 마지막 요소를 가리킬 경우 평가에서 오버플로가 발생하지 않습니다.그렇지 않을 경우 동작은 정의되지 않습니다.결과가 배열 객체의 마지막 요소를 1개 지나갔을 경우, 평가되는 단항 * 연산자의 피연산자로 사용할 수 없습니다.
에서는, 「」의 이 됩니다.0x1
유효한 오브젝트를 가리키지 않는 것은 거의 확실합니다.이 경우 추가는 정의되어 있지 않습니다.그러나 임베디드 구현은 특정 값에 대한 포인터 설정을 지원할 수 있으며, 만약 그렇다면 다음과 같은 경우가 있습니다.0x1
실제로는 유효한 오브젝트를 가리키고 있습니다.이 경우 동작은 올바르게 정의되어 있으며, 그렇지 않으면 정의되어 있지 않습니다.
아니요, 이 프로그램의 동작은 정의되어 있지 않습니다.프로그램에서 정의되지 않은 구성에 도달하면 향후 동작은 정의되지 않습니다.역설적으로 과거의 행동도 정의되어 있지 않다.
의 void *ptr = (char*)0x01;
정의되어 으로는 '실장 정의'가입니다.★★★★★★★★★★★★★★★★★★★★★★★★★★★★,char
는 트랩 표현을 가질 수 있습니다.
과 같습니다.ptr = (char *)ptr + 1;
정의되어 있지 않습니다.그 이유는 포인터 산술은 어레이의 끝 부분을 포함한 어레이 내에서만 유효하기 때문입니다.이를 위해 개체는 길이 1의 배열입니다.
정의되지 않은 동작입니다.
N1570부터 (강조 추가):
정수는 임의의 포인터 타입으로 변환할 수 있습니다.이전에 지정한 경우를 제외하고 결과는 구현 정의이며 올바르게 정렬되지 않을 수 있으며 참조된 유형의 엔티티를 가리키지 않을 수 있으며 트랩 표현일 수 있습니다.
값이 트랩 표현일 경우 판독은 정의되지 않은 동작입니다.
특정 개체 표현은 개체 유형의 값을 나타낼 필요가 없습니다.객체의 저장된 값이 이러한 표현을 가지며 문자 유형이 없는 lvalue 식에 의해 읽혀질 경우 동작은 정의되지 않습니다.이러한 표현이 문자 유형이 없는 lvalue 식에 의해 객체의 전부 또는 일부를 수정하는 부작용에 의해 생성되는 경우 동작은 정의되지 않습니다.)이러한 표현을 트랩 표현이라고 합니다.
그리고.
식별자는 객체(이 경우 lvalue) 또는 함수(이 경우 함수 지정자)를 지정하는 것으로 선언된 경우 기본 표현입니다.
행은 「」입니다.void *ptr = (char *)0x01;
는 이미 정의되지 않은 일 가능성이 이에서는, 「가 사용되고 있습니다.(char*)0x01
또는(void*)(char*)0x01
는 트랩 표현입니다.왼쪽은 문자 유형이 없고 트랩 표현을 읽는 lvalue 표현입니다.
일부 하드웨어에서는 기계 레지스터에 비활성 포인터를 로드하면 프로그램이 크래시될 수 있으므로 이는 표준 위원회의 강제 조치였습니다.
이 기준서는 구현에서 특정 정수 값, 또는 Null Pointer 상수 이외의 가능한 정수 값에 대해 의미 있는 방식으로 정수 대 포인터 변환을 처리하도록 요구하지 않는다.이러한 변환에 대해 보장되는 유일한 것은 이러한 변환 결과를 적절한 포인터 유형의 객체에 직접 저장하고 해당 객체의 바이트를 검사하는 것 외에는 아무것도 하지 않는 프로그램이 최악의 경우 지정되지 않은 값을 참조한다는 것입니다.정수를 포인터로 변환하는 동작은 Implementation-Defined(실제로 이러한 변환을 사용하여 무엇을 하든)는 표현의 바이트 중 일부(또는 모든)가 Unspecified(미지정) 값을 가지도록 지정하고 일부(또는 모든) 정수 값이 동작하도록 지정하는 것을 금지할 수 없습니다.단, 트랩 표현을 생성합니다.
이 기준서가 정수 대 포인트 전환에 대해 언급하는 유일한 이유는 다음과 같다.
구현에 따라서는 구축이 의미가 있으며, 이러한 구현에 필요한 프로그램도 있습니다.
이 기준서의 작성자들은 일부 구현에 사용된 구조가 다른 구현에 대한 제약 위반을 나타낼 것이라는 생각이 마음에 들지 않았다.
이 기준서가 구조를 기술하되 모든 경우에 정의되지 않은 행동을 명시하는 것은 이상했을 것이다.
개인적으로, 나는 이 기준서가, 컴파일러가 의미 없는 코드를 받아들이도록 요구하는 것이 아니라, 그들이 유용할 수 있는 상황을 정의하지 않는다면, 그 기준서에서 정수 대 소수점 변환을 제약 위반으로 취급할 수 있도록 허용했어야 한다고 생각하지만, 그 당시에는 그것이 철학이 아니었다.
간단히 말하면 포인터 변환에서 받은 intptr_t 값 또는 uintptr_t 값 이외의 정수에서 포인터로 변환하는 조작은 Undefined Beha를 처리하는 저레벨 프로그래밍을 목적으로 한 품질 구현에 공통되는 것입니다.환경 특유의 문서화된 방식으로 vior.이 표준에서는 구현이 UB를 호출하는 프로그램을 언제 처리해야 하는지 명시하지 않고 대신 구현 품질 문제로 취급합니다.
구현에서 정수에서 포인터로 변환이 동작하도록 지정되어 있는 경우
char *p = (char*)1;
p++;
"char p = (char)2;"와 동등하므로 구현이 이와 같이 작동해야 합니다.한편, 실장에서는, 다음과 같은 방법으로, 정수로부터 포인터로 변환하는 동작을 정의할 수 있습니다.
char *p = (char*)1;
char *q = p; // Not doing any arithmetic here--just a simple assignment
코의 악마를 풀어줄 거야대부분의 플랫폼에서, 정수 대 포인터 변환에 의해 생성된 포인터의 산술이 이상하게 동작하는 컴파일러는 낮은 수준의 프로그래밍에 적합한 고품질 구현으로 간주되지 않을 것이다.따라서 다른 종류의 구현을 목표로 하지 않는 프로그래머는 그러한 구조가 이 기준서에서 요구하지 않더라도 코드가 적합한 컴파일러에서 유용하게 동작할 것으로 예상할 수 있다.
예, 코드는 구현 정의로 잘 정의되어 있습니다.정의되어 있지 않습니다.ISO/IEC 9899:2011 [6.3.2.3]/5 및 주 67을 참조하십시오.
C 언어는 원래 시스템 프로그래밍 언어로 만들어졌습니다.시스템 프로그래밍에서는 메모리 매핑하드웨어를 조작할 필요가 있었습니다.하드 코드화된 주소를 포인터에 삽입하거나 포인터를 늘리거나 데이터를 읽고 쓸 필요가 있었습니다.이를 위해 포인터에 할당 및 정수를 할당하고 산술적으로 포인터를 조작하는 것은 언어에 의해 잘 정의된다.실장 정의로 함으로써, 이 언어에서는, 종래의 정지 후 캐치 파이어로부터, 홀수 주소를 참조 해제하려고 할 때의 버스 에러 발생까지, 모든 종류의 일이 가능하게 됩니다.
정의되지 않은 동작과 구현 정의되지 않은 동작의 차이는 기본적으로 "그렇게 하지 마세요.무슨 일이 일어날지 모릅니다"를 의미하며, 구현 정의 동작은 "그렇게 해도 괜찮습니다.무슨 일이 일어날지는 사용자에게 달려 있습니다"를 의미합니다.
언급URL : https://stackoverflow.com/questions/51083356/does-the-c-standard-permit-assigning-an-arbitrary-value-to-a-pointer-and-increme
'source' 카테고리의 다른 글
포획은 루프 안쪽으로 할까요, 아니면 바깥쪽으로 할까요? (0) | 2022.07.23 |
---|---|
Vue 3 - 대체 Vue.delete (0) | 2022.07.23 |
Vue에 페이지를 남길 때 저장되지 않은 변경 사항을 경고하는 방법은 무엇입니까? (0) | 2022.07.23 |
Mixin 내 VueX (0) | 2022.07.23 |
Vue momentjs는 상대 시간을 실시간으로 업데이트합니다. (0) | 2022.07.17 |