关于指针:为什么不能在C语言中将’char **’转换为’const char * const *’?

关于指针:为什么不能在C语言中将’char **’转换为’const char * const *’?

Why can't I convert 'char**' to a 'const char* const*' in C?

以下代码段(正确)在C中给出了警告,并在C ++中给出了错误(分别使用经版本3.4.5和4.2.1测试的gcc和g ++; MSVC似乎无关紧要):

1
2
char **a;
const char** b = a;

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

1
2
char **a;
const char* const* b = a;

但是,在纯C语言中,更正后的版本(使用const char * const *)仍然发出警告,我不明白为什么。
有没有一种方法可以解决这个问题而无需使用演员表?

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

1
2
3
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中向您发出警告:

1
2
char **a;
const char* const* b = a;

但这不会:

1
2
const char **a;
const char* const* b = a;

另外,您可以强制转换:

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

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


However, in pure C, this still gives a warning, and I don't understand why

您已经确定了问题-此代码不是const正确的。"正确的常量"意味着,除了const_cast和删除const的C样式强制转换之外,您永远不能通过这些const指针或引用来修改const对象。

const-正确性-const的值大部分用于检测程序员错误。如果您将某些内容声明为const,则表示您不应该对其进行修改,或者至少,那些只能使用const版本的用户不能对其进行修改。考虑:

1
void foo(const int*);

如声明的那样,foo没有权限修改其参数指向的整数。

如果不确定为什么发布的代码不正确,请考虑以下代码,该代码与HappyDude的代码仅稍有不同:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 ++语言对待的方式根本不同。也许可以考虑仅对此源代码行禁用编译器警告。


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

1
2
3
4
5
6
char c = 'c';
char *p = &c;
char **a = &p;

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

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


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

1
const volatile int *const serial_port = SERIAL_PORT;

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

const是否有助于编译器的优化器?一点都不。由于可以通过强制转换将常量添加到数据中或从数据中删除常量,因此编译器无法确定const数据是否确实是常量(因为强制转换可以在其他转换单元中完成)。在C ++中,您还可以使用mutable关键字使事情进一步复杂化。

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

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


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

test.c的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdlib.h>
#include <stdio.h>
void foo(const char * const * bar)
{
    printf("bar %s null
"
, 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

1
2
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

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

使用gcc输出:gcc -Wall test.c

1
2
3
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

没有输出


推荐阅读

    linux命令行设置语言?

    linux命令行设置语言?,系统,管理,环境,国家,工具,电脑,软件,文化,底部,语言,l

    linux显示错误命令?

    linux显示错误命令?,信息,系统,电脑,状态,时间,环境,命令,搜狐,密码,异常,虚

    linux退出错误命令的?

    linux退出错误命令的?,系统,电脑,环境,命令,位置,管理,工具,设备,终端,进程,L

    linux使用命令改语言?

    linux使用命令改语言?,系统,工作,管理,电脑,设备,字符集,中文,命令,语言,虚

    c语言写linux命令?

    c语言写linux命令?,系统,工具,代码,智能,工作,环境,情况,位置,命令,文件,如何

    linux没有该命令错误?

    linux没有该命令错误?,系统,第一,环境,命令,分析,软件,异常,文件,目录,空格,

    linux汇编语言命令?

    linux汇编语言命令?,系统,地址,代码,数据,网络,平台,平均,位置,灵活,工作,汇

    linux汇编语言命令?

    linux汇编语言命令?,系统,地址,代码,数据,网络,平台,平均,位置,灵活,工作,汇

    linux命令错误代码?

    linux命令错误代码?,系统,密码,电脑,网络,手机,网址,软件,代码,设备,老板,Lin

    linux命令是什么语言?

    linux命令是什么语言?,系统,环境,代码,传播,管理,语言,操作系统,源码,自由,

    linux改语言命令行?

    linux改语言命令行?,系统,环境,工具,密码,概念,地方,软件,通信,管理,国际,lin

    linux命令行c语言?

    linux命令行c语言?,代码,系统,工具,环境,工作,保险,发行,命令,文件,终端,linu

    c语言在linux命令?

    c语言在linux命令?,系统,工作,管理,命令,保险,基础,环境,信息,文件,语言,linu

    linux编写c语言命令?

    linux编写c语言命令?,系统,基础,环境,代码,盘面,保险,百度,情况,数据,工具,在

    linux命令忽略错误?

    linux命令忽略错误?,系统,地址,工作,信息,设备,命令,设计,灵活,观察,标准,lin

    linux改变语言命令?

    linux改变语言命令?,系统,管理,网上,官方网站,情况,服务,中文,语言,命令,终

    c语言编译linux命令?

    c语言编译linux命令?,代码,工具,环境,系统,基础,保险,百度,语言,源程序,文件

    linux常用命令语言?

    linux常用命令语言?,工作,地址,系统,信息,命令,目录,标准,管理,工具,服务,lin

    r语言命令行写linux?

    r语言命令行写linux?,环境,数据,系统,工具,简介,官网,语言,报告,软件,发展,如

    linux语言查找命令行?

    linux语言查找命令行?,系统,工作,位置,标准,地址,信息,命令,管理,时间,文件,