【问题标题】:I get incompatible pointer types warning when I use const for pointers当我使用 const 作为指针时,我收到不兼容的指针类型警告
【发布时间】:2021-08-24 12:06:39
【问题描述】:

在制作以下代码时:

#include <general.dep>

typedef struct {
} obj;

int f1(obj **o) {
    return 0;
}

int f2(obj *const *o) {
    return 0;
}

int f3(const obj **o) {
    return 0;
}

int f4(const obj *const *o) {
    return 0;
}

int main() {
    obj **o;
    f1(o);
    f2(o);
    f3(o);
    f4(o);
    return 0;
}

我收到以下警告消息:

gcc t1.c -I/home/hamidi/code/lib/general/inc -fmax-errors=1 -lgeneral -llzma -pthread -o t1
t1.c: In function ‘main’:
t1.c:26:6: warning: passing argument 1 of ‘f3’ from incompatible pointer type [-Wincompatible-pointer-types]
26 |   f3(o);
   |      ^
   |      |
   |      obj ** {aka struct <anonymous> **}
t1.c:14:20: note: expected ‘const obj **’ {aka ‘const struct <anonymous> **’} but argument is of type ‘obj **’ {aka ‘struct <anonymous> **’}
14 | int f3(const obj **o) {
   |        ~~~~~~~~~~~~^
t1.c:27:6: warning: passing argument 1 of ‘f4’ from incompatible pointer type [-Wincompatible-pointer-types]
27 |   f4(o);
   |      ^
   |      |
   |      obj ** {aka struct <anonymous> **}
t1.c:18:26: note: expected ‘const obj * const*’ {aka ‘const struct <anonymous> * const*’} but argument is of type ‘obj **’ {aka ‘struct <anonymous> **’}
18 | int f4(const obj *const *o) {
   |        ~~~~~~~~~~~~~~~~~~^

如何解决这些警告问题?

更新

我标记了以下发出警告的函数:

void f0(obj **ppo) {}
void f1(const obj **ppo) {}  //
void f2(obj const **ppo) {}  //
void f3(obj *const *ppo) {}
void f4(obj **const ppo) {}
void f5(const obj const **ppo) {}  //
void f6(const obj *const *ppo) {}  //
void f7(const obj **const ppo) {}  //
void f8(obj const *const *ppo) {}  //
void f9(obj const **const ppo) {}  //
void fa(obj *const *const ppo) {}
void fb(const obj const *const *ppo) {}        //
void fc(const obj const **const ppo) {}        //
void fd(const obj *const *const ppo) {}        //
void fe(obj const *const *const ppo) {}        //
void ff(const obj const *const *const ppo) {}  //

我不确定哪些是等价的。似乎 f2 和 f3 不同,因为 f2 发出警告而 f3 没有。那么,我们可以说obj const *obj *const 不同吗?

更新 2

obj 的 const 可以放在它之前或之后,但对于指针,它应该放在它们之后。所以,让我们把两者都放在 const 之后。现在我的问题是在以下代码中:

void f(int const* const* ppi) {
    //**ppi = **ppi;
    //*ppi = *ppi;
    ppi = ppi;
}

void g(int const* pi) {
    //*pi = *pi;
    pi = pi;
}

int main() {
    int i, *pi = &i, **ppi = &pi;
    f(ppi);
    g(pi);
    return 0;
}

我调用了两个函数,f & g。两个函数原型都表明参数的内容没有改变。 g不改变指针的内容,f不改变指针的内容和指针内容指向的内容。

现在我的问题是,为什么编译器仅在调用 f 而不是 g 时才发出此警告?!如果 f 要说我不打算改变指针的内容和它所指向的内容,那么它的原型应该是什么?

warning: passing argument 1 of ‘f’ from incompatible pointer type [-Wincompatible-pointer-types]
   16 |   f(ppi);
      |     ^~~
      |     |
      |     int **
t2.c:3:26: note: expected ‘const int * const*’ but argument is of type ‘int **’
    3 | void f(int const* const* ppi) {
      |        ~~~~~~~~~~~~~~~~~~^~~

【问题讨论】:

  • 是的。 const obj constconst objobj const 都是一样的。当* 变成** 时,编译器发出警告,指出指针指向的实体在调用者和被调用函数中不同,而如果这是一个问题,那么当* 时也应该是一个问题用来。我的问题正是这个。 *** 这两者有什么区别。为什么我使用*时没有报告问题?
  • are all the same 是的。 When * turns into **我不明白那部分。 * and ** 这些只是明星。 what the issue is not reported when I use *? 因为语言是以这种方式创建的并且允许这样做。 port70.net/~nsz/c/c11/n1570.html#6.5.16.1p1port70.net/~nsz/c/c11/n1570.html#6.5.16.1p6port70.net/~nsz/c/c11/n1570.html#note112
  • 您使用哪种语言?注意 C 和 C++ 是不同的语言。在 C++ 中,int ** 类型的参数与 int const* const* 类型的参数兼容,而在 C 中则不兼容。标签 gcc 属于 GNU Compiler Collection,其中包含 C 编译器、C++ 编译器等。
  • 纯C。我用g++编译C++。
  • 去掉无关标签,添加相关标签

标签: c


【解决方案1】:

指针指向一些数据。指针本身可以是 const 也可以不是。数据可以是 const 也可以不是。

const TT const 之间没有区别(在顶层)。

指向数据的指针可以隐式转换为指向常量数据的指针。

int * a;
const int * b = a; // ok

但该属性不具有传递性——例如,当数据本身是指针时,它不适用。指向数据的指针可以转换为指向数据的 const 指针。

int * * c; // pointer to a pointer to data
int * const * e = c; // ok, e is a pointer to a const pointer to data

但是指向数据指针的指针不能隐式转换为指向 const 数据指针的指针。

int * * c; // pointer to a pointer to data
const int * * d = c; // err

如何解决这些警告问题?

使用转换将指针转换为正确的类型。

我不确定哪些是等价的。

参数的限定符在函数声明中无关紧要。参见https://en.cppreference.com/w/cpp/language/function 部分Top-level cv-qualifiers are dropped from the parameter type ...。限定符在函数 definition 中可见,例如不能修改 const 参数。

让我们对函数定义进行分组,希望我做对了。

void f0(obj **ppo) {}

void f4(obj **const ppo) {}

// same
void f1(const obj **ppo) {}
void f2(obj const **ppo) {}
void f5(const obj const **ppo) {}
// same declarations with above
void f7(const obj **const ppo) {}  // same with f9
void f9(obj const **const ppo) {}
void fc(const obj const **const ppo) {}

// same declarations
void f3(obj *const *ppo) {}
void fa(obj *const *const ppo) {}

// same
void f6(const obj *const *ppo) {}
void f8(obj const *const *ppo) {}
void fb(const obj const *const *ppo) {}
// same declarations with above
void fe(obj const *const *const ppo) {} // same
void fd(const obj *const *const ppo) {}
void ff(const obj const *const *const ppo) {}

请注意,指定多个相同的限定符在 C 中有效,在 C++ 中无效。

我们可以说 obj const * 不同于 obj *const 吗?

是的 - 一个是指向可变 obj 的 const 指针,另一个是指向 const obj 的可变指针。

为什么编译器只有在调用 f 时才会发出这个警告

如上所述,const 属性不具有传递性。 T ** 指针不能隐式转换为 const T** 指针。

,不是g?!

但是 T* 指针可以转换为 const T* 指针。就像T **** 指针可以转换为T *** const * 指针。就像最后一个 * 一样重要。

如果f要说我不打算改变指针的内容和它指向的地方的内容,应该是什么原型?

它应该保持原样。有些人认为这是一种语言限制,T** 无法隐式转换为const T**。在这种情况下只需投射指针。有关现实生活中的限制,请参阅man 3p execveRATIONALE 部分The statement about argv[] and envp[] being constants is ....。为了完整起见,我将复制表格:

Comptibility of dst=src:
┌────────────────────┬──────────┬────────────────┬───────────────┬─────────────────────┐
│               dst: │ char **  │ const char * * │ char *const*  │ const char *const*  │
├────────────────────┼──────────┼────────────────┼───────────────┼─────────────────────┤
│src:                │          │                │               │                     │
│char **             │  VALID   │       —        │     VALID     │          —          │
│const char **       │    —     │     VALID      │       —       │        VALID        │
│char * const *      │    —     │       —        │     VALID     │          —          │
│const char *const*  │    —     │       —        │       —       │        VALID        │
└────────────────────┴──────────┴────────────────┴───────────────┴─────────────────────┘

我还发现:Why isn't it legal to convert "pointer to pointer to non-const" to a "pointer to pointer to const"pointer to array not compatible to a pointer to 'const' array?Pointer to array with const qualifier in C & C++

【讨论】:

  • 感谢您的完整回答。 Some consider it a language limitation that T** can't be converted to const T** implicitly. 我也这么认为。我认为应该进行 const 转换。每个函数都应该能够定义指针的哪些部分不会改变。如果你有一个 int,每个函数都应该能够明确定义它不会改变它的值。它应该能够定义它不会改变的部分,包括 int 值本身。我认为这个警告是不合理的。
  • 我认为你做错了。 f3、fa 和 fb 不同。在 fa 你不能改变 ppo,而如果你可以改变 f3。在 fb 中,您会收到警告,而在其他两个中则不会。
  • 所以结论是gcc不合理地发出警告,应该改变。我的这个结论不正确吗?
  • think you did a bit mistake. f3, fa and fb differ 是的,希望已修复。 gcc issues the warning irreasonably and should be changed. Am I incorrect in this conclusion 不,这是非常不正确的。 Gcc 遵循语言规范并正确诊断无效的语言结构。
【解决方案2】:

更新:此答案已更新以修复 cmets 中讨论的错误

const 与指针一起使用时的工作方式有点微妙。

const obj*obj const* 表示指针指向 const obj 类型,而 obj* const 表示指针是 const 并指向非 const obj 类型。

使用这个逻辑const obj** 等价于obj const**,它是一个非常量指针,指向一个非常量指针,指向一个常量对象,而obj* const* 是一个非常量指针,指向一个常量指向非 const obj 的指针。而obj** const是一个const指针,指向一个非常量指针,指向一个非常量obj。

C++ 可以将非 const 类型隐式转换为 const 类型。您可以在调用 f2(o) 中看到这一点,它隐式地将 obj* 转换为 obj* const

另一方面,C++ 在隐式转换多级指针时必须小心。如果 C++ 允许将 obj** 隐式转换为 const obj**,则将允许以下代码:

const int x = 0;             //x should never be modified
int* xPtr;
const int** xPtrPtr = &xPtr; //would be legal if C++ allowed int** -> const int** conversion
*xPtrPtr = &x;
*xPtr = 1;                   //This changes the value of x!

因此,GCC 在调用f3(o) 时会发出错误,因为它会将obj** 隐式转换为const obj**

您可以阅读完整的多级指针转换要求here

我不知道你需要你的代码做什么,但如果你要修改f3 中的底层objobj*,你应该将其参数更改为obj** const。如果您不想修改底层 obj 但修改 obj* 您应该使用 obj const** 作为参数。如果您不想修改objobj*,您应该使用obj const* const*

【讨论】:

  • 感谢您的描述。实际上,我需要确保调用者将指针视为 const。我的意思是内容而不是指针本身。如果被调用函数改变了指针,它不会影响调用函数中指针的值。所以,我的意思是内容。那么如何定义函数呢?
  • @hamidi 你应该使用obj const**obj const*const*
  • @hamidi 或者你可以使用:typedef const obj* objPtr;int f3(const objPtr* o) { ... } 相当于 int f3(obj const*const* o) 但更容易阅读
  • 我认为你在第二段中做错了。 const obj 等于 const obj constobj const。我们可以将 const 放在 obj 之前或之后,它的含义没有什么不同。但是 const before * 和 after * 导致两种不同的含义。由于指针的 const 可能只在它们之后,所以我们也将 const 放在 obj 之后。现在我回顾一下我的问题。为此,我更新它。请检查它。
  • const obj* means that the pointer points to a const obj type while obj const*const obj*obj const* 意思相同。
猜你喜欢
  • 1970-01-01
  • 2015-10-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多