programing

GCC가 C99에서 건설 구조물의 필드를 수정하는 것에 대해 경고할 수 있습니까?

oldcodes 2023. 10. 31. 22:40
반응형

GCC가 C99에서 건설 구조물의 필드를 수정하는 것에 대해 경고할 수 있습니까?

코드를 수정하려고 하다가 작은 문제를 발견했습니다.

저는 컴파일러에게 "구조를 수정하고 있는지 알려주세요, 왜냐하면 저는 정말 그렇게 하고 싶지 않기 때문입니다."라고 말하기 위해 구조에 포인터를 가져다 주는 함수를 작성하고 싶었습니다.

컴파일러가 이 작업을 허용할 것이라는 생각이 갑자기 들었습니다.

struct A
{
    char *ptrChar;
};

void f(const struct A *ptrA)
{
    ptrA->ptrChar[0] = 'A'; // NOT DESIRED!!
}

이는 이해할 수 있습니다. 왜냐하면 실제로 일정한 것은 포인터 자체이지 가리키는 유형이 아니기 때문입니다.가능하다면 컴파일러가 내가 하고 싶지 않은 일을 하고 있다고 말해줬으면 합니다.

저는 gcc를 컴파일러로 사용했습니다.위의 코드가 합법적이어야 한다는 것을 알고 있지만, 그래도 경고가 나올지는 확인했지만 아무 것도 오지 않았습니다.나의 명령행은 다음과 같습니다.

gcc -std=c99 -Wall -Wextra -pedantic test.c

이 문제를 해결할 수 있습니까?

필요한 경우 이 방법을 설계하는 방법은 읽기/쓰기 유형과 읽기 전용 유형의 두 가지 유형을 동일한 개체에 사용하는 것입니다.

typedef struct
{
  char *ptrChar;
} A_rw;

typedef struct
{
  const char* ptrChar;
} A_ro;


typedef union
{
  A_rw rw;
  A_ro ro;  
} A;

함수가 개체를 수정해야 할 경우 매개 변수로 읽기-쓰기 유형을 사용하고 그렇지 않으면 읽기 전용 유형을 사용합니다.

void modify (A_rw* a)
{
  a->ptrChar[0] = 'A';
}

void print (const A_ro* a)
{
  puts(a->ptrChar);
}

호출자 인터페이스를 예쁘게 하고 일관성을 유지하기 위해 ADT의 공용 인터페이스로 래퍼 기능을 사용할 수 있습니다.

inline void A_modify (A* a)
{
  modify(&a->rw);
}

inline void A_print (const A* a)
{
  print(&a->ro);
}

이 방법으로.A이제 호출자에 대한 구현을 숨기기 위해 불투명 유형으로 구현할 수 있습니다.

이것은 구현 대 인터페이스, 또는 "정보 숨기기", 또는 오히려 숨김이 없는 ;-) 문제의 예입니다.C++에서는 단순히 포인터를 비공개로 하고 적합한 퍼블릭 콘스탄트 액세스자를 정의합니다.또는 접근자와 추상 클래스("인터페이스")를 정의합니다.적절한 구조가 그것을 구현할 것입니다.구조 인스턴스를 만들 필요가 없는 사용자는 인터페이스만 보면 됩니다.

C에서 구조에 대한 포인터를 매개 변수로 사용하고 포인터를 constchar로 반환하는 함수를 정의함으로써 에뮬레이션할 수 있습니다.이러한 구조의 인스턴스를 생성하지 않는 사용자의 경우 구조의 구현을 유출하지 않고 포인터를 취하는 함수(또는 공장처럼 반환)를 조작하는 것만 정의하는 "사용자 헤더"를 제공할 수도 있습니다.이로 인해 구조물은 불완전한 유형으로 남게 됩니다(인스턴스에 대한 포인터만 사용할 수 있습니다).이 패턴은 효과적으로 C++가 그 뒤에서 하는 일을 에뮬레이트합니다.this포인팅합니다.

