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

What is the difference between const int*, const int * const, and int const *?

提问人: 提问时间:7/17/2009 最后编辑:flamingo 更新时间:8/29/2023 访问量:710551

问:

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

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

C C 指针 常量 C++-FAQ

评论

247赞 James McNellis 6/14/2010
您可以使用“顺时针/螺旋规则”来破译大多数 C 和 C++ 声明。
74赞 Dave 11/3/2010
cdecl.org 是一个很棒的网站,可以为您自动翻译 C 声明。
11赞 Mark K Cowan 7/10/2015
@Calmarius:从类型名称 / 应该在的地方开始,尽可能向右移动,必要时向左移动。.从括号的右边开始,然后我们必须向左移动: .在 parens 之外,我们可以向右移动:.然后我们必须向左移动: .重复上述步骤以展开参数 (the ): 。在像 Pascal 这样易于阅读的语言中,等效的单行声明是什么?int *(*)(char const * const)*pointerpointer to function of ...pointer to function of ... that returns pointer to int...pointer to function of (constant pointer to constant char) that returns pointer to int
3赞 Calmarius 7/10/2015
@MarkKCowan 在 Pascal 中,它会像 .函数类型意味着指向函数的指针,因此无需指定它,并且 Pascal 不强制执行常量正确性。它可以从左到右阅读。function(x:^char):^int
12赞 Cupcake 7/31/2016
“const”左边的第一件事是常数。如果“const”是最左边的东西,那么它右边的第一个东西就是不变的东西。

答:

2817赞 Matt Price 7/17/2009 #1

向后阅读(由顺时针/螺旋法则驱动):

  • 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 指针
  • ...

如果您不确定,可以使用 cdecl+ 等工具自动将声明转换为散文。

为了确保我们清楚以下的含义: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* constNULL

评论

597赞 Michael 7/18/2009
我想附加一个经验法则,它可以帮助您记住如何发现“const”是适用于指针还是指向数据:在星号处拆分语句,那么,如果 const 关键字出现在左侧(如“const int * foo”) - 它属于指向数据,如果它位于右侧('int * const bar') - 它是关于指针的。
14赞 Mooing Duck 5/29/2013
@Jeffrey:只要没有括号,向后阅读就可以很好地工作。然后,好吧......使用 typedefs
17赞 Wolf 6/18/2014
+1,但更好的总结是:向后阅读指针声明,这意味着,接近@Michael的陈述:在第一个星号处停止正常的从左到右阅读。
4赞 RastaJedi 8/9/2016
@gedamial,它工作正常,但你必须在声明它的同时分配它(因为你不能重新分配“const 指针”)。 工作得很好。const int x = 0; const int *const px = &x; const int *const *const p = &px;
3赞 Forbin 10/9/2019
@JayeshBhoi,没有。常量指针的全部意义(没有双关语)是你不能改变它所指向的位置!在 C 和 C++ 中,这完全独立于指针的值。NULL 在这里没有获得任何特殊特权。
21赞 AProgrammer 7/17/2009 #2

一般规则是关键字立即应用于其前面的内容。例外情况,开始适用于以下内容。constconst

  • const int*与“指向常量 int 的指针”相同,并表示“指向常量 int 的指针”。int const*
  • const int* const与 和 表示“指向常量 int 的常量指针”相同。int const* const

编辑:对于该做和不该做,如果这个答案还不够,你能更准确地说明你想要什么吗?

20赞 ufukgun 7/17/2009 #3

的简单用法。const

最简单的用法是声明一个命名常量。为此,将一个常量声明为变量,但在它前面添加。必须立即在构造函数中初始化它,因为当然,以后不能设置该值,因为这会改变它。例如:const

const int Constant1=96; 

将创建一个整数常量,毫无想象力地称为 ,其值为 96。Constant1

这些常量对于程序中使用的参数很有用,但在编译程序后不需要更改。对于程序员来说,它比 C 预处理器命令具有优势,因为它被编译器本身理解和使用,而不仅仅是在到达主编译器之前由预处理器替换到程序文本中,因此错误消息更有帮助。#define

