我在尝试制作常量正确的代码时偶然发现了一个小问题。
我本来想写一个带有指向const结构的指针的函数,告诉编译器"请告诉我是否修改结构,因为我真的不想"。
我突然想到编译器会允许我这样做:
struct A
{
char *ptrChar;
};
void f(const struct A *ptrA)
{
ptrA->ptrChar[0] = 'A'; // NOT DESIRED!!
}
这是可以理解的,因为实际上const是指针本身,而不是它指向的类型。我希望编译器告诉我,我做了一些我不想做的事,但是,如果可能的话。
我使用gcc作为我的编译器。虽然我知道上面的代码应该是合法的,但我仍然检查它是否会发出警告,但没有任何结果。我的命令行是:
gcc -std=c99 -Wall -Wextra -pedantic test.c
是否有可能解决这个问题?
答案 0 :(得分:7)
这是实现与界面或“信息隐藏”的一个例子 - 或者说非隐藏;-) - 问题。在C ++中,只需将指针设为私有,并定义合适的公共const访问器。或者可以使用访问者定义抽象类 - “接口”。正确的结构将实现这一点。不需要创建结构实例的用户只需要查看该接口。
在C中,可以通过定义一个函数来模拟它,该函数将指向struct的指针作为参数并返回指向const char的指针。对于不创建这些结构实例的用户,甚至可以提供一个“用户头”,它不会泄漏结构的实现,而只是定义操纵函数(或返回,如工厂)指针。这使结构保持不完整的类型(因此只能使用指向实例的指针)。这种模式有效地模拟了C ++在幕后使用this
指针所做的事情。
答案 1 :(得分:5)
这是C语言的已知问题,无法避免。毕竟,您没有修改结构,而是通过从结构中获取的非const
限定指针修改单独的对象。 const
语义最初的设计是为了将内存区域标记为物理上不可写的常量,而不是对防御性编程的任何担忧。
答案 2 :(得分:5)
也许如果您决定使用C11,您可以实现一个Generic宏,它引用同一成员的常量或变量版本(您还应该在结构中包含一个union)。像这样:
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
只能用于此结构的指针或对象,而不能同时用于两者。
答案 3 :(得分:3)
我们可以隐藏一些" accessor"功能:
// 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; }
答案 4 :(得分:2)
不,除非您将结构定义更改为:
struct A
{
const char *ptrChar;
};
保持旧结构定义不变的另一个复杂解决方案是定义一个具有相同成员的新结构,其相关指针成员设置为:指向const类型。然后,您调用的函数将更改为采用新结构。定义了一个包装函数,该函数接受旧结构,通过成员复制将成员复制到新结构并将其传递给函数。
答案 5 :(得分:1)
GCC可以警告我修改C99中const结构的字段吗?
您没有修改const结构的字段。
struct A的值包含指向非const char的指针。 ptrA是指向const结构A的指针。因此,您无法在* ptrA处更改struct A值。所以你不能在(* ptrA).Char aka ptrA-> ptrChar上改变指向char的指针。但是你正在改变ptrA-> ptrChar指向的值,即*(ptrA-> Char)的值,也就是ptrA-> Char [0]。这里唯一的结果是struct As,而你没有改变结构A,那么究竟什么是“不希望”?
如果您不想允许更改struct A的Char字段指向的值(通过该结构A),则使用
struct A
{
const char *ptrChar; // or char const *ptrChar;
};
但也许你在
void f(const struct A *ptrA)
{
const char c = 'A';
ptrA->ptrChar = &c;
}
编译器会收到错误。