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

时间:2016-02-10 15:55:11

标签: c++ c gcc clang const

我知道无法完成从char **const char **的隐式转换以及为什么转换为char *const *。请参见下文,了解相关说明的链接。

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

#include <stdio.h>

void
print(const char *const*param)
{
    printf("%s\n", param[0]);
}

int
main(int argc, char **argv)
{
    print(argv);
    return 0;
}

如果我将其编译为C ++代码,它编译得非常好。但是,如果相同的代码仅编译为C代码,我会收到错误(嗯,警告,但是假设-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 **以及{{1} }和char *const *。显式地转换每个调用是不可维护的。我不知道函数原型应该怎么样。

这是引起我好奇心的问题: Implicit conversion from char** to const char**

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

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

1 个答案:

答案 0 :(得分:13)

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 cvn Pn-1cvn-1 … P1cv1 P0cv0T cv'n Pn-1cv'n-1 … P1cv'1 P0cv'0

(其中T是一种类型; P1…Pn是指针/数组类型构造函数,每个cv0…cvn都可能是constvolatile的空子集)

提供:

  1. 对于每个k > 0cvk都是cv'k的子集(因此您无法移除constvolatile),以及

  2. 如果某些cvk的{​​{1}}和cv'k不同,则以下所有k > 0都包含cv'i>k

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

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

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

    对于它的价值,Rationale section of the Posix specification of the exec* interfaces中有一个关于这些原型引起的问题的注释,以及Posix选择的解决方法,即使用char*const*作为原型和文本注释这些是不变的:(强调添加)

      

    包含有关char*[]argv[]为常量的语句,以便明确将来语言绑定的编写者使这些对象完全不变。 由于ISO C标准的限制,无法在标准C中声明该想法。指定envp[]的两个级别 - const和{{ exec函数的参数似乎是自然的选择,因为这些函数不会修改指针数组或函数所指向的字符,但这会禁止现有的正确代码。相反,只有指针数组被标记为常量。

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