例如,我可以执行以下操作,以便只有foo.c
可以修改变量foo
吗?:
foo.h
:
extern const int foo;
void foo_init(void);
void foo_reinit(void);
foo.private.h
:
int foo;
void foo_init(void);
void foo_reinit(void);
foo.c
:
#include "foo.private.h"
void foo_init() { foo = 1; /* ... */ }
void foo_reinit() { foo++; /* ... */ }
bar.c
:
#include <foo.h>
int main()
{
foo_init(); printf("foo: %d\n", foo);
foo_reinit(); printf("foo: %d\n", foo);
return 0;
}
以下情况会产生错误/警告:
baz.c
:
#include <foo.h>
int main()
{
foo_init(); printf("foo: %d\n", foo);
foo = 0; /* ERROR/WARNING for modifying const variable */
return 0;
}
这是否可以保证正确链接?
答案 0 :(得分:5)
没有。如果声明不匹配,则程序的行为未定义。特别是,由于使用公共头的翻译单元声明对象是const
限定的,因此编译器在翻译时可以假设指向的数据永远不会改变,因此它可以缓存调用外部函数的值,包括更改它的函数。
如果您只是想要防止意外编写试图修改数据的代码,请执行以下操作:
extern int foo;
#define foo (*(const int *)&foo)
然后,您可以在实际允许更改它的模块中#undef foo
。
答案 1 :(得分:5)
我可以在公共标题中将变量声明为const而不是在私有标题中吗?
不,你不能因为它调用未定义的行为。
(C11,6.2.7p2)“引用同一对象或函数的所有声明都应具有兼容类型;否则,行为未定义。”
const int
和int
是两种类型不兼容的类型。
(C11,6.7.3p10)“对于兼容的两种合格类型,两者都应具有相同的合格版本 兼容类型;说明符或限定符列表中类型限定符的顺序 不会影响指定的类型“
答案 2 :(得分:2)
当你踏上冰块时,你喜欢它的厚度有多薄?
是的,你可以做到。你有点侥幸逃脱。但标准并不能保证您的代码能够按预期工作。应该在任何地方始终声明相同的对象。
但主要的问题是你丢失了你应该要求的交叉检查,因为你的私人&#39;代码应该使用公共头,以确保外部使用的声明与实现匹配。也就是说,foo.c
应该#include "foo.h"
以确保告知使用者的消息与实现提供的内容相匹配(在foo.c
中)。如果你不这样做,迟早会出问题。
另外,请记住编译器讨厌被骗。当你骗他们时,他们会想办法让自己回来。你的代码对编译器说谎。
答案 3 :(得分:1)
不,你不能,但正如我理解你的代码,你可以将foo
静态,作为函数foo_init
和foo_reinit
,并公开一个简单的{{1其他模块。