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

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

我可以理解并接受这一点。
此问题的 C++ 解决方案是将 b 更改为 const char * const *,这不允许重新分配指针并阻止您规避 const 正确性(C++ 常见问题解答).

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** 上调用它,参数的正确类型是什么?

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

我已经接受了这样的答案:如果没有演员阵容,我想做的事情就无法完成,无论它是否可能。

有帮助吗?

解决方案

几年前我也遇到过同样的问题,这让我一直烦恼不已。

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

归根结底,这只是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++ 除外)。

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

您已经发现了问题——这段代码不是常量正确的。“Const 正确”意味着,除了 const_cast 和删除 const 的 C 风格转换之外,您永远不能通过这些 const 指针或引用修改 const 对象。

const 正确性的价值——const 在很大程度上是为了检测程序员的错误。如果您将某些内容声明为 const,则表示您认为不应修改它——或者至少,那些只能访问 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;

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

最初声明为 const 的对象特别特殊——编译器可以假设它们永远不会改变。但是,如果可以在不进行强制转换的情况下为“b”分配“a”的值,那么您可能会无意中尝试修改 const 变量。这不仅会破坏您要求编译器进行的检查,禁止您更改该变量值 - 它还会让您破坏编译器优化!

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

编辑-添加:

快乐哥们:你的评论很到位。C 语言或您使用的 C 编译器对待 const char * const * 的方式与 C++ 语言对待它的方式根本不同。也许考虑仅对此源代码行静音编译器警告。

编辑-删除: 删除了错字

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

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

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

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

将 char** 隐式转换为 const char * const * 时,至少在 MSVC 14 (VS2k5) 和 g++ 3.3.3 上,我无法收到错误。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 测试.c

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

编译为 C++ 代码输出:cl /TP /W4 /Wp64 测试.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++ -墙测试.C

无输出

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

const volatile int *const serial_port = SERIAL_PORT;

这是有效的代码。易失性和常量如何共存?简单的。易失性告诉编译器在使用数据时始终读取内存,而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)时会发生什么。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top