const双指针的惯用C语言

jwd*_*jwd 24 c idioms const idiomatic

我知道,在C你不能隐式转换,例如,char**const char**(CF C-有问必答,SO问题1,SO问题2).

另一方面,如果我看到一个声明如此的函数:

void foo(char** ppData);
Run Code Online (Sandbox Code Playgroud)

我必须假设该函数可能会更改传入的数据.因此,如果我正在编写一个不会更改数据的函数,我认为最好声明:

void foo(const char** ppData);
Run Code Online (Sandbox Code Playgroud)

甚至:

void foo(const char * const * ppData);
Run Code Online (Sandbox Code Playgroud)

但这使得该功能的用户处于尴尬的境地.他们可能有:

int main(int argc, char** argv)
{
    foo(argv); // Oh no, compiler error (or warning)
    ...
}
Run Code Online (Sandbox Code Playgroud)

为了干净地调用我的函数,他们需要插入一个演员.

我来自一个主要是C++的背景,由于C++更深入的const规则,这不是一个问题.

C中惯用的解决方案是什么?

  1. 声明foo为a char**,并且只记录它不会改变其输入的事实?这看起来有点严重,尤其是 因为它惩罚那些可能const char**想要传递它的用户(现在他们必须抛弃常量)

  2. 强制用户投射他们的输入,增加常量.

  3. 别的什么?

Jen*_*edt 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
}
Run Code Online (Sandbox Code Playgroud)

CASTIT宏看起来有点复杂,但它所做的是首先检查是否X[0]是分配兼容char const*.它使用复合文字.然后将其隐藏在a中sizeof以确保实际上永远不会创建复合文字,X并且该测试也不会评估复合文字.

然后是一个普通的演员,但这本身就太危险了.

正如您在main本文中的示例中所看到的那样,可以准确地检测出错误的情况.

宏可以实现很多这样的东西.我最近想出一个复杂的例子const-qualified阵列.


nmi*_*els 6

2比1好.1虽然很常见,因为大量的C代码根本不使用const.因此,如果您正在为新系统编写新代码,请使用2.如果您正在为const很少的现有系统编写维护代码,请使用1.