포인터를 역 참조하지 않고 범위를 벗어난 포인터를 유지하는 것이 안전합니까? [복제]

Jan 10 2021

C에서 추가 산술을 위해 포인터를 역 참조하지 않고 범위를 벗어난 상태로 유지하는 것이 안전합니까?

void f(int *array)
{
    int *i = array - 1;  // OOB

    while(...) {
        ++i;
        ...
    }
}

void g(int *array, int *end /* past-the-end pointer: OOB */)
{
    while(array != end) {
        ...
        ++array;
    }
}

주소가 메모리의 첫 번째이거나 마지막 주소라면 극단적 인 경우를 상상합니다.

답변

9 MikeCAT Jan 10 2021 at 07:31

포인터를 마지막 요소를지나 한 요소로 이동하는 것은 허용되지만 더 멀리 이동하거나 첫 번째 요소 이전으로 이동할 수 없습니다.

N1570 6.5.6 덧셈 연산자의 인용문 (포인트 8) :

정수 유형이있는 표현식이 포인터에서 더해 지거나 뺄 때 결과는 포인터 피연산자의 유형을 갖습니다. 포인터 피연산자가 배열 개체의 요소를 가리키고 배열이 충분히 큰 경우 결과는 결과와 원래 배열 요소의 첨자의 차이가 정수 식과 같도록 원래 요소에서 오프셋 된 요소를 가리 킵니다. 즉, 표현식 P가 배열 객체의 i 번째 요소를 가리키는 경우 표현식 (P) + N (동등하게 N + (P)) 및 (P) -N (여기서 N은 n 값) 배열 객체의 i + n 번째 요소와 i-n 번째 요소 (존재하는 경우)에 각각. 또한 표현식 P가 배열 객체의 마지막 요소를 가리키면 표현식 (P) +1이 배열 객체의 마지막 요소를 하나 지나고, 표현식 Q가 배열 객체의 마지막 요소지나 하나를 가리키면, 표현식 (Q) -1은 배열 객체의 마지막 요소를 가리 킵니다. 포인터 피연산자와 결과가 동일한 배열 개체의 요소를 가리 키거나 배열 개체의 마지막 요소를 지나는 요소를 가리키는 경우 평가는 오버플로를 생성하지 않습니다. 그렇지 않으면 동작이 정의되지 않습니다. 결과가 배열 객체의 마지막 요소를 하나 지나면 평가되는 단항 * 연산자의 피연산자로 사용되지 않습니다.

3 dbush Jan 10 2021 at 07:32

포인터는 배열의 마지막 요소를 지나는 한 요소를 가리킬 수 있으며 포인터 산술은 해당 포인터와 배열 요소에 대한 포인터 사이에서 수행 될 수 있습니다.

이러한 포인터는 역 참조 할 수 없지만 포인터 산술에 사용할 수 있습니다. 예를 들어 다음은 유효합니다.

char arr[10];
char *p1, *p2;
p1 = arr + 10;
p2 = arr + 5;
int diff = p1 - p2;
printf("diff=%d\n", diff);   // prints 5

포인터는 첫 번째 요소 앞을 가리킬 수 없습니다 .

이것은 C 표준 의 섹션 6.5.6p8에 설명되어 있습니다 .

정수 유형이있는 표현식이 포인터에서 더해 지거나 뺄 때 결과는 포인터 피연산자의 유형을 갖습니다. 포인터 피연산자가 배열 개체의 요소를 가리키고 배열이 충분히 큰 경우 결과는 결과와 원래 배열 요소의 첨자의 차이가 정수 식과 같도록 원래 요소에서 오프셋 된 요소를 가리 킵니다. 즉, 표현식 이 배열 객체 Pi 번째 요소를 가리키는 경우 표현식 (P)+N(동등하게, N+(P)) 및 (P)-N( nN 값이있는 경우 ) 은 각각 i + n 번째 및 i−n 번째를 가리 킵니다. 배열 개체의 요소 (존재하는 경우) 또한 표현식 이 배열 객체의 마지막 요소를 가리키면 표현식이 배열 객체 의 마지막 요소를 하나 지나고 , 표현식 이 배열 객체 의 마지막 요소를지나 하나를 가리키면 표현식 은 마지막 요소를 가리 킵니다. 배열 개체의. 포인터 피연산자와 결과가 동일한 배열 개체의 요소를 가리 키거나 배열 개체의 마지막 요소를 지나는 요소를 가리키는 경우 평가는 오버플로를 생성하지 않습니다. 그렇지 않으면 동작이 정의되지 않습니다. 결과가 배열 객체의 마지막 요소를 하나 지나면 평가 되는 단항 연산자 의 피연산자로 사용되지 않습니다 .P(P)+1Q(Q)-1*

포인터가 배열의 끝을지나 한 요소를 가리 키도록 만들 수 있으며 배열 시작 이전의 어떤 지점을 가리 키도록 허용하는 것이 없다는 것을 나타내는 굵은 부분이 있습니다.

klutt Jan 10 2021 at 08:19

다른 사람들이 지적했듯이, 당신은 과거 를 가리킬 수 있습니다. 그러나 첫 번째 요소 앞에 한 요소를 가리킬 수 없습니다 . 따라서 배열을 역방향으로 탐색하는 알고리즘을 작성하는 경우주의해야 할 수 있습니다. 이 스 니펫이 유효하지 않기 때문에 :

void foo(int *arr, int *end) {
    while(end-- != arr) { // Ouch, bad idea...
        // Code
    }
    // Here, end has the value arr[-1]
}

즉의 때문에 때 end와 동일한 요소를 가리키는 arr조건이 거짓 수 있지만, 이후 것을 end다시 한번 감소되며, 하나 개의 요소를 가리 전에 이에 정의되지 않은 동작을 호출 어레이.

그 외에는 코드가 잘 작동합니다. 버그를 수정하려면 대신 다음을 수행 할 수 있습니다.

void foo(int *arr, int *end) {
    while(end != arr) { 
        end--; // Move end-- to inside the loop, in the very beginning

        // Code
    }

    // And here, end is equal to arr, which is perfectly fine
}

루프의 코드는 이전과 동일하게 작동합니다. 유일한 차이점은 end마지막으로 감소되지 않는다는 것입니다.