2011-03-04 57 views
23

我知道,在C你不能隐式转换,例如,char**const char**(C.F. C-FaqSO question 1SO 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++的更深入的常量规则,这不是一个问题。

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

  1. 申报富为取一个char**,只是记录事实,即它不会改变它的投入?这似乎有点严重,尤其是。因为惩罚谁可能有一个const char**他们想通过它用户(现在他们已经投常量性)

  2. 强制用户投了投入,增加常量性。

  3. 还有其他的东西吗?

+1

我猜2)没问题。 – Andrey 2011-03-04 17:06:09

+2

我觉得2)是合适的。 – 2011-03-04 17:10:43

+0

1)'const'是一个无用的C语言编译器注释。 – 2011-03-04 17:13:15

回答

6

2比1好。1是很常见的,因为大量的C代码根本不使用const。因此,如果您要为新系统编写新代码,请使用2.如果要为现有系统编写维护代码,其中const为稀缺性,请使用1.

2

请使用选项2.选项1有缺点你提到并且不太安全。

如果我看到一个功能,需要一个char **争吵,我已经有了一个char *const *或类似的,我会做一个拷贝,传递,以防万一。

+0

良好的回答 - 我给你一个快艇和翻转硬币的答案(:看起来像没有银弹 – jwd 2011-03-04 17:29:18

+0

nmichaels是第一个,所以这是公平的,他得到接受 – 2011-03-04 17:36:20

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

+0

这是一个如何C变得模糊的例子。一个后来的维护者会很难理解这里发生了什么,除非这个名字是全部大写的,否则我会认为'toto()'是一个函数调用,除非它在任何地方都有注释。后来的维护人员收到警告,编译器不会告诉你关于宏的原因需要很长时间才能找到。 – atlpeg 2011-03-04 20:41:57

+0

@atlpeg:如果所有干扰你的人都应该使用全部大写字母,那么这是不行的如果你愿意的话,可以把它当作'TOTO',其余的我可以看到你的观点,但我完全不同意,缺少将'char **'强制转换为'char const * const *'的可能性显然是语言的缺陷。C++得到一个它更好的方式。强制用户使用解决方案1或2来引入大量的演员阵容是一个噩梦来维护并且非常危险,因为C只会发起一种类型的演员阵容。因此,如果用户将类型从'char'更改为'int',那么演员只会接受。 – 2011-03-04 20:53:14

+0

绝对有趣。不确定我会使用它,但有趣的(: – jwd 2011-03-07 17:59:51