为什么不能在C中将'char **'转换为'const char * const *'?
以下代码段(正确)在C语言中给出了警告,并在C ++中给出了错误(分别使用gcc和g ++,并通过3.4.5和4.2.1版进行了测试; MSVC似乎并不在乎):
char **a; const char** b = a;
我可以理解并接受。
C ++解决此问题的方法是将b更改为const char * const *,这不允许重新分配指针并阻止我们规避const正确性(C ++ FAQ)。
char **a; const char* const* b = a;
但是,在纯C语言中,更正后的版本(使用const char * const *)仍然会发出警告,我不明白为什么。
有没有一种方法可以解决这个问题而无需使用演员表?
澄清:
1)为什么这会在C语言中生成警告?它应该完全是const安全的,C ++编译器似乎也是如此。
2)在说(并让编译器强制执行)我将不修改其指向的字符时接受此char **作为参数的正确方法是什么?
例如,如果我想编写一个函数:
void f(const char* const* in) { // Only reads the data from in, does not write to it }
我想在char **上调用它,参数的正确类型是什么?
编辑:
感谢答复,特别是那些回答问题和/或者跟进我的答复的人。
我已经接受了这样一个答案:我想要做的事情如果没有演员表就无法完成,无论它是否应该可行。
解决方案
回答
几年前,我遇到了同样的问题,这让我感到无休止。
C语言中的规则用更简单的方式表示(即,它们没有列出将char *转换为const char * const *`之类的异常)。因此,这是不允许的。在C ++标准中,它们包括更多的规则以允许类似这样的情况。
最后,这只是C标准中的问题。我希望下一个标准(或者技术报告)能解决这个问题。
回答
但是,在纯C语言中,这仍然会发出警告,我不明白为什么
我们已经确定了问题-此代码不是const正确的。 "正确的常量"表示,除了const_cast和C样式强制类型转换删除const之外,我们永远无法通过这些const指针或者引用来修改const对象。
const-correctness -const的值大部分用于检测程序员错误。如果我们将某些内容声明为const,则表示我们不应该对其进行修改,或者至少,只有那些可以访问const版本的人才不能对其进行修改。考虑:
void foo(const int*);
如所声明的,foo没有权限修改其参数所指向的整数。
如果不确定为什么发布的代码不是const正确的,请考虑以下代码,该代码与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;
非const类型只能以特定方式转换为const类型,以防止在没有显式强制转换的情况下对数据类型进行任何" const"规避。
最初声明为const的对象特别特殊-编译器可以假定它们永不更改。但是,如果可以在不强制转换的情况下为'b'赋值'a',那么我们可能会无意间尝试修改const变量。这不仅会破坏我们要求编译器进行的检查,还不允许我们更改该变量的值-它还将使我们破坏编译器的优化!
在某些编译器上,这将打印为" 42",在某些编译器上为" 43",而其他程序则将崩溃。
编辑添加:
HappyDude:评论很对。无论是C语言还是正在使用的C编译器,对const char * const *的处理都根本不同于C ++语言。也许只考虑使此源代码行的编译器警告静音。
编辑删除:删除错字
回答
至少在MSVC 14(VS2k5)和g ++ 3.3.3上将char **隐式转换为const char * const *时,我无法出错。 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告诉编译器在使用数据时始终读取内存,而const告诉编译器在尝试使用serial_port指针写入内存时创建错误。
const是否有助于编译器的优化器?一点都不。由于可以通过强制转换将常量添加到数据中或者从数据中删除常量,因此编译器无法确定const数据是否确实是常量(因为强制转换可以在其他转换单元中完成)。在C ++中,我们还可以使用mutable关键字使事情进一步复杂化。
char *const p = (char *) 0xb000; //error: p = (char *) 0xc000; char **q = (char **)&p; *q = (char *)0xc000; // p is now 0xc000
尝试写入真正只读的内存(例如ROM)时会发生什么,该标准可能根本没有定义。
回答
为了被认为是兼容的,源指针应该在前向间接级别中为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 ++中除外)。
回答
这很烦人,但是如果我们愿意添加另一级别的重定向,则通常可以执行以下操作以向下推入指针到指针:
char c = 'c'; char *p = &c; char **a = &p; const char *bi = *a; const char * const * b = &bi;
它的含义略有不同,但是通常是可行的,并且不使用强制转换。