const int\*、const int \* const 和 int const \* 有什么区别?

新手上路,请多包涵

我总是搞砸如何正确使用 const int*const int * constint const * 。是否有一套规则来定义你能做什么和不能做什么?

我想知道在分配、传递给函数等方面的所有注意事项。

原文由 ultraman 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.3k
2 个回答

向后阅读(由 Clockwise/Spiral Rule 驱动):

  • int* 指向int的指针
  • int const * - 指向 const int 的指针
  • int * const - 指向 int 的 const 指针
  • int const * const - 指向 const int 的 const 指针

现在第一个 const 可以在类型的任一侧,所以:

  • const int * == int const *
  • const int * const == int const * const

如果您想真正发疯,可以执行以下操作:

  • int ** - 指向 int 的指针
  • int ** const - 一个指向 int 指针的 const 指针
  • int * const * - 一个指向 const 的指针,指向一个 int
  • int const ** - 指向 const int 的指针
  • int * const * const - 一个指向 int 的 const 指针

并确保我们清楚 const 的含义:

 int a = 5, b = 10, c = 15;

const int* foo;     // pointer to constant int.
foo = &a;           // assignment to where foo points to.

/* dummy statement*/
*foo = 6;           // the value of a can´t get changed through the pointer.

foo = &b;           // the pointer foo can be changed.

int *const bar = &c;  // constant pointer to int
                      // note, you actually need to set the pointer
                      // here because you can't change it later ;)

*bar = 16;            // the value of c can be changed through the pointer.

/* dummy statement*/
bar = &a;             // not possible because bar is a constant pointer.

foo 是一个指向常量整数的变量指针。这使您可以更改指向的内容,但不能更改指向的值。最常见的情况是 C 风格的字符串,其中您有一个指向 const char 的指针。您可以更改指向的字符串,但不能更改这些字符串的内容。当字符串本身位于程序的数据段中并且不应更改时,这一点很重要。

bar 是指向可以更改的值的常量或固定指针。这就像一个没有额外语法糖的参考。由于这个事实,通常你会使用一个引用,你会使用 T* const 指针,除非你需要允许 NULL 指针。

原文由 Matt Price 发布,翻译遵循 CC BY-SA 4.0 许可协议

没有人提到 Kernighan 和 Ritchie 在他们的 C 书中指出的声明的 系统 基础:

声明模仿表达式。

我将重复这一点,因为它非常重要,并给出了一个清晰的策略来解析最复杂的声明:

声明模仿表达式。

声明包含与声明的标识符稍后可以出现的表达式相同的运算符,它们在表达式中具有相同的优先级。这就是为什么“顺时针螺旋规则”是错误的:评估顺序严格由运算符优先级决定,完全不考虑左、右或旋转方向。

以下是一些示例,按照复杂性的顺序排列:

  • int i; :当 i 按原样使用时,它是 int 类型的表达式。因此, i 一个整数。
  • int *p; :当 p 被取消引用时 * ,表达式的类型是 int 。因此, p 是一个指向 int 的指针。
  • const int *p; :当 p* 取消引用时,表达式的类型是 const int 因此, p 是一个指向 const int 的指针。
  • int *const p; : p 是常量。如果使用 * 取消引用此常量表达式,则表达式的类型为 int 。因此, p 是一个指向 int 的 const 指针。
  • const int *const p; : p 是常量。如果使用 * 取消引用此常量表达式,则表达式的类型为 const int 。因此, p 是一个指向 const int 的 const 指针。

到目前为止,我们还没有遇到运算符优先级的任何问题:我们只是从右到左进行评估。当我们对指针数组和指向数组的指针感兴趣时,情况就会改变。您可能希望打开 备忘单

  • int a[3]; :当我们将数组索引运算符应用于 a 时,结果是 int 。因此, a 是一个 int 数组。
  • int *a[3]; :这里索引运算符优先级较高,所以我们先应用它:当我们将数组索引运算符应用到 a 时,结果是 int * 。因此, a 是一个指向 int 的指针数组。这并不少见。
  • int (*a)[3]; :这里的运算符优先级被圆括号覆盖,与任何表达式完全相同。因此,我们 首先 取消引用。我们现在知道 a 是指向某种类型的指针。 *a ,取消引用的指针,是 该类型的表达式。 当我们将数组索引运算符应用于 *a 时,我们得到一个普通的 int,这意味着 *a 是一个由三个 int 组成的数组,而 a 是一个指针到那个数组。这在 C++ 模板之外相当少见,这就是运算符优先级不适合这种情况的原因。请注意,如何使用这样的指针是其声明的模型: int i = (*a)[1]; 。括号是必须首先取消引用的。
  • int (*a)[3][2]; :没有什么可以阻止任何人拥有指向多维数组的指针,在这种情况下,圆形螺旋顺时针建议变得明显是无稽之谈。

现实生活中有时会出现的东西是函数指针。我们也需要括号,因为函数调用运算符( operator()() 在 C++ 中,简单的语法规则在 C 中)比取消引用具有更高的优先级 operator*() ,同样因为拥有函数更常见返回指针而不是指向函数的指针:

  • int *f(); :首先调用函数,所以 f 是一个函数。调用必须取消引用才能产生一个 int,因此返回值是一个指向 int 的指针。用法: int i = *f();

  • int (*fp)(); :括号更改运算符应用程序的顺序。因为我们必须首先取消引用,所以我们知道 fp 是指向某物的指针。因为我们可以将函数调用运算符应用于 *fp 我们知道(在 C 中) fp 是指向函数的指针;在 C++ 中,我们只知道定义了 operator()() 的东西。由于调用不带参数并返回一个 int, fp 在 C++ 中是指向具有该签名的函数的指针。 (在 C 中,空参数列表表示对参数一无所知,但未来的 C 规范可能会禁止这种过时的使用。)

  • int *(*fp)(); :当然我们可以从指向的函数返回指向 int 的指针。

  • int (*(*fp)())[3]; :首先取消引用,因此是指针;应用函数调用运算符 next,因此是指向函数的指针;再次取消引用返回值,因此指向返回指针的函数的指针;将索引运算符应用于 指向函数的指针,返回指向数组的指针。结果是一个整数,因此指向函数的指针返回指向整数数组的指针。-

所有括号都是必需的:如前所述,我们必须优先使用 (*fp) 取消引用函数指针,然后再发生其他任何事情。显然,我们需要函数调用;并且由于该函数返回一个 指向数组 的指针(而不是它的第一个元素!),我们必须在索引它之前也取消引用它。我承认我写了一个测试程序来检查这个,因为我不确定,即使使用这种万无一失的方法;-)。这里是:

 #include <iostream>
using namespace std;

int (*f())[3]
{
  static int arr[3] = {1,2,3};
  return &arr;
}

int (*(*fp)())[3] = &f;

int main()
{
  for(int i=0; i<3; i++)
  {
    cout << (*(*fp)())[i] << endl;
  }
}

注意声明模仿表达的方式多么美妙!

原文由 Peter - Reinstate Monica 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题