비 const에 대한 포인터와 동일한 주소의 const 인수에 대한 포인터를 가진 함수 호출 작성하고 싶습니다. 두

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

두 경우 나는 결과 무엇인지 궁금 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원본 코드에서 삭제하면 잘 정의 되어 있습니까?



답변

동작이 잘 정의되어있는 것은 사실이지만 그렇지 않습니다. 있다는 사실이지만 컴파일러가 의미하는 바에 따라 “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.


답변

이것은 잘 정의되어 있으며 (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안전?


답변

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

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

(C11 §6.5 7)

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

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