C11을 사용하기로 결정한 경우 동일한 멤버의 상수 또는 변수 버전을 참조하는 Generic 매크로를 구현할 수 있습니다(구조에 유니언도 포함해야 함).이와 같은 것:

struct A
{
    union {
        char *m_ptrChar;

        const char *m_cptrChar;
    } ;
};

#define ptrChar_m(a) _Generic(a, struct A *: a->m_ptrChar,        \
                                 const struct A *: a->m_cptrChar)//,  \
                                 //struct A: a.m_ptrChar,        \
                                 //const struct A: a.m_cptrChar)

void f(const struct A *ptrA)
{
    ptrChar_m(ptrA) = 'A'; // NOT DESIRED!!
}

조합은 단일 구성원에 대해 2가지 해석을 생성합니다. 더m_cptrChar는 상수 char를 가리키는 포인터이고 그리고m_ptrChar 에게 그런 다음 는 매개 변수의에 따라합니다.그런 다음 매크로는 매개 변수의 유형에 따라 참조할 것을 결정합니다.

유일한 문제는 매크로가ptrChar_m는 이 구조의 포인터 또는 객체에 대해서만 작동할 수 있으며 둘 다 작동할 수 없습니다.

이것은 C 언어의 알려진 문제이므로 피할 수 없습니다.결국 구조를 수정하는 것이 아니라 non을 통해 다른 개체를 수정하는 것입니다.const- 구조물에서 획득한 적격 포인터.const시맨틱은 원래 물리적으로 쓸 수 없는 일정한 기억 영역을 표시할 필요성을 중심으로 설계되었습니다. 방어적 프로그래밍에 대한 우려가 아니라 말이죠.

일부 "액세스자" 기능 뒤에 정보를 숨길 수 있습니다.

// header
struct A;           // incomplete type
char *getPtr(struct A *);
const char *getCPtr(const struct A *);

// implementation
struct A { char *ptr };
char *getPtr(struct A *a) { return a->ptr; }
const char *getCPtr(const struct A *a) { return a->ptr; }

아니요, 구조 정의를 다음으로 변경하지 않는 한:

struct A
{
    const char *ptrChar;
};

이전 구조 정의를 그대로 유지하는 또 다른 복잡한 해결책은 동일한 멤버를 가진 새로운 구조를 정의하는 것이며, 이 구조의 관련 포인터 멤버는 다음과 같이 설정됩니다: points to const type.그러면 전화하시는 기능이 새로운 구조로 변경됩니다.기존 구조를 취하고 멤버별로 새 구조로 복사하여 함수로 전달하는 래퍼 함수가 정의됩니다.

GCC가 C99에서 건설 구조물의 필드를 수정하는 것에 대해 경고할 수 있습니까?

구조물의 필드를 수정하고 있지 않습니다.

구조 A의 값에는 비구분 문자에 대한 포인터가 포함되어 있습니다. ptrA는 구조 A에 대한 포인터입니다.따라서 *ptrA에서는 구조 A 값을 변경할 수 없습니다.따라서 포인터를 문자(*ptrA)로 변경할 수 없습니다.Characa ptrA-> ptrChar.그러나 ptrA->ptrChar가 가리키는 곳의 값, 즉 *(ptrA->Char)의 값을 ptrA->Char[0]로 변경하는 것입니다.여기서 유일한 상수는 구조 A뿐이고 구조 A를 변경하지 않을 것인데 정확히 "원하지 않는" 것은 무엇입니까?

구조 A의 Char 필드가 가리키는 값(구조 A를 통해)에 변경을 허용하지 않으려면 다음을 사용합니다.

struct A
{
    const char *ptrChar; // or char const *ptrChar;
};

하지만 아마도 당신이 생각하는 당신이 inf에서 하고 있는 것은

void f(const struct A *ptrA)
{
    const char c = 'A';
    ptrA->ptrChar = &c;
}

컴파일러에서 오류가 발생합니다.

언급URL : https://stackoverflow.com/questions/35646493/can-gcc-warn-me-about-modifying-the-fields-of-a-const-struct-in-c99

반응형