提问人: 提问时间:7/17/2009 最后编辑:flamingo 更新时间:8/29/2023 访问量:710551
const int*、const int * const 和 int const * 有什么区别?
What is the difference between const int*, const int * const, and int const *?
问:
我总是搞砸如何使用 、 和正确。有没有一套规则来定义你能做什么和不能做什么?const int*
const int * const
int const *
我想知道在分配、传递给函数等方面的所有注意事项。
答:
向后阅读(由顺时针/螺旋法则驱动):
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* const
NULL
评论
const int x = 0; const int *const px = &x; const int *const *const p = &px;
一般规则是关键字立即应用于其前面的内容。例外情况,开始适用于以下内容。const
const
const int*
与“指向常量 int 的指针”相同,并表示“指向常量 int 的指针”。int const*
const int* const
与 和 表示“指向常量 int 的常量指针”相同。int const* const
编辑:对于该做和不该做,如果这个答案还不够,你能更准确地说明你想要什么吗?
的简单用法。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
就像几乎每个人都指出的那样:
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 对象。
评论
const X* p;
X const * p;
"p points to an X that is const": the X object can't be changed via p.
我想这里已经回答了所有问题,但我只想补充一点,你应该提防 s!它们不仅仅是文本替换。typedef
例如:
typedef char *ASTRING;
const ASTRING astring;
的类型是 ,而不是 。这也是我总是倾向于放在字体右边的原因之一,而不是在开头。astring
char * const
const char *
const
评论
typedef int* PINT
*
P
PINT
这个问题恰恰说明了为什么我喜欢按照我在问题中提到的方式做事,类型 id 之后的 const 是否可以接受?
简而言之,我发现记住这条规则的最简单方法是“const”在它所适用的事物之后。所以在你的问题中,“int const *”意味着 int 是常量,而“int * const”意味着指针是常量。
如果有人决定把它放在最前面(例如:“const int *”),在这种情况下,作为一个特殊的例外,它适用于它后面的事物。
许多人喜欢使用这个特殊的例外,因为他们认为它看起来更好。我不喜欢它,因为它是一个例外,因此混淆了事情。
评论
const T*
T* const
boost::shared_ptr<const T>
const boost::shared_ptr<T>
const
int const * const * p;
const
p
const
p
在 C++ 中,围绕常量正确性还有许多其他微妙的点。我想这里的问题只是关于 C,但我会给出一些相关示例,因为标签是 C++ :
您经常传递大型参数(如字符串),以防止对象被修改或复制。例:
TYPE const &
TYPE& TYPE::operator=(const TYPE &rhs) { ... return *this; }
但毫无意义,因为引用始终是常量。
TYPE & const
应始终将不修改类的类方法标记为 ,否则无法从引用调用该方法。例:
const
TYPE 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 &
常量参考:
对变量(此处为 int)的引用,该变量是常量。我们主要将变量作为引用传递,因为引用的大小小于实际值,但有一个副作用,那就是因为它就像实际变量的别名。我们可能会通过对别名的完全访问意外地更改主变量,因此我们将其设置为恒定以防止这种副作用。
int var0 = 0; const int &ptr1 = var0; ptr1 = 8; // Error var0 = 6; // OK
常量指针
一旦常量指针指向变量,它就不能指向任何其他变量。
int var1 = 1; int var2 = 0; int *const ptr2 = &var1; ptr2 = &var2; // Error
指向常量的指针
无法更改其指向的变量值的指针称为指向常量的指针。
int const * ptr3 = &var2; *ptr3 = 4; // Error
指向常量的常量指针
指向常量的常量指针是指针既不能更改它所指向的地址,也不能更改保留在该地址上的值。
int var3 = 0; int var4 = 0; const int * const ptr4 = &var3; *ptr4 = 1; // Error ptr4 = &var4; // Error
这很简单,但很棘手。请注意,我们可以将限定符应用于任何数据类型(、、等)。const
int
char
float
让我们看看下面的例子。
const int *p
==> *p
是只读的 [ 是指向常量整数的指针]p
int const *p
==> *p
是只读的 [ 是指向常量整数的指针]p
int *p const
==> 错陈述。编译器引发语法错误。
int *const p
==> p
是只读的 [ 是指向整数的常量指针]。
由于此处的指针是只读的,因此声明和定义应位于同一位置。p
p
const int *p const
==> 错陈述。编译器引发语法错误。
const int const *p
==> *p
是只读的
const int *const p
==> *p
并且是只读的 [ 是指向常量整数的常量指针]。由于此处的指针是只读的,因此声明和定义应位于同一位置。p
p
p
int const *p const
==> 错陈述。编译器引发语法错误。
int const int *p
==> 错陈述。编译器引发语法错误。
int const const *p
==> *p
是只读的,等同于int const *p
int const *const p
==> *p
并且是只读的 [ 是指向常量整数的常量指针]。由于此处的指针是只读的,因此声明和定义应位于同一位置。p
p
p
我和你有同样的疑问,直到我看到C++大师斯科特·迈耶斯(Scott Meyers)的这本书。请参阅本书中的第三项,其中他详细讨论了如何使用 .const
只需遵循此建议即可
- 如果单词出现在星号的左侧,则指向的是恒定的
const
- 如果单词出现在星号的右侧,则指针本身是恒定的
const
- 如果两边都出现,则两者都是恒定的
const
对于那些不了解顺时针/螺旋法则的人: 从变量的名称开始,顺时针移动(在本例中,向后移动)到下一个指针或类型。重复直到表达结束。
下面是一个演示:
评论
void (*signal(int, void (*fp)(int)))(int);
* const
constant pointer
C和C++声明语法被原始设计者反复描述为失败的实验。
相反,让我们将类型命名为“指针”;我称之为:Type
Ptr_
template< class Type >
using Ptr_ = Type*;
现在是指向 的指针。Ptr_<char>
char
Ptr_<const char>
是指向 的指针。const char
并且是指向 的指针。const Ptr_<const char>
const
const char
评论
这主要涉及第二行:最佳实践、分配、函数参数等。
一般做法。尽你所能。或者换一种说法,让一切从头开始,然后准确地删除允许程序运行所需的最小集合。这将对实现持续正确性有很大帮助,并有助于确保当人们尝试分配到他们不应该修改的内容时,不会引入微妙的错误。const
const
const
避免像瘟疫一样const_cast<>。它有一两个合法的用例,但它们很少,而且相距甚远。如果你试图改变一个对象,你会做得更好,找到第一个宣布它的人,并与他们讨论这个问题,就应该发生什么达成共识。const
const
这非常巧妙地导致了作业。只有当它是非常量时,你才能赋值到某物中。如果要分配到 const 中,请参阅上文。请记住,在声明中,不同的事情是 - 这里的其他答案已经令人钦佩地涵盖了这个问题,所以我不会深入探讨。int const *foo;
int * const bar;
const
功能参数:
按值传递:例如 在呼叫站点,您不会以一种或另一种方式关心。可以说,有一些用例可以将函数声明为,但这对调用者没有影响,只对函数本身有影响,因为函数在调用期间不能更改传递的任何值。void func(int param)
void func(int const param)
通过引用传递:例如 现在它确实有所作为。正如刚才宣布的那样,允许更改,任何调用站点都应该准备好处理后果。将声明更改为更改合同,并保证现在不能更改,这意味着传入的内容将返回。正如其他人所指出的,这对于廉价地传递您不想更改的大型对象非常有用。传递引用比按值传递大型对象要便宜得多。void func(int ¶m)
func
param
void func(int const ¶m)
func
param
通过指针传递:例如 这两个函数几乎是它们的引用对应物的同义词,但需要注意的是,被调用的函数现在需要检查,除非其他一些合同保证保证它永远不会收到 in 。void func(int *param)
void func(int const *param)
nullptr
func
nullptr
param
关于该主题的评论文章。在这样的情况下证明正确性是非常困难的,犯错太容易了。因此,不要冒险,并始终检查指针参数。从长远来看,您将避免痛苦和折磨,并且很难找到错误。至于检查的成本,它非常便宜,如果编译器中内置的静态分析可以管理它,优化器无论如何都会省略它。打开 MSVC 的链接时间代码生成,或 GCC 的 WOPR(我认为),您将在程序范围内获得它,即即使在跨越源代码模块边界的函数调用中也是如此。nullptr
归根结底,以上所有内容都是一个非常坚实的理由,即始终更喜欢引用指针。他们只是在各方面都更安全。
两边带有 int 的 const 将指向常量 int 的指针:
const int *ptr=&i;
艺术
int const *ptr=&i;
const
after 将常量指针指向 int:*
int *const ptr=&i;
在本例中,所有这些都是指向常量整数的指针,但这些都不是常量指针:
const int *ptr1=&i, *ptr2=&j;
在这种情况下,all 都是指向常量整数的指针,而 ptr2 是指向常量整数的常量指针。但 ptr1 不是常量指针:
int const *ptr1=&i, *const ptr2=&j;
对我来说,相对于 TO 或 RIGHT 出现在 LEFT 和 RIGHT 上的位置,可以帮助我弄清楚实际含义。const
*
左边的 A 表示指针指向的对象是对象。
const
*
const
右边的 A 表示指针是指针。
const
*
const
下表摘自斯坦福CS106L标准C++编程实验室课程阅读器。
评论
只是为了 C 的完整性,遵循其他解释,不确定 C++。
- pp - 指针到指针
- p - 指针
- data - 示例中指向的事物
x
- bold - 只读变量
指针
- p 数据 -
int *p;
- p 数据 -
int const *p;
- p 数据 -
int * const p;
- p 数据 -
int const * const p;
指针到指针
- PP P 数据 -
int **pp;
- PP P 数据 -
int ** const pp;
- PP P 数据 -
int * const *pp;
- PP P 数据 -
int const **pp;
- PP P 数据 -
int * const * const pp;
- PP P 数据 -
int const ** const pp;
- PP P 数据 -
int const * const *pp;
- 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);
- 如果 位于 的左边,则表示值(是否为 或
const
*
const int
int const
) - 如果 位于 的右边,则指指针本身
const
*
- 它可以同时是两者
重要的一点:并不意味着你所指的值是恒定的!!。这意味着您无法通过该指针更改它(这意味着,您不能分配 $*p = ...')。该值本身可能会以其他方式更改。例如const int *p
int x = 5;
const int *p = &x;
x = 6; //legal
printf("%d", *p) // prints 6
*p = 7; //error
这主要用于函数签名,以保证函数不会意外更改传递的参数。
const int*
- 指向常量对象的指针。int
您可以更改指针的值;您不能更改指针指向的对象的值。int
const int * const
- 指向常量对象的常量指针。int
不能更改指针的值,也不能更改指针指向的对象的值。int
int const *
- 指向常量对象的指针。int
此语句等效于 1。 - 您可以更改指针的值,但不能更改指针指向的对象的值。const int*
int
实际上,还有第四个选项:
int * const
- 指向对象的常量指针。int
您可以更改指针指向的对象的值,但不能更改指针本身的值。指针将始终指向同一对象,但可以更改此对象的此值。int
int
如果你想确定某种类型的C或C++结构,你可以使用David Anderson制定的顺时针/螺旋规则;但不要与罗斯·J·安德森(Ross J. Anderson)制定的安德森法则混淆,后者非常不同。
很多人回答正确,我会在这里组织得很好,并放置一些给定答案中缺少的额外信息。
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.
完成
简单助记符:
type
指针 <- ->指针*
name
我喜欢把它看作是宣布“是”;从这个意义上说,意味着“是”的deref“,而意味着”是的deref”。int *i
i
int
const int *i
i
const int
int *const i
const i
int
(这样想的一个危险是,它可能导致人们倾向于声明的风格,人们可能会讨厌/不允许这种风格)int const *i
以简单的方式记住:
如果 const 在 * 之前,则 value 是常量。
如果 const 在 * 之后,则 address 是常量。
如果 const 在 * 之前和之后都可用,则 value 和 address 都是恒定的。
例如
int * 常量变量;这里的地址是恒定的。
int const * 变量;这里的值是常量。
int const * const 变量;value 和 address 都是常量。
我在下面画了一张图片来解释这一点,也许有帮助。
int const v
并且是相同的。const int v
没有人提到 Kernighan 和 Ritchie 在他们的 C 书中指出的声明背后的系统:
声明模仿表达式。
我将重复这一点,因为它非常重要,并且给出了一个清晰的策略来解析即使是最复杂的声明:
声明模仿表达式。
声明包含的运算符与声明的标识符稍后可以出现的表达式相同,并且它们在表达式中的优先级相同。这就是为什么“顺时针螺旋法则”是错误的:评估顺序严格由操作员优先级决定,完全无视左、右或旋转方向。
以下是一些示例,按复杂程度递增的顺序排列:
int i;
:按原样使用时,它是 类型的表达式。因此,是一个 int。i
int
i
int *p;
:当取消引用时,表达式的类型为 。因此,是指向 int 的指针。p
*
int
p
const int *p;
:当取消引用时,表达式的类型为 。因此,是指向 const int 的指针。p
*
const int
p
int *const p;
:为常量。如果此常量表达式被取消引用 ,则表达式的类型为 。因此,是指向 int 的常量指针。p
*
int
p
const int *const p;
:是常量。如果此常量表达式被取消引用 ,则表达式的类型为 。因此,是指向 const int 的 const 指针。p
*
const int
p
到目前为止,我们在运算符优先级方面还没有任何问题:我们只是从右到左进行了评估。当我们对指针数组和指向数组的指针感到有趣时,这种情况就会改变。您可能希望打开备忘单。
int a[3];
:当我们将数组索引运算符应用于 时,结果是一个 .因此,是 int 的数组。a
int
a
int *a[3];
:这里索引运算符的优先级更高,所以我们先应用它:当我们将数组索引运算符应用于 时,结果是一个.因此,是指向 int 的指针数组。这种情况并不少见。a
int *
a
int (*a)[3];
:此处运算符优先级被圆括号覆盖,与任何表达式完全相同。因此,我们首先取消引用。我们现在知道这是指向某种类型的指针。,取消引用的指针是该类型的表达式。当我们将数组索引运算符应用于 时,我们得到一个普通的 int,这意味着这是一个由三个 int 组成的数组,并且是指向该数组的指针。这在 C++ 模板之外相当罕见,这就是为什么运算符优先级不适合这种情况的原因。请注意,使用此类指针是其声明的模型。括号是必须的,以便首先取消引用。a
*a
*a
*a
a
int i = (*a)[1];
int (*a)[3][2];
: 没有什么能阻止任何人拥有指向多维数组的指针,在这种情况下,顺时针旋转的圆形螺旋建议变得明显是无稽之谈。
现实生活中有时会出现的一件事是函数指针。我们还需要括号,因为函数调用运算符(在 C++ 中,C 中的简单语法规则)的优先级高于 取消引用 ,再次是因为函数返回指针比指向函数的指针更常见:operator()()
operator*()
int *f();
:首先调用函数,函数也是如此。必须取消引用调用才能生成 int,因此返回值是指向 int 的指针。 用法: .f
int i = *f();
int (*fp)();
:括号更改运算符应用程序的顺序。因为我们必须首先取消引用,所以我们知道这是指向某事的指针。因为我们可以将函数调用运算符应用于我们知道(在 C 中)这是指向函数的指针;在 C++ 中,我们只知道它是为其定义的东西。由于调用不带参数并返回 int,因此在 C++ 中是指向具有该签名的函数的指针。(在 C 语言中,空参数列表表示对参数一无所知,但未来的 C 规范可能会禁止过时的使用。fp
*fp
fp
operator()()
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;
}
}
请注意,声明模仿表达式是多么漂亮!
评论
int *(*)(char const * const)
*
pointer
pointer 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
function(x:^char):^int