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

uhry853o  于 2023-02-03  发布在  其他
关注(0)|答案(6)|浏览(220)

下面的代码片段(正确地)在C中给出了一个警告,在C++中给出了一个错误(分别使用gcc & g ++,用版本3. 4. 5和4. 2. 1测试;MSVC似乎并不在意):

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

我可以理解和接受。
这个问题的C解决方案是将b更改为const char * const *,这样就不允许重新分配指针,并防止您绕过常量正确性(C FAQ)。

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

然而,在纯C中,修正后的版本(使用const char * const *)仍然会给出警告,我不明白为什么。有没有办法不使用强制类型转换就能解决这个问题?
澄清:
1.为什么这会在C中生成警告?它应该是完全常量安全的,并且C++编译器似乎可以识别它。
1.接受char**作为参数,同时声明(并让编译器强制执行)我不会修改它所指向的字符,正确的方法是什么?例如,如果我想写一个函数:

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

我想在char**上调用它,那么参数的正确类型是什么呢?

uxhixvfz

uxhixvfz1#

几年前我也遇到过同样的问题,它让我烦恼不已。
C中的规则表述得更简单(例如,它们没有列出将char**转换为const char*const*这样的例外)。因此,这是不允许的。随着C++标准的出现,它们包含了更多的规则来允许这样的情况。
说到底,这只是C标准中的一个问题,我希望下一个标准(或技术报告)会解决这个问题。

jgwigjjp

jgwigjjp2#

要考虑兼容性,源代码指针应该是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++除外)。

c8ib6hqw

c8ib6hqw3#

但是,在纯C中,这仍然会给出警告,我不明白为什么
您已经发现了问题所在--这段代码不是常量正确的。“常量正确”意味着,除了const_cast和删除const的C样式强制转换之外,您永远不能通过这些常量指针或引用修改const对象。
const-correction--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的对象特别特殊--编译器可以假定它们永远不会更改。然而,如果b可以被赋值为a而无需强制转换,那么您可能会无意中试图修改const变量。这不仅会破坏您要求编译器进行的检查,不允许您更改变量值--这也将允许您破坏编译器优化!
在某些编译器上,这将打印42,在某些43上,程序将崩溃。

编辑-添加:

HappyDude:你的评论说得很对。无论是C语言,还是你使用的C编译器,对待const char * const *的方式都与 C++ 有根本的不同。也许可以考虑只对这一行代码关闭编译器警告。

yvgpqqbh

yvgpqqbh4#

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

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

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

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

jhkqcmku

jhkqcmku5#

至少在MSVC 14(VS 2k 5)和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 /W 4/Wp 64试验c

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

编译为C++代码的输出:cl /TP /W 4/Wp 64试验c

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

使用gcc的输出:gcc -壁厚试验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

  • 无输出 *
6yoyoihd

6yoyoihd6#

我很确定const关键字并不意味着数据不能被改变或者是常量,只是意味着数据将被视为只读。

const volatile int *const serial_port = SERIAL_PORT;

volatile和const如何共存?很简单,volatile告诉编译器在使用数据时总是读取内存,const告诉编译器在试图使用serial_port指针写入内存时创建错误。
const对编译器的优化有帮助吗?没有。一点也没有。因为constness可以通过强制转换添加到数据中或从数据中删除,所以编译器无法判断const数据是否真的是常量(因为强制转换可以在不同的翻译单元中完成)。在C++中,您还可以使用mutable关键字来使问题进一步复杂化。

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

当试图写入实际上是只读的内存(例如ROM)时会发生什么,可能根本没有在标准中定义。

相关问题