我一直觉得const char **x
是用于动态分配的常量字符串数组的正确类型,如下所示:
#include <stdlib.h>
int main()
{
const char **arr = malloc(10 * sizeof(const char *));
const char *str = "Hello!";
arr[0] = str;
free(arr);
}
但是,当使用VS2017编译此代码时,我在free
行收到此警告:
warning C4090: 'function': different 'const' qualifiers
我的代码有问题吗?FWIW,当我使用 GCC 编译时,即使使用-Wall -Wextra -pedantic
也没有收到任何警告.
你的代码没有错。这方面的规则可以在C标准中找到:
6.3.2.3 指针 指向
void 的指针可以转换为指向任何对象类型的指针,也可以从指向任何对象类型的指针转换为指针。指向任何对象类型的指针可以是 转换为指向 void 的指针,然后再次返回;结果应 与原始指针相等。对于任何限定符 q,指向非 q 限定类型的指针可以是 转换为指向类型的 q 限定版本的指针;这 存储在原始指针和转换指针中的值应进行比较 平等。
这意味着任何指向对象类型(指向变量)的指针都可以转换为 void 指针,除非指针是限定的(常量或易失性)。所以做得很好
void* vp;
char* cp;
vp = cp;
但这是不行的
void* vp;
const char* cp;
vp = cp; // not an allowed form of pointer conversion
目前为止,一切都好。但是,当我们混合指针到指针时,const
-ness是一个非常令人困惑的主题。
当我们有一个const char** arr
时,我们有一个指向常量字符的指针(提示:从右到左读取表达式)。或者在 C 标准乱码中:指向类型限定指针的指针。 不过,arr
本身并不是一个合格的指针!它只指向一个。
free()
期望一个指向 void 的指针。我们可以将任何类型的指针传递给它,除非我们传递一个合格的指针。const char**
不是一个合格的指针,所以我们可以很好地传递它。
char* const*
限定指针指向指向类型的指针。
请注意,当我们尝试这样做时,gcc 是如何抱怨的:
char*const* arr = malloc(10 * sizeof(char*const*));
free(arr);
gcc -std=c11 -pedantic-errors -Wall - Wextra
:
错误:"免费"的传递参数 1 会丢弃"const"限定符 指针目标类型
显然,Visual Studio给出了不正确的诊断。或者,您将代码编译为 C++,这不允许与void*
进行隐式转换。
分配有效的原因与以下分配有效的原因相同。
const char** arr = malloc(10 * sizeof(const char *));
void* p = arr;
此规则1说明,如果两个操作数都是指针类型,则左指针指向的类型必须与右指针指向的类型具有相同的限定符。
右操作数是指向没有任何限定符的类型的指针。此类型是指向常量字符 (const char*
) 的类型指针。不要让常量限定符混淆您,该限定符不属于指针类型。
左操作数是一个指针,指向也没有任何限定符的类型。类型为空。因此,分配是有效的。
如果指针指向具有限定符的类型,则赋值将无效:
const char* const* arr = malloc(10 * sizeof(const char *));
void* p = arr; //constraint violation
右操作数是指向具有限定符 const 的类型的指针,此类型是指向 const 字符 (const char* const
) 的类型 const 指针。
左操作数是一个指针,指向没有任何限定符的类型,此类型为 void。赋值违反了约束1。
1(引自:ISO/IEC 9899:201x 6.5.16.1 简单赋值约束 1)
左操作数具有原子、限定或非限定指针类型,并且(考虑 左操作数在左值转换后将具有的类型)一个操作数是一个指针 指向对象类型,另一个是指向限定或非限定版本的指针 void,并且左边指向的类型具有指向的类型的所有限定符 靠右边;