C 표준이 constness를 재귀 적으로 고려하는 이유는 무엇입니까?


9

C99 표준은 6.5.16 : 2에 나와 있습니다.

할당 연산자는 왼쪽 피연산자로 수정 가능한 lvalue를 가져야합니다.

그리고 6.3.2.1:1에서 :

수정 가능한 lvalue는 배열 유형이없고 불완전한 유형이 없으며 const 한정 유형이 없으며 구조 또는 공용체 인 경우 멤버를 포함하지 않는 lvalue입니다 (재귀 적으로 모든 멤버 포함) const 한정 유형의 모든 포함 된 집계 또는 공용체의 요소).

이제, 비 살펴 보자 const structA의 const필드.

typedef struct S_s {
    const int _a;
} S_t;

기본적으로 다음 코드는 정의되지 않은 동작 (UB)입니다.

S_t s1;
S_t s2 = { ._a = 2 };
s1 = s2;

이것의 의미 론적 문제는 엔 클로징 엔티티 ( struct)가 선언 된 엔티티 유형 ()으로 판단하여 쓰기 가능 (읽기 전용 S_t s1이 아닌) 것으로 간주되어야하지만 표준의 문구 (2 절)에 의해 쓰기 가능한 것으로 간주되어서는 안된다는 것입니다 const필드로 인해) _a. 표준은 프로그래머가 코드를 읽는 것이 할당이 실제로 UB라는 것을 불분명하게 만듭니다 struct S_s ... S_t. 유형 의 정의없이 말할 수 없기 때문 입니다.

또한, 필드에 대한 읽기 전용 액세스는 어쨌든 구문 적으로 만 적용됩니다. const비- 일부 필드가 const struct실제로 읽기 전용 스토리지에 배치 될 방법은 없습니다 . 그러나 표준에 대한 이러한 표현은 const이러한 필드의 접근 자 절차에서 필드 의 한정자 를 의도적으로 쫓아내는 코드를 불법화합니다 ( C의 구조 필드를 제한하는 것이 좋은 생각입니까 ).

(*)

#include <stdlib.h>
#include <stdio.h>

typedef struct S_s {
    const int _a;
} S_t;

S_t *
create_S(void) {
    return calloc(sizeof(S_t), 1);
}

void
destroy_S(S_t *s) {
    free(s);
}

const int
get_S_a(const S_t *s) {
    return s->_a;
}

void
set_S_a(S_t *s, const int a) {
    int *a_p = (int *)&s->_a;
    *a_p = a;
}

int
main(void) {
    S_t s1;
    // s1._a = 5; // Error
    set_S_a(&s1, 5); // OK
    S_t *s2 = create_S();
    // s2->_a = 8; // Error
    set_S_a(s2, 8); // OK

    printf("s1.a == %d\n", get_S_a(&s1));
    printf("s2->a == %d\n", get_S_a(s2));

    destroy_S(s2);
}

따라서 어떤 이유로 전체 struct를 읽기 전용으로 선언하면 충분합니다.const

const S_t s3;

그러나 전체 struct가 읽기 전용이 아닌 경우 w / o로 선언하는 것만으로는 충분하지 않습니다 const.

내가 더 좋을 것이라고 생각하는 것은 다음 중 하나입니다.

  1. 필드를 사용 하여 비 const구조체 생성을 제한하고 const그러한 경우 진단을 발행합니다. 그것은 struct포함하는 읽기 전용 필드가 읽기 전용 이라는 것을 분명히 합니다.
  2. 위의 코드 (*) 가 표준을 준수하도록 constconst구조에 속하는 필드에 쓸 경우 동작을 정의합니다 .

그렇지 않으면 행동이 일관되고 이해하기 어렵습니다.

그렇다면 C Standard가 const재귀 적 으로 재점 을 고려하는 이유는 무엇 입니까?


솔직히 말해서, 나는 거기에 질문이 보이지 않습니다.
Bart van Ingen Schenau

@BartvanIngenSchenau는 본문 끝의 주제에 언급 된 질문을 추가하기 위해 편집했습니다
Michael Pankov

1
왜 공감해야합니까?
Michael Pankov

답변:


4

그렇다면 C 표준이 constness를 재귀 적으로 고려하는 이유는 무엇입니까?

유형 관점에서 볼 때, 그렇게하지 않으면 소리가 나지 않습니다 (즉, 엄청나게 부서지고 의도적으로 신뢰할 수 없음).

그리고 그것은 "="가 구조체에서 의미하는 것 때문입니다. 그것은 재귀 적 할당입니다. 결국 s1._a = <value>"입력 규칙 내부" 가 발생합니다. 표준이 "중첩 된" const필드에 대해이를 허용하는 경우, 유형 시스템 정의에 명시 적 모순으로 심각한 불일치가 추가됩니다 (정의로 인해 const쓸모없고 신뢰할 수 없게 된 기능을 버릴 수도 있음 ).

