GCC能否提醒我修改C99中的const结构的字段?

我偶然发现一个小问题,同时试图做出正确的代码。

我会喜欢写一个函数,它需要一个指向const结构的指针,告诉编译器“请告诉我,如果我正在修改结构,因为我真的不想”。

我突然想到,编译器会允许我这样做:

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

这是可以理解的,因为实际上const是指针本身,而不是它指向的types。 我希望编译器告诉我,我正在做一些我不想做的事情,尽pipe如此,甚至有可能。

我使用gcc作为我的编译器。 虽然我知道上面的代码应该是合法的,但我仍然检查是否会发出警告,但没有任何结果。 我的命令是:

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

是否有可能解决这个问题?

如果需要的话,一种devise你的解决方法是为同一个对象使用两种不同的types:一种是读/写types和一种是只读types。

 typedef struct { char *ptrChar; } A_rw; typedef struct { const char* ptrChar; } A_ro; typedef union { A_rw rw; A_ro ro; } A; 

如果函数需要修改对象,则以读写types为参数,否则为只读types。

 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实现为不透明types,以隐藏调用者的实现。

这是实现与接口或“信息隐藏” – 或者说非隐藏—)问题的一个例子。 在C ++中,只需要指针是私有的,并定义合适的公共const访问器。 或者可以定义一个抽象类 – 一个“接口” – 访问者。 结构本身将实现。 不需要创build结构实例的用户只需要看到接口。

在C中,可以通过定义一个将指针指向struct的函数作为参数并返回一个指向const char的指针来模拟。 对于不创build这些结构实例的用户,甚至可以提供一个“用户头”,它不会泄露结构的实现,但只定义了操作函数(或像工厂一样返回)指针。 这留下了结构不完整的types(以便只能使用指向实例的指针)。 这个模式有效地模拟了C ++在后台使用this指针所做的this

这是C语言的一个已知问题,不可避免。 毕竟,你并没有修改结构,而是通过从结构中获得的非const限定指针来修改单独的对象。 const语义最初是围绕需要将内存区域标记为常量而devise的,而物理上不可写,而不是围绕防御性编程的任何问题。

也许如果你决定使用C11,你可以实现一个generics的macros,它指向同一成员的常量或variables版本(你也应该在你的结构中包含一个联合)。 像这样的东西:

 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!! } 

工会为单个成员创build2个解释。 m_cptrChar是指向常量char的指针, m_ptrChar指向非常量。 然后macros根据参数的types决定要引用哪一个。

唯一的问题是,macrosptrChar_m只能使用这个结构的指针或对象,而不是两者。

我们可以隐藏一些“访问器”函数的信息:

 // 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; }; 

保持旧的结构定义完整的另一个复杂的解决scheme是定义一个具有相同成员的新结构,其相关的指针成员被设置为:指向consttypes。 然后,您正在调用的函数将更改为新的结构。 定义了一个包装函数,该函数采用旧的结构,通过成员将成员复制到新结构并将其传递给函数。

GCC能否提醒我修改C99中的const结构的字段?

你不修改一个const结构的字段。

struct A的值包含一个指向非const char的指针。 ptrA是一个指向const结构A的指针。所以你不能在* ptrA上改变struct A的值。 所以你不能把指针改为(* ptrA).Char aka ptrA-> ptrChar。 但是你正在改变ptrA-> ptrChar指向的值,即*(ptrA-> Char)又名ptrA-> Char [0]的值。 这里唯一的常量是结构体,而你并没有改变一个结构体A,所以精确性是“不需要的”?

如果你不想允许改变结构A的Char字段指向的值(通过结构A),那么就使用它

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

但是也许你认为你在f中做的是类似的

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

这将从编译器中得到一个错误。