它也适用于指针,但必须小心确定指针或它指向的内容是恒定的还是两者兼而有之。例如:const

const int * Constant2 

声明 that 是指向常量整数的可变指针,并且:Constant2

int const * Constant2

是一种替代语法,它执行相同的操作,而

int * const Constant3

声明 that 是指向变量整数的常量指针,并且Constant3

int const * const Constant4

声明这是指向常量整数的常量指针。基本上,“const”适用于其左边的任何事物(除非那里什么都没有,在这种情况下,它适用于其右边的任何东西)。Constant4

编号: http://duramecho.com/ComputerInformation/WhyHowCppConst.html

73赞 luke 7/17/2009 #4

就像几乎每个人都指出的那样:

const X* p、X* const p 和 const X* const p 有什么区别?

您必须阅读指针声明 从右到左。

  • const X* p表示“p 指向一个 const 的 X”:X 对象不能通过 p 更改。

  • X* const p表示“p 是指向非 const 的 X 的常量指针”:你不能改变指针 p 本身,但你可以通过 p 改变 X 对象。

  • const X* const p表示“p 是指向 const 的 X 的常量指针”:您不能更改指针 p 本身,也不能通过 p 更改 X 对象。

评论

7赞 Jesse Chisholm 1/18/2019
别忘了 == 如const X* p;X const * p;"p points to an X that is const": the X object can't be changed via p.
183赞 Kaz Dragon 7/17/2009 #5

我想这里已经回答了所有问题,但我只想补充一点,你应该提防 s!它们不仅仅是文本替换。typedef

例如:

typedef char *ASTRING;
const ASTRING astring;

的类型是 ,而不是 。这也是我总是倾向于放在字体右边的原因之一,而不是在开头。astringchar * constconst char *const

评论

39赞 Mephane 1/28/2011
对我来说,这就是从不 typedef 指针的原因。我没有看到这样的事情的好处(我认为它来自C语言的实践,许多开发人员一直在这样做)。太好了,我用 ,它不会加快打字速度,并介绍您提到的问题。typedef int* PINT*P
2赞 T.E.D. 10/17/2012
@Mephane - 我能看到。然而,在我看来,为了继续使用特殊的语法规则(关于“const”位置),而不是避免使用特殊的语法规则,以便您可以安全地使用这种语言功能,这似乎是一种倒退。
9赞 ApproachingDarknessFish 12/27/2013
@Mephane确实是 typedef 的一个相当愚蠢的用法,尤其是因为它让我认为系统存储使用啤酒作为内存。不过,typedef 对于处理指向函数的指针非常有用。PINT
3赞 vgru 5/9/2017
@Mephane:在使用某些遗留宏时,我不得不使用pSomething几次,这些宏被编写为接受类型,但如果该类型不是单个字母数字标识符,则会分解。:)
0赞 dgnuff 3/15/2018
@T.E.D.当您开始混合指针、常量和其他限定符时,问题就来了。你要么有一个半生不熟的解决方案,要么你进入 typedef 重载。请参阅 Win32 API,了解这可能导致的可怕的难看的混乱。LPCTSTR 是 Win32 typedef 的一个示例。它到底是什么?是的,我也必须查找它,并且我花了大量时间对 Win32 API 进行编程。
22赞 T.E.D. 7/17/2009 #6

这个问题恰说明了为什么我喜欢按照我在问题中提到的方式做事,类型 id 之后的 const 是否可以接受?

简而言之,我发现记住这条规则的最简单方法是“const”在它所适用的事物之后。所以在你的问题中,“int const *”意味着 int 是常量,而“int * const”意味着指针是常量。

如果有人决定把它放在最前面(例如:“const int *”),在这种情况下,作为一个特殊的例外,它适用于它后面的事物。

许多人喜欢使用这个特殊的例外,因为他们认为它看起来更好。我不喜欢它,因为它是一个例外,因此混淆了事情。

评论

3赞 Matt Price 7/17/2009
我在这个问题上很纠结。从逻辑上讲,这是有道理的。然而,大多数 c++ 开发人员会编写,并且它变得更加自然。无论如何,您多久使用一次,通常引用就可以了。当我想要一个时,我曾经被这一切咬过,而是写了.在略有不同的上下文中出现相同的问题。const T*T* constboost::shared_ptr<const T>const boost::shared_ptr<T>
0赞 T.E.D. 7/17/2009
实际上,我使用常量指针的频率比使用常量的频率更高。此外,您必须考虑在存在指向指针(等)的情况下将如何反应。 诚然,这些情况很少见,但以一种您可以沉着处理这些情况的方式思考事情会很好。
1赞 dgnuff 3/15/2018
将 const 放在类型的右边的另一个很好的优点是,现在 any 左边的所有东西都是 const 的类型,而它右边的一切都是实际上是 const 的类型。举个例子。不,我通常不会这样写,这只是一个例子。首先:键入 int,而 int 即 const 是 const 指针的内容,即 的内容。第二个 const: type 是指向 int 的指针,const oblect 是constint const * const * p;constpconstp
6赞 Jeff Burdges 9/13/2011 #7

在 C++ 中,围绕常量正确性还有许多其他微妙的点。我想这里的问题只是关于 C,但我会给出一些相关示例,因为标签是 C++ :

  • 您经常传递大型参数(如字符串),以防止对象被修改或复制。例:TYPE const &

    TYPE& TYPE::operator=(const TYPE &rhs) { ... return *this; }

    但毫无意义,因为引用始终是常量。TYPE & const

  • 应始终将不修改类的类方法标记为 ,否则无法从引用调用该方法。例:constTYPE const &

    bool TYPE::operator==(const TYPE &rhs) const { ... }

  • 在一些常见情况下,返回值和方法都应为 const。例:

    const TYPE TYPE::operator+(const TYPE &rhs) const { ... }

    事实上,const 方法不得返回内部类数据作为对非常量的引用。

  • 因此,通常必须使用 const 重载创建 const 和非 const 方法。例如,如果你定义 ,那么你可能还需要由以下公式给出的非常量版本:T const& operator[] (unsigned i) const;

    inline T& operator[] (unsigned i) { return const_cast<char&>( static_cast<const TYPE&>(*this)[](i) ); }

Afaik,C 中没有 const 函数,非成员函数本身在 C++ 中不能是 const,const 方法可能会有副作用,编译器不能使用 const 函数来避免重复的函数调用。事实上,即使是一个简单的引用,也可能见证它所引用的值在其他地方被改变。int const &

54赞 Behrooz Tabesh 5/18/2014 #8
  1. 常量参考:

    对变量(此处为 int)的引用,该变量是常量。我们主要将变量作为引用传递,因为引用的大小小于实际值,但有一个副作用,那就是因为它就像实际变量的别名。我们可能会通过对别名的完全访问意外地更改主变量,因此我们将其设置为恒定以防止这种副作用。

    int var0 = 0;
    const int &ptr1 = var0;
    ptr1 = 8; // Error
    var0 = 6; // OK
    
  2. 常量指针

    一旦常量指针指向变量,它就不能指向任何其他变量。

    int var1 = 1;
    int var2 = 0;
    
    int *const ptr2 = &var1;
    ptr2 = &var2; // Error
    
  3. 指向常量的指针

    无法更改其指向的变量值的指针称为指向常量的指针。

    int const * ptr3 = &var2;
    *ptr3 = 4; // Error
    
  4. 指向常量的常量指针

    指向常量的常量指针是指针既不能更改它所指向的地址,也不能更改保留在该地址上的值。

    int var3 = 0;
    int var4 = 0;
    const int * const ptr4 = &var3;
    *ptr4 = 1;     // Error
     ptr4 = &var4; // Error
    
17赞 Abhijit Sahu 1/4/2015 #9

这很简单,但很棘手。请注意,我们可以将限定符应用于任何数据类型(、、等)。constintcharfloat

让我们看看下面的例子。


const int *p ==> *p是只读的 [ 是指向常量整数的指针]p

int const *p ==> *p是只读的 [ 是指向常量整数的指针]p


int *p const ==> 陈述。编译器引发语法错误。

int *const p ==> p是只读的 [ 是指向整数的常量指针]。 由于此处的指针是只读的,因此声明和定义应位于同一位置。pp


const int *p const ==> 陈述。编译器引发语法错误。

const int const *p ==> *p是只读的

const int *const p ==> *p并且是只读的 [ 是指向常量整数的常量指针]。由于此处的指针是只读的,因此声明和定义应位于同一位置。ppp


int const *p const ==> 陈述。编译器引发语法错误。

int const int *p ==> 陈述。编译器引发语法错误。

int const const *p ==> *p是只读的,等同于int const *p

int const *const p ==> *p并且是只读的 [ 是指向常量整数的常量指针]。由于此处的指针是只读的,因此声明和定义应位于同一位置。ppp

11赞 rgk 3/21/2015 #10

我和你有同样的疑问,直到我看到C++大师斯科特·迈耶斯(Scott Meyers)这本书。请参阅本书中的第三项,其中他详细讨论了如何使用 .const

只需遵循此建议即可

  1. 如果单词出现在星号的左侧,则指向的是恒定的const
  2. 如果单词出现在星号的右侧,则指针本身是恒定的const
  3. 如果两边都出现,则两者都是恒定的const
540赞 Shijing Lv 7/10/2015 #11

对于那些不了解顺时针/螺旋法则的人: 从变量的名称开始,顺时针移动(在本例中,向后移动)到下一个指针类型。重复直到表达结束。

下面是一个演示:

pointer to int

const pointer to int const

pointer to int const

pointer to const int

const pointer to int

评论

9赞 Jan Rüegg 4/8/2016
@Rog它曾经拥有所有开放访问权限......不幸的是,我没有写这篇文章,我自己也没有访问权限。但是,这是该文章的存档版本,该版本仍然有效: archive.is/SsfMX
12赞 Matthew Read 9/19/2016
这个复杂的示例仍然从右到左,但包括像往常一样解析括号。整个顺时针螺旋式的事情并没有让这变得更容易。
13赞 naXa stands with Ukraine 5/4/2017
终极示例:来自 archive.is/SsfMXvoid (*signal(int, void (*fp)(int)))(int);
8赞 haccks 5/12/2017
不要依赖此规则。这并不普遍。在某些情况下,它会失败。
4赞 flonk 4/7/2021
除了提到的事实之外,螺旋形状只是误导,为什么在第二个示例中是用一种颜色着色,但在第五个例子中是用两种颜色着色的,而在后者中它仍然被解释为?颜色到底是什么意思?* constconstant pointer
8赞 Cheers and hth. - Alf 1/6/2016 #12

C和C++声明语法被原始设计者反复描述为失败的实验。

相反,让我们将类型命名为“指针”;我称之为:TypePtr_

template< class Type >
using Ptr_ = Type*;

现在是指向 的指针。Ptr_<char>char

Ptr_<const char>是指向 的指针。const char

并且是指向 的指针。const Ptr_<const char>constconst char

评论

4赞 sp2danny 9/14/2016
你有第一句话的引号吗?
1赞 Cheers and hth. - Alf 9/14/2016
@sp2danny:在谷歌上搜索“C 语法失败的实验”只会咳出对 Bjarne Stroustrup 的一些采访,他在采访中表达了自己的观点,例如,在 Slashdot 采访中,“我认为 C 声明器语法是一个失败的实验”。因此,对于C语言原始设计者的观点,我没有参考。我想它可以通过足够强大的研究工作找到,或者可能只是通过询问他们来反驳,但我认为现在的方式更好。对于那部分索赔,仍未确定并且可能是真的:)
1赞 Stargateur 1/1/2018
“C和C++声明语法一再被原始设计者描述为失败的实验。 对于C是错误的,请更改有关C的句子或提供一些引号。
2赞 Antti Haapala -- Слава Україні 8/25/2019
@Stargateur “Sethi (...观察到,如果将间接运算符用作后缀运算符而不是前缀,许多嵌套声明和表达式将变得更简单,但到那时更改为时已晚。来自 DMR。当然,DMR并没有发明常量和易失性关键字,它们来自C++ / X3J11,如该页面所示。
2赞 dgnuff 3/15/2018 #13

这主要涉及第二行:最佳实践、分配、函数参数等。

一般做法。尽你所能。或者换一种说法,让一切从头开始,然后准确地删除允许程序运行所需的最小集合。这将对实现持续正确性有很大帮助,并有助于确保当人们尝试分配到他们不应该修改的内容时,不会引入微妙的错误。constconstconst

避免像瘟疫一样const_cast<>。它有一两个合法的用例,但它们很少,而且相距甚远。如果你试图改变一个对象,你会做得更好,找到第一个宣布它的人,并与他们讨论这个问题,就应该发生什么达成共识。constconst

这非常巧妙地导致了作业。只有当它是非常量时,你才能赋值到某物中。如果要分配到 const 中,请参阅上文。请记住,在声明中,不同的事情是 - 这里的其他答案已经令人钦佩地涵盖了这个问题,所以我不会深入探讨。int const *foo;int * const bar;const

功能参数:

按值传递:例如 在呼叫站点,您不会以一种或另一种方式关心。可以说,有一些用例可以将函数声明为,但这对调用者没有影响,只对函数本身有影响,因为函数在调用期间不能更改传递的任何值。void func(int param)void func(int const param)

通过引用传递:例如 现在它确实有所作为。正如刚才宣布的那样,允许更改,任何调用站点都应该准备好处理后果。将声明更改为更改合同,并保证现在不能更改,这意味着传入的内容将返回。正如其他人所指出的,这对于廉价地传递您不想更改的大型对象非常有用。传递引用比按值传递大型对象要便宜得多。void func(int &param)funcparamvoid func(int const &param)funcparam

通过指针传递:例如 这两个函数几乎是它们的引用对应物的同义词,但需要注意的是,被调用的函数现在需要检查,除非其他一些合同保证保证它永远不会收到 in 。void func(int *param)void func(int const *param)nullptrfuncnullptrparam

关于该主题的评论文章。在这样的情况下证明正确性是非常困难的,犯错太容易了。因此,不要冒险,并始终检查指针参数。从长远来看,您将避免痛苦和折磨,并且很难找到错误。至于检查的成本,它非常便宜,如果编译器中内置的静态分析可以管理它,优化器无论如何都会省略它。打开 MSVC 的链接时间代码生成,或 GCC 的 WOPR(我认为),您将在程序范围内获得它,即即使在跨越源代码模块边界的函数调用中也是如此。nullptr

归根结底,以上所有内容都是一个非常坚实的理由,即始终更喜欢引用指针。他们只是在各方面都更安全。

4赞 Muhammad Inshaal Muddassir 9/23/2018 #14

两边带有 int 的 const 将指向常量 int 的指针

const int *ptr=&i;

艺术

int const *ptr=&i;

constafter 将常量指针指向 int*

int *const ptr=&i;

在本例中,所有这些都是指向常量整数的指针,但这些都不是常量指针

 const int *ptr1=&i, *ptr2=&j;

在这种情况下,all 都是指向常量整数的指针,而 ptr2 是指向常量整数的常量指针但 ptr1 不是常量指针:

int const *ptr1=&i, *const ptr2=&j;
8赞 sri 2/4/2019 #15

对我来说,相对于 TO 或 RIGHT 出现在 LEFT 和 RIGHT 上的位置,可以帮助我弄清楚实际含义。const*

  1. 左边的 A 表示指针指向的对象是对象。const*const

  2. 右边的 A 表示指针是指针。const*const

下表摘自斯坦福CS106L标准C++编程实验室课程阅读器。

enter image description here

评论

0赞 BattleTested_закалённый в бою 12/14/2020
指针的“重新分配”和“修改”之间到底有什么区别?
0赞 Diracx 6/7/2023
@BattleTested_закалённыйвбою 重新分配意味着可以更改指针指向的地址,但对于常量指针来说,这是不正确的(表中的“否”),如果指针地址可以更改,但它指向的值始终是恒定的,这意味着我们不能修改指针。
2赞 Undefined Behavior 2/18/2019 #16

只是为了 C 的完整性,遵循其他解释,不确定 C++。

  • pp - 指针到指针
  • p - 指针
  • data - 示例中指向的事物x
  • bold - 只读变量

指针

  • p 数据 -int *p;
  • p 数据 - int const *p;
  • p 数据 -int * const p;
  • p 数据 - int const * const p;

指针到指针

  1. PP P 数据 -int **pp;
  2. PP P 数据 -int ** const pp;
  3. PP P 数据 -int * const *pp;
  4. PP P 数据 - int const **pp;
  5. PP P 数据 -int * const * const pp;
  6. PP P 数据 - int const ** const pp;
  7. PP P 数据 - int const * const *pp;
  8. PP P 数据 - int const * const * const pp;
// Example 1
int x;
x = 10;
int *p = NULL;
p = &x;
int **pp = NULL;
pp = &p;
printf("%d\n", **pp);

// Example 2
int x;
x = 10;
int *p = NULL;
p = &x;
int ** const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);

// Example 3
int x;
x = 10;
int * const p = &x; // Definition must happen during declaration
int * const *pp = NULL;
pp = &p;
printf("%d\n", **pp);

// Example 4
int const x = 10; // Definition must happen during declaration
int const * p = NULL;
p = &x;
int const **pp = NULL;
pp = &p;
printf("%d\n", **pp);

// Example 5
int x;
x = 10;
int * const p = &x; // Definition must happen during declaration
int * const * const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);

// Example 6
int const x = 10; // Definition must happen during declaration
int const *p = NULL;
p = &x;
int const ** const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);

// Example 7
int const x = 10; // Definition must happen during declaration
int const * const p = &x; // Definition must happen during declaration
int const * const *pp = NULL;
pp = &p;
printf("%d\n", **pp);

// Example 8
int const x = 10; // Definition must happen during declaration
int const * const p = &x; // Definition must happen during declaration
int const * const * const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);

N 级取消引用

继续前进,但愿人类将你逐出教会。

int x = 10;
int *p = &x;
int **pp = &p;
int ***ppp = &pp;
int ****pppp = &ppp;

printf("%d \n", ****pppp);
4赞 blue_note 8/1/2019 #17
  • 如果 位于 的左边,则表示值(是否为 或const*const intint const)
  • 如果 位于 的右边,则指指针本身const*
  • 它可以同时是两者

重要的一点:并不意味着你所指的值是恒定的!!。这意味着您无法通过该指针更改它(这意味着,您不能分配 $*p = ...')。该值本身可能会以其他方式更改。例如const int *p

int x = 5;
const int *p = &x;
x = 6; //legal
printf("%d", *p) // prints 6
*p = 7; //error 

这主要用于函数签名,以保证函数不会意外更改传递的参数。

2赞 RobertS supports Monica Cellio 2/7/2020 #18
  1. const int*- 指向常量对象的指针。int

您可以更改指针的值;您不能更改指针指向的对象的值。int


  1. const int * const- 指向常量对象的常量指针。int

不能更改指针的值,也不能更改指针指向的对象的值。int


  1. int const *- 指向常量对象的指针。int

此语句等效于 1。 - 您可以更改指针的值,但不能更改指针指向的对象的值。const int*int


实际上,还有第四个选项:

  1. int * const- 指向对象的常量指针。int

您可以更改指针指向的对象的值,但不能更改指针本身的值。指针将始终指向同一对象,但可以更改此对象的此值。intint


如果你想确定某种类型的C或C++结构,你可以使用David Anderson制定的顺时针/螺旋规则;但不要与罗斯·J·安德森(Ross J. Anderson)制定的安德森法则混淆,后者非常不同。

-1赞 Abhishek Mane 3/30/2021 #19

很多人回答正确,我会在这里组织得很好,并放置一些给定答案中缺少的额外信息。

Const 是 C 语言中的关键字,也称为限定符。常量可以 应用于任何变量的声明,以指定其值 不会改变

const int a=3,b;

a=4;  // give error
b=5;  // give error as b is also const int 

you have to intialize while declaring itself as no way to assign
it afterwards.

如何阅读?

只需从右到左阅读,每个语句都可以流畅地工作

3 主要内容

type a.    p is ptr to const int

type b.    p is const ptr to int 
 
type c.    p is const ptr to const int

[错误]

if * comes before int 

两种类型

1. const int *

2. const const int *

我们先看

主要类型 1。常量 int*

在 3 个地方安排 3 件事的方法 3!=6

i. * 开始时

*const int p      [Error]
*int const p      [Error]

ii. 开始时的常量

const int *p      type a. p is ptr to const int 
const *int p      [Error]

iii. 开始时的 int

int const *p      type a. 
int * const p     type b. p is const ptr to int

主要类型 2。const const int*

在 4 个地方安排 4 件事的方法,其中 2 个相似 4!/2!=12

i. * 开始时

* int const const p     [Error]
* const int const p     [Error]
* const const int p     [Error]
 

ii. 开始时的 int

int const const *p      type a. p is ptr to const int
int const * const p     type c. p is const ptr to const int
int * const const p     type b. p is const ptr to int

III. 开始时的常量

const const int *p     type a.
const const * int p    [Error]

const int const *p      type a.
const int * const p     type c.

const * int const p    [Error]
const * const int p    [Error]

多合一挤压

A型P是常量INT的PTR (5)

const int *p
int const *p

int const const *p
const const int  *p
const int  const *p

B型P为常量PTR到int (2)

int * const p
int * const const p;

C 型 p 是常量 PTR 到 const int (2)

int const * const p
const int * const p

只是一点点计算

1. const int * p        total arrangemets (6)   [Errors] (3)
2. const const int * p  total arrangemets (12)  [Errors] (6)

小额外

int 常量 * p,p2 ;

here p is ptr to const int  (type a.) 
but p2 is just const int please note that it is not ptr

int * 常量 p,p2 ;

similarly 
here p is const ptr to int  (type b.)   
but p2 is just int not even cost int

int const * 常量 p,p2 ;

here p is const ptr to const int  (type c.)
but p2 is just const int. 

完成

1赞 l.k 4/24/2021 #20

简单助记符:

type指针 <- ->指针*name


我喜欢把它看作是宣布“是”;从这个意义上说,意味着“是”的deref“,而意味着”是的deref”。int *iiintconst int *iiconst intint *const iconst iint

(这样想的一个危险是,它可能导致人们倾向于声明的风格,人们可能会讨厌/不允许这种风格)int const *i

7赞 MoBaShiR 5/2/2021 #21

以简单的方式记住:

如果 const 在 * 之前,则 value 是常量。

如果 const 在 * 之后,则 address 是常量。

如果 const 在 * 之前和之后都可用,则 value 和 address 都是恒定的。

例如

  1. int * 常量变量;这里的地址是恒定的。

  2. int const * 变量;这里的值是常量。

  3. int const * const 变量;value 和 address 都是常量。

4赞 slh 5/1/2022 #22

我在下面画了一张图片来解释这一点,也许有帮助。

int const v并且是相同的。const int v

enter image description here

2赞 Peter - Reinstate Monica 10/10/2022 #23

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

声明模仿表达式。

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

声明模仿表达式。

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

以下是一些示例,按复杂程度递增的顺序排列:

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

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

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

现实生活中有时会出现的一件事是函数指针。我们还需要括号,因为函数调用运算符(在 C++ 中,C 中的简单语法规则)的优先级高于 取消引用 ,再次是因为函数返回指针比指向函数的指针更常见:operator()()operator*()

  • int *f();:首先调用函数,函数也是如此。必须取消引用调用才能生成 int,因此返回值是指向 int 的指针。 用法: .fint i = *f();

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

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

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

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

请注意,声明模仿表达式是多么漂亮!