내가 이해하는 한 귀하의 솔루션 (1)은 전체 구조가 const필드 중 하나 일 때마다 불필요하게 강제합니다 const. 이런 식 으로을 포함하는 비 const의 비 s1._b = bconst ._b필드에 대해서는 불법입니다 .s1const a


잘. C사운드 타입 시스템은 거의 없다 (수년에 걸쳐 서로 묶여있는 코너 케이스와 비슷하다). 또한 과제를 a에 넣는 다른 방법 structmemcpy(s_dest, s_src, sizeof(S_t))입니다. 그리고 그것이 실제로 구현 된 방법이라고 확신합니다. 그리고 그러한 경우에도 기존의 "유형 시스템"조차 그렇게하지 않습니다.
Michael Pankov

2
매우 사실입니다. 나는 C 타입 시스템이 건전하다는 것을 암시하지 않았고, 특정 의미론을 의도적으로 건전하게 만드는 것이 고의적으로 그것을 패배시켰다. 또한 C의 유형 시스템이 강력하게 적용되지는 않지만 영향을 미치는 방법은 종종 암시적이고 추적하기 어려울지라도 종종 명시 적 (포인터, 간접 액세스, 캐스트) 방법입니다. 따라서, 그것들을 위반하는 명백한 "울타리"를 갖는 것은 정의 자체에 모순되는 것보다 더 나은 정보를 제공합니다.
Thiago Silva

2

그 이유는 읽기 전용 필드가 읽기 전용이기 때문입니다. 큰 놀라움은 없습니다.

ROM에 배치하는 데 유일한 효과가 있다고 가정하고 실수로 인접하지 않은 필드가있는 경우 실제로 불가능합니다. 실제로 최적화 프로그램은 const표현식이 작성되지 않은 것으로 가정 하고이를 기반으로 최적화 할 수 있습니다. 물론 non-const 가명 (alias)이 존재할 때는이 가정이 유지되지 않습니다.

귀하의 솔루션 (1)은 기존의 법적이고 합리적인 코드를 위반합니다. 그런 일은 일어나지 않을 것입니다. 귀하의 솔루션 (2) const은 회원 의 의미를 거의 제거합니다 . 이것이 기존 코드를 위반하지는 않지만 근거가 부족한 것 같습니다.


나는 90 % 확신 최적화 해요 하지 않을 수 있다고 가정 const당신은 항상 사용할 수 있기 때문에 필드에 기록되지 memsetmemcpy, 그리고 심지어 표준을 준수 할 것이다. (1) 최소한 플래그에 의해 활성화되는 추가 경고로 구현 될 수 있습니다. (2)의 이론적 근거는 struct, 전체 구조 쓰기 가능할 때 구성 요소가 쓰기 불가능한 것으로 간주 될 수있는 방법이 없다는 것 입니다.
Michael Pankov

"플래그에 의해 결정된 선택적 진단"은 표준이 요구하는 고유 한 요구 사항입니다. 또한 플래그를 설정하면 기존 코드가 여전히 깨져서 아무도 플래그를 신경 쓰지 않으며 막 다른 골목이 될 것입니다. (2)와 관련하여 6.3.2.1:1은 정반대를 지정합니다 . 하나의 구성 요소가있을 때마다 전체 구조를 쓸 수 없습니다 . 그러나 다른 구성 요소 는 여전히 쓰기 가능할 수 있습니다. Cf. C ++ operator=은 멤버 측면 에서도 정의 하므로 operator=한 멤버가 일 때를 정의하지 않습니다 const. C와 C ++는 여전히 여기에서 호환됩니다.
MSalters

@constantius-고의적으로 구성원의 불변성을 해결하기 위해 무언가를 할 수 있다는 사실은 옵티마이 저가 해당 불변성을 무시하는 이유가 아닙니다. 함수 내에서 불변성을 캐스트하여 물건을 바꿀 수 있습니다. 그러나 호출 컨텍스트의 옵티마이 저는 여전히 그렇지 않다고 가정 할 수 있습니다. 일관성은 프로그래머에게 유용하지만 경우에 따라 옵티 마이저에게도 ​​신이 보냅니다.
Michael Kohne

그렇다면 왜 쓰기 불가능한 구조를 덮어 쓸 수 memcpy있습니까? 다른 이유들-좋아요, 그것은 레거시이지만, 왜 그런 식으로 처음에 그렇게 되었습니까?
Michael Pankov

1
귀하의 의견이 옳은지 여전히 궁금합니다 memcpy. AFACIT John Bode의 다른 질문에 대한 인용문은 옳습니다. 코드는 한정된 객체에 작성되므로 토론이 끝날 때 표준 불만이 아닙니다.
MSalters
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.