为什么C不允许从char **到const char * const *(和C ++)的隐式转换?

我知道从char **const char **隐式转换无法完成,为什么,以及转换为char *const *工作原理。 请参阅底部以获取有关该说明的链接。

除了一件特别的事情,这一切都是有道理的。 所以我有以下代码:

 #include  void print(const char *const*param) { printf("%s\n", param[0]); } int main(int argc, char **argv) { print(argv); return 0; } 

如果我将其编译为C ++代码,它编译得非常好。 但是,如果相同的代码只编译为C代码,我会收到一个错误(好吧,警告,但我们假设 – -Werror-Werror警告视为错误)。

GCC:

 test.c: In function 'main': test.c:12:11: warning: passing argument 1 of 'print' from incompatible pointer type [-Wincompatible-pointer-types] print(argv); ^ test.c:4:1: note: expected 'const char * const*' but argument is of type 'char **' print(const char *const*param) ^ 

铛:

 test.c:12:11: warning: passing 'char **' to parameter of type 'const char *const *' discards qualifiers in nested pointer types [-Wincompatible-pointer-types-discards-qualifiers] print(argv); ^~~~ test.c:4:25: note: passing argument to parameter 'param' here print(const char *const*param) ^ 

这两种行为都是标准独立的,也是独立于编译器的。 我用gccclang尝试了各种标准。

这项调查有两个原因。 首先,我想了解是否存在差异,其次,我有一个函数对指针的任何层都不起作用,我需要它能够使用const char **以及char *const *char ** 。 明确地转换每个调用是不可维护的。 我不知道函数原型应该如何。


这是引起我好奇心的问题: 从char **隐式转换为const char **

以下是char ** => const char**问题的另一个很好的解释: http : //c-faq.com/ansi/constmismatch.html

如果链接与此问题相关,请随意编辑它们。

C和C ++在这方面是不同的。 我没有回答为什么C ++更慷慨,除了C ++行为在我看来是正确的。

C根本不允许间接const转换。 这是一个保守的,易于实现的限制,不幸的结果是你不能将char*[]提供给期望char const* const* 。 限制在§6.3.2.3第2段中,它根本不是递归的:

对于任何限定符q ,指向非q限定类型的指针可以转换为指向该类型的q限定版本的指针; 存储在原始指针和转换指针中的值应相等。

C ++允许根据§4.4[conv.qual]第3段中的某种复杂公式进行转换。允许转换

T cv n P n-1 cv n-1 … P 1 cv 1 P 0 cv 0 T cv' n P n-1 cv' n-1 … P 1 cv' 1 P 0 cv' 0

(其中T是一个类型; P 1 …P n是指针/数组类型构造函数,每个cv 0 …cv n是一些可能是constvolatile空子集)

规定:

  1. 对于每个k > 0cv kcv k的子集(因此你不能删除constvolatile ),

  2. 如果cv kcv' k对于某些k > 0 ,则以下所有cv' i>k包括const

在实际标准中,这种表达方式是相反的; 我把它放在声明的顺序,而在标准中它是按指针/数组构造函数的应用顺序。 但是,我没有改变编号的方向,这就是为什么它们从右到左编号。 我也省略了一些细节 – 例如,两个T s并不是完全相同的 – 但我认为它给出了意图的概念。

对第一个限制的解释是相当明显的。 第二个限制可以防止C FAQ中描述的问题,其中const指针可能存储在非const指针对象中,然后用于改变它指向的const对象。

最重要的是,在C ++中,您的原型const char *const * param将使用char**const char**char*const*类型的参数,但在C中只有最后一个将在没有警告的情况下工作,它是最不实用的。 我所知道的唯一解决方法(除了切换到C ++之外)就是忽略警告。

对于它的价值,有一个关于这个原型引起的问题的exec*接口的Posix规范的基本原理部分中的注释,以及Posix选择的解决方法,即使用char*[]作为原型和文字说明这些是不变的:(强调添加)

关于argv[]envp[]是常量的声明是为了使未来的语言绑定编写者明确表示这些对象是完全不变的。 由于ISO C标准的限制,不可能在标准C中陈述该想法。为exec函数指定argv[]envp[]参数的两个级别的const envp[]似乎是自然的选择鉴于这些函数不修改指针数组或函数指向的字符,但这将禁止现有的正确代码。 相反,只有指针数组被标记为常量。

在该段落之后有一个有用的兼容性图表,由于此站点的格式限制,我没有引用该图表。