我知道在C中你无法隐式转换,例如char**
到const char**
(cf C-Faq,SO question 1,SO Question 2)
另一方面,如果我看到一个声明如下的函数:
void foo(char** ppData);
我必须假设该函数可能会更改传入的数据。 因此,如果我正在编写一个不更改数据的函数,我认为最好声明:
void foo(const char** ppData);
甚至:
void foo(const char * const * ppData);
但是这使得该功能的用户处于尴尬的境地。 他们可能有:
int main(int argc, char** argv)
{
foo(argv); // Oh no, compiler error (or warning)
...
}
为了干净地调用我的函数,他们需要插入一个演员。
我来自一个主要是C ++的背景,由于C ++更深入的const规则,这不是一个问题。
C中的惯用解决方案是什么?
声明foo为char**
,并记录它不会改变输入的事实?这看起来有点严重,尤其是因为它惩罚那些可能有const char**
他们想要传递它的用户(现在他们必须施放远离 const-ness)
强制用户投射他们的输入,添加常量。
还有别的吗?
答案 0 :(得分:7)
虽然你已经接受了答案,但我想去3)即宏。您可以通过函数用户只需编写foo(x);
来编写这些函数,其中x可以是const
- 是否合格。这个想法将有一个宏CASTIT
执行转换并检查参数是否是有效类型,另一个是用户界面:
void totoFunc(char const*const* x);
#define CASTIT(T, X) ( \
(void)sizeof((T const*){ (X)[0] }), \
(T const*const*)(X) \
)
#define toto(X) totoFunc(CASTIT(char, X))
int main(void) {
char * * a0 = 0;
char const* * b0 = 0;
char *const* c0 = 0;
char const*const* d0 = 0;
int * * a1 = 0;
int const* * b1 = 0;
int *const* c1 = 0;
int const*const* d1 = 0;
toto(a0);
toto(b0);
toto(c0);
toto(d0);
toto(a1); // warning: initialization from incompatible pointer type
toto(b1); // warning: initialization from incompatible pointer type
toto(c1); // warning: initialization from incompatible pointer type
toto(d1); // warning: initialization from incompatible pointer type
}
CASTIT
宏看起来有点复杂,但它首先要检查X[0]
是否与char const*
兼容。它使用复合文字。然后将其隐藏在sizeof
内,以确保实际上永远不会创建复合文字,并且X
不会被该测试评估。
然后是一个简单的演员,但这本身就太危险了。
正如您在main
中的示例所看到的那样,这可以准确地检测到错误的情况。
宏可能有很多东西。我最近制作了一个复杂的例子with const
-qualified arrays。
答案 1 :(得分:6)
2优于1. 1非常常见,因为大量的C代码根本不使用const。因此,如果您要为新系统编写新代码,请使用2.如果您正在为const很少的现有系统编写维护代码,请使用1.
答案 2 :(得分:2)
使用选项2.选项1的缺点是您提到并且类型安全性较低。
如果我看到一个带有char **
参数且我有一个char *const *
或类似的函数,我会复制并传递它,以防万一。