비 const에 대한 포인터와 동일한 주소의 const 인수에 대한 포인터를 가진 함수 호출


14

데이터 배열을 입력하고 포인터를 사용하여 다른 데이터 배열을 출력하는 함수를 작성하고 싶습니다.

두 경우 나는 결과 무엇인지 궁금 srcdst같은 주소로 지적 내가 컴파일러는 const를 위해 최적화 할 수 있습니다 알고 있기 때문에. 정의되지 않은 동작입니까? (대답이 서로 다를 수 있는지 확실하지 않기 때문에 C와 C ++ 모두에 태그를 지정했으며 둘 다에 대해 알고 싶습니다.)

void f(const char *src, char *dst) {
    dst[2] = src[0];
    dst[1] = src[1];
    dst[0] = src[2];
}

int main() {
    char s[] = "123";
    f(s,s);
    printf("%s\n", s);
    return 0;
}

위의 질문 외에도 const원본 코드에서 삭제하면 잘 정의 되어 있습니까?

답변:


17

동작이 잘 정의되어있는 것은 사실이지만 그렇지 않습니다. 있다는 사실이지만 컴파일러가 의미하는 바에 따라 "const를 최적화"할 수 .

즉, 컴파일러가되어 있지 허용 매개 변수가를 단지 때문에 가정 const T* ptr에 의해, 메모리는 지적 ptr다른 포인터를 통해 변경되지 않습니다. 포인터가 같을 필요도 없습니다. 그만큼const 보증이 아니라 의무입니다. 해당 포인터를 통해 변경하지 말아야 할 의무는 사용자 (= 기능)입니다.

실제로 그러한 보증을 받으려면 restrict키워드로 포인터를 표시해야 합니다. 따라서이 두 함수를 컴파일하면

int foo(const int* x, int* y) {
    int result = *x;
    (*y)++;
    return result + *x;
}

int bar(const int* x, int* restrict y) {
    int result = *x;
    (*y)++;
    return result + *x;
}

foo()기능은 두 번에서 읽을 수 있어야 x하지만, bar()한 번만 읽을 필요가 :

foo:
        mov     eax, DWORD PTR [rdi]
        add     DWORD PTR [rsi], 1
        add     eax, DWORD PTR [rdi]  # second read
        ret
bar:
        mov     eax, DWORD PTR [rdi]
        add     DWORD PTR [rsi], 1
        add     eax, eax              # no second read
        ret

이 라이브를 참조하십시오 GodBolt.

restrictC의 키워드입니다 (C99 이후). 불행히도, 지금까지 C ++에 도입되지 않았습니다 (C ++에서 도입하기가 더 어려운 열악한 이유로). 그러나 많은 컴파일러가이를 지원합니다.__restrict .

결론 : 컴파일러는 컴파일 할 때 "비밀"사용 사례를 지원해야하며 f()문제가 발생하지 않습니다.


의 사용 사례에 대해서는 이 게시물을 참조하십시오 restrict.


const"포인터를 통해 변경을하지 말아야 할 의무"(= 기능)가 아닙니다. C 표준은 함수가 const캐스트를 통해 제거 하고 결과를 통해 객체를 수정하도록 허용합니다 . 본질적으로, const의도하지 않은 객체 수정을 피하기 위해 프로그래머에게 조언과 편의를 제공합니다.
에릭 Postpischil

@EricPostpischil : 그것은 당신이 얻을 수있는 의무입니다.
einpoklum

귀하가 얻을 수있는 의무는 의무가 아닙니다.
Eric Postpischil

2
@EricPostpischil : 1. 당신은 여기서 머리카락을 나누고 있습니다. 2. 사실이 아닙니다.
einpoklum

1
이 이유 memcpystrcpy함께 선언 restrict하면서 인수 memmove하지 않습니다 - 단지 후자는 메모리 블록 사이에 중첩 할 수 있습니다.
Barmar

5

이것은 잘 정의되어 있으며 (C ++에서는 더 이상 확실하지 않음) const 한정자가 있거나 .

가장 먼저 찾아야 할 것은 엄격한 앨리어싱 규칙 1 입니다. 만약 srcdst같은 객체에 대한 포인트 :

const한정자에 관해서는 dst == src함수가 어떤 src점을 효과적으로 수정할 때로 src자격이 없어야 한다고 주장 할 수 있습니다 const. 이것은 const작동 하지 않습니다 . 두 가지 경우를 고려해야합니다.

  1. const에서처럼 객체가로 정의 된 char const data[42];경우 (직접 또는 간접적으로) 수정하면 정의되지 않은 동작이 발생합니다.
  2. const에서와 같이 객체에 대한 참조 또는 포인터 가 정의되면 객체가 2char const* pdata = data; 로 정의되지 않은 경우 기본 객체를 수정할 수 있습니다 (1 참조). 따라서 다음은 잘 정의되어 있습니다.const
int main()
{
    int result = 42;
    int const* presult = &result;
    *const_cast<int*>(presult) = 0;
    return *presult; // 0
}

1) 엄격한 앨리어싱 규칙은 무엇입니까?
2) const_cast안전?


OP가 과제의 재정렬을 의미 할 수 있습니까?
Igor R.

char*char const*호환되지 않습니다. _Generic((char *) 0, const char *: 1, default: 0))0으로 평가됩니다.
에릭 Postpischil

" const객체에 대한 참조 또는 포인터 가 정의 된 경우" 라는 문구 가 올바르지 않습니다. const정규화 된 유형에 대한 참조 또는 포인터 가 정의 된 경우,이를 가리 키도록 설정된 객체가 다양한 방법으로 수정되지 않을 수 있음을 의미하지는 않습니다. (포인터가 const오브젝트 를 가리키는 경우, 이는 오브젝트가 실제로 const정의에 의한 것이므로이를 수정하려는 동작이 정의되지 않음을 의미합니다.)
Eric Postpischil

@ 에릭, 나는 표준에 관한 질문이나 태그가있을 때만 특정합니다 language-lawyer. Exactitude는 내가 소중히 여기는 가치이지만 더 복잡하다는 것도 알고 있습니다. 여기서는 단순하고 이해하기 쉬운 문장을 찾기로 결정했습니다. 그렇지 않다고 생각하면 대답을 해보자. 어쨌든 귀하의 의견에 감사드립니다.
YSC

3

이는 C에 잘 정의되어 있습니다. 엄격한 별칭 지정 규칙은 char유형 또는 동일한 유형의 두 포인터에 적용되지 않습니다 .

"최적화"가 무엇을 의미하는지 잘 모르겠습니다 const. 내 컴파일러 (GCC 8.3.0 x86-64)는 두 경우 모두에 대해 동일한 코드를 생성합니다. 추가하면restrict포인터에 지정자를 생성 된 코드가 약간 나아지지만 포인터는 동일합니다.

(C11 §6.5 7)

객체는 다음 유형 중 하나를 갖는 lvalue 표현식에 의해서만 저장된 값에 액세스해야합니다.
— 객체
의 유효 유형과 호환되는 유형
— 객체의 유효 유형과 호환되는 유형의 정규화 된 버전 — 오브젝트의 유효 유형에 해당하는 부호있는 유형 또는 부호없는 유형 인 유형 (유효한 오브젝트
유형의 규정 된 버전에 해당하는 부호있는 유형 또는 부호없는 유형 인 유형)
-하나를 포함하는 집합 또는 공용체 유형 재귀 적으로 하위 집합 또는 포함 된 공용체의 멤버를 포함하여 전술 한 유형 중 하나 또는
문자 유형.

이 경우 (없이 restrict) 항상 121결과를 얻습니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.