const char* const* p 参数在 g++ 中编译但不是 gcc

Posted

技术标签:

【中文标题】const char* const* p 参数在 g++ 中编译但不是 gcc【英文标题】:const char* const* p param compiled in g++ but not gcc 【发布时间】:2021-12-27 22:48:32 【问题描述】:

我有以下程序在 g++ 中编译得很好,但在 gcc 中编译得不好。

g() 接受一个指向 char* 数组的指针,并且不应修改 p 指向的任何内容。

无论如何要让它在 C 中正常工作?

% cat t.c
void g(const char* const* p) 
int main()

  char** p = 0;
  g(p);  


% g++ t.c
% gcc t.c
t.c: In function ‘main’:
t.c:5:5: warning: passing argument 1 of ‘g’ from incompatible pointer type [-Wincompatible-pointer-types]
   g(p);
     ^
t.c:1:6: note: expected ‘const char * const*’ but argument is of type ‘char **’
 void g(const char* const* p) 
      ^
% 

【问题讨论】:

C++C 是不同的语言。在 C 中,*const 使其在嵌套指针类型中不兼容。 【参考方案1】:

错误消息会告诉您需要了解的所有信息。使 p 与 g 的参数具有相同的签名。

    const char* const* p = 0;

但是,如果您需要 p 指向的任何内容都是只读的,那么这种类型规范就足以分配 p 和 g 参数(尽管可以更改数组指向的任何内容):

    char* const* p = 0;

【讨论】:

换句话说,我需要在 C 中转换,而不是在 C++ 中。 @cktan 是的,您可以在调用 g 时强制转换 p 并将 p 声明保持原样。【参考方案2】:

C 中的情况

在 C 中,限定符只允许隐式添加到最顶层的指针级别:

6.3.2.3 指针

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

所以你只能隐式地将 const 添加到最顶层的指针,例如:

char** p = 0;
char* const* other = p; // ok

const char** other = p; // not ok: adding const not at top-level

这样做的原因是,如果您跳过几个环节,它将允许您获得指向 const 值的非 const 指针:

const char c = 'C';
char* ptr;
const char **ptrPtr = &ptr; // this is not legal
*ptrPtr = &c;

乍一看,这段代码看起来很合理——你有指向ptrptrPtr指向c。 问题是您现在可以通过ptr 修改c(这是 const-qualified),例如:

*ptr = 'X'; // will change c

Here's an FAQ entry about this 如果您想了解更多相关信息。


如果你想使用显式强制转换,你仍然可以强制它:

void g(const char* const* p) 

int main()

  char** p = 0;
  g((const char* const*)p); 

C++ 中的情况

C++ 在可以隐式添加多少常量方面更为宽松。

7.3.6 Qualification conversions

(1)类型 T 的限定分解是一个序列 cvi 和 Pi 使得 T 是 “cv0 P0 cv1 P1 … cvn-1 P n−1 cvn U” 对于 n ≥ 0, 其中每个 cvi 是一组 cv 限定符([basic.type.qualifier]),每个 Pi 是“指向”([dcl.ptr] )、“指向类型 Ci 的成员的指针”([dcl.mptr])、“Ni 的数组”或“未知边界的数组” ([dcl.array])。如果 Pi 指定一个数组,则元素类型上的 cv 限定符 cvi+1 也被视为 cv 限定符 cvi数组。

(2) 两个类型 T1 和 T2 是相似的,如果它们具有具有相同 n 的限定分解,使得对应的 Pi 分量相同或一个是“array of Ni”,另一个是“array of unknown bound of”,U表示的类型相同。

(3)两种类型T1和T2的限定组合类型是与T1相似的类型T3,其限定分解为:

(3.1) 对于每一个 i > 0,cv3i 是 cv1i 的并集 和 cv2i, (3.2) 如果 P1i 或 P2i 是“数组of unknown bound of”,P3i 为“未知边界数组”,否则为 P1i子>,和 (3.3) 如果结果 cv3i 不同于 cv1i或 cv2i,或生成的 P3i 与 P1不同>i 或 P2i,然后将 const 添加到每个 cv3k 为 0

其中 cvji 和 Pji 是 Tj.

这基本上归结为:

您始终可以将 const 添加到顶层 如果将 const 添加到较低级别,则从该级别开始的所有级别也必须是 const

这个“const-propagation”对于避免出现在 C 中的极端情况是必要的:获取指向 const 对象的非常量指针。

几个例子:godbolt

    int i = 1;
    int* ip = &i;
    int** ipp = &ip;
    int*** ippp = &ipp;

    const int* t1 = ip; // ok
    const int* const* t2 = ipp; // ok
    const int* const* const* t3 = ippp; // ok

    // const int** t4 = ipp; // not ok
    // const int*** t5 = ippp; // not ok
    // const int* const** t6 = ippp; // not ok

    int* const* t7 = ipp; // ok
    int* const* const* t8 = ippp; // ok

    // int* const** t9 = ippp; // not ok


结论

这就是为什么你的代码用 g++ 编译但不能用 gcc 编译的原因:

C++ 允许您为多个指针级别隐式添加 const 限定符(只要您遵循上述 da 规则),而 C 仅允许您将 const 隐式添加到***指针。

【讨论】:

以上是关于const char* const* p 参数在 g++ 中编译但不是 gcc的主要内容,如果未能解决你的问题,请参考以下文章

C++标准库 如何连接两个const char *类型字符串,并返回const char * 类型结果?

深入理解const char*p,char const*p,char *const p,const char **p,char const**p,char *const*p,char**cons(示例

c++关于const char*的问题

c++ uint32 如何转 const char

const char *p; char const *p; char * const p的区别

在Qt中如何将QString转换为const char*