简体   繁体   English

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

[英]Why C doesn't allow implicit conversion from char ** to const char *const * (and C++ does)?

I know implicit conversion from char ** to const char ** cannot be done and why, and that the conversion to char *const * works. 我知道从char **const char **隐式转换无法完成,为什么,以及转换为char *const *工作原理。 See bottom for links to explanation on that. 请参阅底部以获取有关该说明的链接。

It all makes sense apart from one particular thing. 除了一件特别的事情外,这一切都是有道理的 So I have the following code: 所以我有以下代码:

#include <stdio.h>

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

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

If I compile this as a C++ code, it compiles quite fine. 如果我将其编译为C ++代码,它编译得非常好。 However, if the same code is compiled as a C code only, I get an error (well, a warning, but let's suppose -Werror , ie treat warnings as errors). 但是,如果相同的代码只编译为C代码,我会收到一个错误(好吧,一个警告,但让我们假设-Werror-Werror警告视为错误)。

gcc: 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)
 ^

clang: 铛:

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)
                        ^

Both behaviours are standard-independent and also compiler-independent. 这两种行为都是标准无关的,也是独立于编译器的。 I tried various standards with both gcc and clang . 我用gccclang尝试了各种标准。

There are two reasons for this inquiry. 这项调查有两个原因。 Firstly, I want to understand whether there is a difference and, secondly, I have a function that does nothing with any layer of the pointers and I need it to be able to work with const char ** as well as char *const * and char ** . 首先,我想了解是否存在差异,其次,我有一个函数对指针的任何层都不起作用,我需要它能够使用const char **以及char *const *char ** Explicitly casting each call is not maintainable. 显式地转换每个调用是不可维护的。 And I have no idea how should the function prototype look like. 我不知道函数原型应该怎么样。


This is the question that started my curiosity: Implicit conversion from char** to const char** 这是引起我好奇心的问题: 从char **隐式转换为const char **

And here is another nice explanation for the char ** => const char** problem: http://c-faq.com/ansi/constmismatch.html 这是char ** => const char**问题的另一个很好的解释: http//c-faq.com/ansi/constmismatch.html

If the links are confusing related to this question, feel free to edit them out. 如果链接与此问题相关,请随意编辑它们。

C and C++ are different in this respect. C和C ++在这方面是不同的。 I don't have an answer to why C++ is more generous, other than that the C++ behaviour seems to me to be correct. 我没有回答为什么C ++更慷慨,除了C ++行为在我看来是正确的。

C simply doesn't allow indirect const conversion. C根本不允许间接const转换。 That is a conservative, easy-to-implement restriction, with the unfortunate consequence that you cannot provide char*[] to a function expecting char const* const* . 这是一个保守的,易于实现的限制,不幸的结果是你不能将char*[]提供给期望char const* const* The restriction is in §6.3.2.3, paragraph 2, and it is simply not recursive: 限制在§6.3.2.3第2段中,它根本不是递归的:

For any qualifier q , a pointer to a non- q -qualified type may be converted to a pointer to the q -qualified version of the type; 对于任何限定符q ,指向非q限定类型的指针可以转换为指向该类型的q限定版本的指针; the values stored in the original and converted pointers shall compare equal. 存储在原始指针和转换指针中的值应相等。

C++ allows conversions according to a somewhat complex formulation in §4.4 [conv.qual], paragraph 3. It is permitted to convert C ++允许根据§4.4[conv.qual]第3段中的某种复杂公式进行转换。允许转换

T cv n P n-1 cv n-1 … P 1 cv 1 P 0 cv 0T 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 0T cv' n P n-1 cv' n-1 … P 1 cv' 1 P 0 cv' 0

(where T is a type; P 1 …P n are pointer/array type constructors, and each cv 0 …cv n is some possibly empty subset of const and volatile ) (其中T是一个类型; P 1 …P n是指针/数组类型构造函数,每个cv 0 …cv n是一些可能是constvolatile空子集)

provided that: 规定:

  1. For every k > 0 , cv k is a subset of cv' k (so you can't remove a const or a volatile ), and 对于每个k > 0cv k的一个子集cv' k (所以无法删除一个constvolatile ),和

  2. If cv k and cv' k differ for some k > 0 , all the following cv' i>k include const . 如果cv kcv' k为一些不同k > 0 ,满足以下所有cv' i>k包括const

In the actual standard, that expression is reversed; 在实际标准中,这种表达方式是相反的; I put it in the order of declaration, whereas in the standard it is in order of application of the pointer/array constructors. 我把它放在声明的顺序,而在标准中它是按指针/数组构造函数的应用顺序。 I didn't change the direction of the numbering, though, which is why they are numbered right to left. 但是,我没有改变编号的方向,这就是为什么它们从右到左编号。 I also left out some details -- for example, it's not strictly necessary for the two T s to be identical -- but I think it gives an idea of the intention. 我也省略了一些细节 - 例如,两个T s并不是完全相同的 - 但我认为它给出了意图的概念。

The explanation for the first restriction is reasonably obvious. 对第一个限制的解释是相当明显的。 The second restriction prevents the problem described in the C FAQ, where a const pointer might be stored into a non- const pointer object, and then subsequently used to mutate the const object it points to. 第二个限制可以防止C FAQ中描述的问题,其中const指针可能存储在非const指针对象中,然后用于改变它指向的const对象。

The bottom line is that in C++, your prototype const char *const * param will work with arguments of type char** , const char** , or even char*const* , but in C only the last one will work without warning, and it is the least useful. 底线是在C ++中,你的原型const char *const * param将使用char**const char**或者char*const*类型的参数,但在C中只有最后一个可以在没有警告的情况下工作,它是最不实用的。 The only workaround I know of (other than switching to C++) is to ignore the warning. 我所知道的唯一解决方法(除了切换到C ++之外)就是忽略警告。

For what it's worth, there is a note in the Rationale section of the Posix specification of the exec* interfaces about the problem this causes for these prototypes, and the workaround selected by Posix, which is to use char*[] as the prototype and textually note that these are constant: (emphasis added) 对于它的价值,有一个关于这个原型引起的问题exec*接口的Posix规范的基本原理部分中的注释,以及Posix选择的解决方法,即使用char*[]作为原型和文本上注意到这些是不变的:(强调添加)

The statement about argv[] and envp[] being constants is included to make explicit to future writers of language bindings that these objects are completely constant. 关于argv[]envp[]作为常量的声明是为了使未来的语言绑定编写者明确表示这些对象是完全不变的。 Due to a limitation of the ISO C standard, it is not possible to state that idea in standard C. Specifying two levels of const -qualification for the argv[] and envp[] parameters for the exec functions may seem to be the natural choice, given that these functions do not modify either the array of pointers or the characters to which the function points, but this would disallow existing correct code. 由于ISO C标准的限制,不可能在标准C中陈述该想法。为exec函数指定argv[]envp[]参数的两个级别的const envp[]似乎是自然的选择鉴于这些函数不修改指针数组或函数指向的字符,但这将禁止现有的正确代码。 Instead, only the array of pointers is noted as constant. 相反,只有指针数组被标记为常量。

There's a useful compatibility chart following that paragraph, which I didn't quote because of the formatting limitations of this site. 在该段落之后有一个有用的兼容性图表,由于此站点的格式限制,我没有引用该图表。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM