为什么我不能在C中将'char **'转换为'const char * const *'?

下面的代码片段(正确)给出了C中的警告和C ++中的错误(分别使用gcc&g ++,在版本3.4.5和4.2.1中testing; MSVC似乎不在乎):

char **a; const char** b = a; 

我可以理解并接受这一点。
这个问题的C ++解决scheme是将b改为const char * const *,它不允许重新分配指针,并且阻止你规避const正确性( C ++ FAQ )。

 char **a; const char* const* b = a; 

但是,在纯C中,修正后的版本(使用const char * const *)仍然会给出警告,我不明白为什么。 有没有办法解决这个问题,而不使用一个演员?

澄清:
1)为什么这会在C中产生警告? 它应该完全是常量安全的,C ++编译器似乎也是这样认识的。
2)接受这个char **作为参数的正确方法是什么?在说(并且让编译器强制执行)的时候我不会修改它指向的字符? 例如,如果我想写一个函数:

 void f(const char* const* in) { // Only reads the data from in, does not write to it } 

我想在char **上调用它,参数的正确types是什么?

编辑:感谢那些已经回复的人,特别是那些回答问题和/或跟进我的回应的人。

我已经接受了这样一个答案,即不pipe没有演员,我想做的事情都是不可能完成的,不pipe是否可能。

几年前,我也遇到了同样的问题,这让我感到无法自拔。

在C中的规则更简单地陈述(即他们不列出例如转换char**const char*const*exception)。 结果,这是不允许的。 在C ++标准中,他们包含了更多的规则来允许这样的情况。

最后,这只是C标准中的一个问题。 我希望下一个标准(或技术报告)能解决这个问题。

>然而,在纯粹的C,这仍然给出警告,我不明白为什么

你已经发现了这个问题 – 这个代码不是const正确的。 “Const correct”意思是,除了const_cast和C-style强制删除const之外,你永远不能通过这些const指针或引用来修改一个const对象。

常量正确性的值const在很大程度上是为了检测程序员错误。 如果你将某些东西声明为const,那么你就说你不认为它应该被修改,或者至less那些只能访问const版本的应该不能修改它。 考虑:

 void foo(const int*); 

正如所声明的,foo没有修改其参数指向的整数的权限

如果您不确定为什么您发布的代码不是常量不正确,请考虑以下代码,与HappyDude的代码略有不同:

 char *y; char **a = &y; // a points to y const char **b = a; // now b also points to y // const protection has been violated, because: const char x = 42; // x must never be modified *b = &x; // the type of *b is const char *, so set it // with &x which is const char* .. // .. so y is set to &x... oops; *y = 43; // y == &x... so attempting to modify const // variable. oops! undefined behavior! cout << x << endl; 

非常量types只能以特定的方式转换为常量types,以防止在没有显式强制转换的情况下规避数据types的“常量”。

最初声明const的对象特别特别 – 编译器可以假定它们永远不会改变。 但是,如果'b'可以被赋值为'a'而没有强制转换,那么你可能会不小心尝试修改一个constvariables。 这不仅会中断你要求编译器做的检查,而且不允许你改变这个variables的值 – 这也会让你打破编译器的优化!

在某些编译器上,这将打印“42”,在某些“43”和其他文件上,程序将崩溃。

编辑补充:

HappyDude:您的评论是现货。 你使用的C语言或者C编译器,对待const char * const *与C ++语言对待它基本上是不同的。 也许可以考虑只沉默这个源码行的编译器警告。

编辑 – 删除:删除错字

为了被认为是兼容的,源指针应该是在前面的间接层次上的const。 所以,这会给你在GCC的警告:

 char **a; const char* const* b = a; 

但是这不会:

 const char **a; const char* const* b = a; 

或者,你可以投它:

 char **a; const char* const* b = (const char **)a; 

您将需要相同的强制转换来调用函数f(),如您所述。 据我所知,在这种情况下没有办法进行隐式转换(C ++除外)。

这很烦人,但是如果你愿意添加另一个redirect级别,你通常可以做下面的事情来下压指针指针:

 char c = 'c'; char *p = &c; char **a = &p; const char *bi = *a; const char * const * b = &bi; 

它有一个稍微不同的含义,但它通常是可行的,它不使用演员。

当隐式将char **转换为const char * const *,至less在MSVC 14(VS2k5)和g ++ 3.3.3上时,我无法得到一个错误。 GCC 3.3.3发出警告,我不确定它是否正确。

test.c的:

 #include <stdlib.h> #include <stdio.h> void foo(const char * const * bar) { printf("bar %s null\n", bar ? "is not" : "is"); } int main(int argc, char **argv) { char **x = NULL; const char* const*y = x; foo(x); foo(y); return 0; } 

以C代码编译输出:cl / TC / W4 / Wp64 test.c

 test.c(8) : warning C4100: 'argv' : unreferenced formal parameter test.c(8) : warning C4100: 'argc' : unreferenced formal parameter 

用C ++代码编译输出:cl / TP / W4 / Wp64 test.c

 test.c(8) : warning C4100: 'argv' : unreferenced formal parameter test.c(8) : warning C4100: 'argc' : unreferenced formal parameter 

用gcc输出:gcc -Wall test.c

 test2.c: In function `main': test2.c:11: warning: initialization from incompatible pointer type test2.c:12: warning: passing arg 1 of `foo' from incompatible pointer type 

用g ++输出:g ++ -Wall test.C

没有输出

我很确定,const关键字并不意味着数据不能被改变/是不变的,只是数据将被视为只读。 考虑这个:

 const volatile int *const serial_port = SERIAL_PORT; 

这是有效的代码。 volatile和const怎么能并存? 简单。 volatile指示编译器在使用数据时总是读取内存,而当使用serial_port指针尝试写入内存时,const告诉编译器创build一个错误。

const是否帮助编译器的优化器? 一点都不。 因为通过强制转换可以将常量添加到数据中或从中删除,编译器无法确定const数据是否真的是常量(因为可以在不同的转换单元中执行强制转换)。 在C ++中,你也有可变关键字使事情进一步复杂化。

 char *const p = (char *) 0xb000; //error: p = (char *) 0xc000; char **q = (char **)&p; *q = (char *)0xc000; // p is now 0xc000 

当尝试写入真正只读的内存(例如ROM)时,会发生什么情况可能在标准中没有定义。