60

以下代码片段(正确)在 C 中给出警告,在 C++ 中给出错误(分别使用 gcc 和 g++,使用版本 3.4.5 和 4.2.1 进行测试;MSVC 似乎并不关心):

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

我可以理解并接受这一点。
此问题的 C++ 解决方案是将 b 更改为 const char * const *,这不允许重新分配指针并防止您规避 const-correctness ( 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** 上调用它,参数的正确类型是什么?

4

6 回答 6

59

几年前我也遇到过同样的问题,这让我很恼火。

C 中的规则更简单地说明(即它们没有列出转换char**为等例外情况const char*const*)。结果,就是不允许。在 C++ 标准中,它们包含了更多规则来允许这样的情况。

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

于 2008-09-16T23:04:21.097 回答
10

但是,在纯 C 中,这仍然会发出警告,我不明白为什么

您已经发现了问题——此代码不是 const 正确的。“const 正确”意味着,除了const_castC 风格的强制类型转换 remove之外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++ 语言处理方式根本不同。也许考虑只为这个源代码行消除编译器警告。

于 2008-09-16T23:08:36.500 回答
10

为了被认为是兼容的,源指针在直接前间接级别中应该是 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++ 除外)。

于 2008-09-17T00:06:21.853 回答
1

这很烦人,但是如果您愿意添加另一个级别的重定向,您通常可以执行以下操作来下推指针到指针:

char c = 'c';
char *p = &c;
char **a = &p;

const char *bi = *a;
const char * const * b = &bi;

它的含义略有不同,但通常是可行的,并且不使用强制转换。

于 2008-09-19T00:41:00.083 回答
0

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

测试.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

没有输出

于 2008-09-16T23:42:06.250 回答
0

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

const volatile int *const serial_port = SERIAL_PORT;

这是有效的代码。volatile 和 const 如何共存?简单的。volatile 告诉编译器在使用数据时始终读取内存,而 const 告诉编译器在尝试使用 serial_port 指针写入内存时创建错误。

const 是否有助于编译器的优化器?一点都不。因为 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)时会发生什么,标准中可能根本没有定义。

于 2008-09-16T23:46:37.657 回答