const int*、const int * const 和 int * const 之间有什么区别?
我总是搞乱如何正确使用 const int *
、const int * const
和 int * const
。 是否有一套规则来定义你可以做什么和不能做什么?
我想知道在分配、传递给函数等方面所有该做和不该做的事情。
I always mess up how to use const int *
, const int * const
, and int * const
correctly. Is there a set of rules defining what you can and cannot do?
I want to know all the do's and all don'ts in terms of assignments, passing to the functions, etc.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(23)
很多人都回答正确,我会在这里好好组织一下,并添加一些给定答案中缺少的额外信息。
如何阅读?
只需从右向左阅读每个语句即可顺利
3个主要内容
[错误]
两种类型
我们先看
主要类型 1. const int*
i。 * 开始时
ii. 开始处为常量
iii. int at start
主要类型 2. const const int*
i。 * 开始时
ii. int at start
iii. const at start
将所有内容压缩为一个
输入 a。 p 是 const int (5) 的 ptr
类型 b。 p 是 int (2) 的 const ptr
类型 c。 p 是 const ptr 到 const int (2)
只需少量计算
少量额外
int const * p,p2 ;
int * const p,p2 ;
int const * const p,p2 ; >
完成
Lot of people answered correctly I will just organize well here and put some Extra info which is missing in given Answers.
How to read ?
just read from right to left every statement works smoothly
3 main things
[Error]
two types
we look first
Major type 1. const int*
i. * at start
ii. const at start
iii. int at start
Major type 2. const const int*
i. * at start
ii. int at start
iii. const at start
squeezing all in one
type a. p is ptr to const int (5)
type b. p is const ptr to int (2)
type c. p is const ptr to const int (2)
just little calculation
little Extra
int const * p,p2 ;
int * const p,p2 ;
int const * const p,p2 ;
Finished
简单助记符:
type
指针<-*
-> pointeename
我喜欢将
int *i
视为声明“i
的取消引用是int
”; 从这个意义上说,const int *i
的意思是“i
的 deref 是const int
”,而int *const i
code> 表示“const i
的 deref 为int
”。(这样思考的一个危险是它可能会导致人们倾向于
int const *i
声明风格,人们可能会讨厌/不允许这种风格)simple mnemonic:
type
pointer <-*
-> pointeename
I like to think of
int *i
as declaring "the dereference ofi
isint
"; in this sense,const int *i
means "the deref ofi
isconst int
", whileint *const i
means "deref ofconst i
isint
".(the one danger of thinking like this is it may lead to favoring
int const *i
style of declaration, which people might hate/disallow)没有人提到 Kernighan 和 Ritchie 在他们的 C 书中指出的系统底层声明:
声明模仿表达式。
我会重复这一点,因为它非常重要,并且给出了清晰的策略解析最复杂的声明:
声明模仿表达式。
声明包含与声明的标识符稍后可以出现的表达式相同的运算符,并且具有与表达式中相同的优先级。 这就是为什么“顺时针螺旋规则”是错误的:求值顺序严格由运算符优先级决定,完全不考虑左、右或旋转方向。
以下是一些示例,按复杂性递增的顺序排列:
int i;
:当按原样使用i
时,它是以下表达式类型int
。 因此,i
是一个 int。int *p;
:当使用*
取消引用p
时,表达式的类型为int< /代码>。 因此,
p
是一个指向int的指针。const int *p;
:当用*
取消引用p
时,表达式的类型为const int
。 因此,p
是一个指向const int的指针。int *const p;
:p
是 const。 如果使用*
取消引用此常量表达式,则该表达式的类型为int
。 因此,p
是一个指向 int 的 const 指针。const int *const p;
:p
是 const。 如果使用*
取消引用此常量表达式,则该表达式的类型为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];
:没有什么可以阻止任何人拥有指向多维数组的指针,这是一种顺时针循环螺旋建议变得显而易见的情况废话。现实生活中有时会出现的一个东西是函数指针。 我们还需要括号,因为函数调用运算符(C++ 中的
operator()()
,C 中的简单语法规则)比取消引用的operator*()
具有更高的优先级,同样是因为函数返回指针比函数指针更常见:int *f();
:首先调用函数,因此f< /code> 是一个函数。 该调用必须取消引用才能产生 int,因此返回值是指向 int 的指针。 用法:
int i = *f();
。int (*fp)();
:括号更改运算符应用程序的顺序。 因为我们必须首先取消引用,所以我们知道fp
是一个指向某个东西的指针。 因为我们可以将函数调用运算符应用于 *fp,所以我们知道(在 C 语言中)fp
是一个指向函数的指针; 在 C++ 中,我们只知道它是为它定义的operator()()
。 由于该调用不带参数并返回 int,因此在 C++ 中,fp
是指向具有该签名的函数的指针。 (在 C 中,空参数列表表示对参数一无所知,但未来的 C 规范可能会禁止这种过时的使用。)int *(*fp)();
:当然,我们可以从指向的函数返回指向 int 的指针。int (*(*fp)())[3];
:首先取消引用,因此是一个指针; 接下来应用函数调用运算符,因此是指向函数的指针; 再次取消引用返回值,因此是指向返回指针的函数的指针; 将索引运算符应用于that:指向返回数组指针的函数的指针。 结果是一个 int,因此指向函数的指针返回指向 int 数组的指针。-所有括号都是必需的:正如所讨论的,我们必须在发生其他事情之前优先使用
(*fp)
取消对函数指针的引用。 显然,我们需要函数调用; 并且由于该函数返回一个指向数组的指针(而不是指向其第一个元素!),因此在对其进行索引之前我们也必须取消引用它。 我承认我写了一个测试程序来检查这一点,因为我不确定,即使使用这种万无一失的方法;-)。 这是:注意声明对表达式的模仿是多么漂亮!
Nobody has mentioned the system underlying declarations which Kernighan and Ritchie pointed out in their C book:
Declarations mimic expressions.
I'll repeat this because it so essential and gives a clear strategy to parse even the most complicated declarations:
Declarations mimic expressions.
The declarations contain the same operators as expressions the declared identifier can appear in later, with the same priority they have in expressions. This is why the "clockwise spiral rule" is wrong: The evaluation order is strictly determined by the operator precedences, with complete disregard for left, right or rotational directions.
Here are a few Examples, in order of increasing complexity:
int i;
: Wheni
is used as-is, it is an expression of typeint
. Therefore,i
is an int.int *p;
: Whenp
is dereferenced with*
, the expression is of typeint
. Therefore,p
is a pointer to int.const int *p;
: Whenp
is dereferenced with*
, the expression is of typeconst int
. Therefore,p
is a pointer to const int.int *const p;
:p
is const. If this constant expression is dereferenced with*
, the expression is of typeint
. Therefore,p
is a const pointer to int.const int *const p;
:p
is const. If this constant expression is dereferenced with*
, the expression is of typeconst int
. Therefore,p
is a const pointer to const int.So far we didn't have any issues with operator precedence yet: We simply evaluated right-to-left. This changes when we have fun with arrays of pointers and pointers to arrays. You may want to have a cheat sheet open.
int a[3];
: When we apply the array indexing operator toa
, the result is anint
. Therefore,a
is an array of int.int *a[3];
: Here the indexing operator has higher precedence, so we apply it first: When we apply the array indexing operator toa
, the result is anint *
. Therefore,a
is an array of pointers to int. This is not uncommon.int (*a)[3];
: Here the operator precedence is overridden by round parentheses, exactly as in any expression. Consequently, we dereference first. We know now thata
is a pointer to some type.*a
, the dereferenced pointer, is an expression of that type. When we apply the array indexing operator to*a
, we obtain a plain int, which means that*a
is an array of three ints, anda
is a pointer to that array. This is fairly uncommon outside of C++ templates, which is why the operator precedences are not catering to this case. Note how the use of such a pointer is the model for its declaration:int i = (*a)[1];
. The parentheses are mandatory to dereference first.int (*a)[3][2];
: There is nothing preventing anybody from having pointers to multi-dimensional arrays, one case where circular spiral clockwise advice becomes obvious nonsense.A thing that sometimes comes up in real life are function pointers. We need parentheses there as well because the function call operator (
operator()()
in C++, simple syntax rule in C) has higher priority than the dereferencingoperator*()
, again because it's more common to have functions returning pointers than pointers to functions:int *f();
: Function call first, sof
is a function. The call must be dereferenced to result in an int, so the return value is a pointer to int. Usage:int i = *f();
.int (*fp)();
: Parentheses change order of operator application. Because we must dereference first we know thatfp
is a pointer to something. Because we can apply the function call operator to*fp
we know (in C) thatfp
is a pointer to a function; in C++ we only know that it is something for whichoperator()()
is defined. Since the call takes no parameters and returns an int,fp
is in C++ a pointer to a function with that signature. (In C, an empty parameter list indicates that nothing is known about the parameters, but future C specifications may forbid that obsolete use.)int *(*fp)();
: Of course we can return pointers to int from a function pointed to.int (*(*fp)())[3];
: Dereference first, hence a pointer; apply function call operator next, hence a pointer to function; dereference the return value again, hence a pointer to a function returning a pointer; apply the indexing operator to that: pointer to function returning pointer to array. The result is an int, hence pointer to function returning pointer to array of ints.-All parentheses are necessary: As discussed, we must prioritize dereferencing of the function pointer with
(*fp)
before anything else happens. Obviously, we need the function call; and since the function returns a pointer to an array (not to its first element!), we must dereference that as well before we can index it. I admit that I wrote a test program to check this because I wasn't sure, even with this fool-proof method ;-). Here it is:Note how beautifully the declaration mimics the expression!
const int*
- 指向常量int
对象的指针。可以改变指针的值; 您无法更改指针所指向的
int
对象的值。const int * const
- 指向常量int
对象的常量指针。您不能更改指针的值,也不能更改指针指向的
int
对象的值。int const *
- 指向常量int
对象的指针。此语句相当于 1.
const int*
- 您可以更改指针的值,但不能更改指针所指向的int
对象的值。实际上,还有第四个选项:
int * const
- 指向int
对象的常量指针。您可以更改指针指向的对象的值,但不能更改指针本身的值。 指针将始终指向同一个
int
对象,但该int
对象的值可以更改。如果您想确定某种类型的 C 或 C++ 构造,可以使用 Clockwise/Spiral大卫·安德森 (David Anderson) 制定的规则; 但不要与 Ross J. Anderson 制定的安德森规则混淆,这是非常独特的。
const int*
- pointer to constantint
object.You can change the value of the pointer; you can not change the value of the
int
object, the pointer points to.const int * const
- constant pointer to constantint
object.You can not change the value of the pointer nor the value of the
int
object the pointer points to.int const *
- pointer to constantint
object.This statement is equivalent to 1.
const int*
- You can change the value of the pointer but you can not change the value of theint
object, the pointer points to.Actually, there is a 4th option:
int * const
- constant pointer toint
object.You can change the value of the object the pointer points to but you can not change the value of the pointer itself. The pointer will always point to the same
int
object but this value of thisint
object can be changed.If you want to determine a certain type of C or C++ construct you can use the Clockwise/Spiral Rule made by David Anderson; but not to confuse with Anderson`s Rule made by Ross J. Anderson, which is something quite distinct.
只是为了 C 的完整性遵循其他解释,不确定 C++。
x
Pointer
int *p;
代码>int const *p;
int * const p;
int const * const p;
指向指针
int **pp;
int ** const pp;
int * const *pp;
int const **pp;
int * const * const pp;
int const ** const pp;
int const * const * const pp;< /code>
N 级取消引用
继续前进,但愿人类将你逐出教会。
Just for the sake of completeness for C following the others explanations, not sure for C++.
x
Pointer
int *p;
int const *p;
int * const p;
int const * const p;
Pointer to pointer
int **pp;
int ** const pp;
int * const *pp;
int const **pp;
int * const * const pp;
int const ** const pp;
int const * const *pp;
int const * const * const pp;
N-levels of Dereference
Just keep going, but may the humanity excommunicate you.
这主要涉及第二行:最佳实践、分配、函数参数等。
一般实践。 尽量将所有内容都设为 const。 或者换句话说,从一开始就将所有内容设置为 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
,这意味着什么传入的内容将会返回。 正如其他人指出的那样,这对于廉价地传递您不想更改的大型对象非常有用。 传递引用比按值传递大对象要便宜得多。通过指针传递:例如
void func(int *param)
和void func(int const *param)
这两个与它们的引用对应物几乎是同义词,但需要注意的是被调用函数现在需要检查nullptr
,除非其他合同保证func
它永远不会在param< 中收到
nullptr
/代码>。关于该主题的意见。 在这样的情况下证明正确性非常困难,而且很容易犯错误。 因此,不要冒险,并始终检查指针参数是否为 nullptr。 从长远来看,您将避免痛苦和痛苦,并且很难发现错误。 至于检查的成本,它非常便宜,并且在编译器内置的静态分析可以管理它的情况下,优化器无论如何都会忽略它。 打开 MSVC 的链接时间代码生成,或 GCC 的 WOPR(我认为),您将在程序范围内获得它,即即使在跨越源代码模块边界的函数调用中。
归根结底,以上所有内容都为我们总是更喜欢引用而不是指针提供了一个非常可靠的理由。 他们只是更安全。
This mostly addresses the second line: best practices, assignments, function parameters etc.
General practice. Try to make everything
const
that you can. Or to put that another way, make everythingconst
to begin with, and then remove exactly the minimum set ofconst
s necessary to allow the program to function. This will be a big help in attaining const-correctness, and will help ensure that subtle bugs don't get introduced when people try and assign into things they're not supposed to modify.Avoid const_cast<> like the plague. There are one or two legitimate use cases for it, but they are very few and far between. If you're trying to change a
const
object, you'll do a lot better to find whoever declared itconst
in the first pace and talk the matter over with them to reach a consensus as to what should happen.Which leads very neatly into assignments. You can assign into something only if it is non-const. If you want to assign into something that is const, see above. Remember that in the declarations
int const *foo;
andint * const bar;
different things areconst
- other answers here have covered that issue admirably, so I won't go into it.Function parameters:
Pass by value: e.g.
void func(int param)
you don't care one way or the other at the calling site. The argument can be made that there are use cases for declaring the function asvoid func(int const param)
but that has no effect on the caller, only on the function itself, in that whatever value is passed cannot be changed by the function during the call.Pass by reference: e.g.
void func(int ¶m)
Now it does make a difference. As just declaredfunc
is allowed to changeparam
, and any calling site should be ready to deal with the consequences. Changing the declaration tovoid func(int const ¶m)
changes the contract, and guarantees thatfunc
can now not changeparam
, meaning what is passed in is what will come back out. As other have noted this is very useful for cheaply passing a large object that you don't want to change. Passing a reference is a lot cheaper than passing a large object by value.Pass by pointer: e.g.
void func(int *param)
andvoid func(int const *param)
These two are pretty much synonymous with their reference counterparts, with the caveat that the called function now needs to check fornullptr
unless some other contractual guarantee assuresfunc
that it will never receive anullptr
inparam
.Opinion piece on that topic. Proving correctness in a case like this is hellishly difficult, it's just too damn easy to make a mistake. So don't take chances, and always check pointer parameters for
nullptr
. You will save yourself pain and suffering and hard to find bugs in the long term. And as for the cost of the check, it's dirt cheap, and in cases where the static analysis built into the compiler can manage it, the optimizer will elide it anyway. Turn on Link Time Code Generation for MSVC, or WOPR (I think) for GCC, and you'll get it program wide, i.e. even in function calls that cross a source code module boundary.At the end of the day all of the above makes a very solid case to always prefer references to pointers. They're just safer all round.
我在下面画了一张图来解释这一点,也许有帮助。
int const v
和const int v
是相同的。I drew an image below to explain this, maybe helpful.
int const v
andconst int v
are identical.const
位于*
的左侧,则它引用该值(无论是否为const int 或
int const
)const
位于*
的右侧,则它引用指针重要的一点:
const int *p
并不意味着您引用的值是常量!!。 这意味着您无法通过该指针更改它(这意味着您无法分配 $*p = ...`)。 该值本身可以通过其他方式改变。 例如,这主要用于函数签名,以保证函数不会意外更改传递的参数。
const
is to the left of*
, it refers to the value (it doesn't matter whether it'sconst int
orint const
)const
is to the right of*
, it refers to the pointer itselfAn important point:
const int *p
does not mean the value you are referring to is constant!!. It means that you can't change it through that pointer (meaning, you can't assign $*p = ...`). The value itself may be changed in other ways. EgThis is meant to be used mostly in function signatures, to guarantee that the function can't accidentally change the arguments passed.
两边带有 int 的 const 将成为指向常量 int 的指针:
或者:
*
之后的const
将成为指向 int 的常量指针:在这种情况下,所有这些都是指向常整数的指针,但这些都不是常量指针:
在这种情况下,所有这些都是指向常整数的指针和ptr2是指向常量整数的常量指针。 但 ptr1 不是常量指针:
The const with the int on either sides will make pointer to constant int:
or:
const
after*
will make constant pointer to int:In this case all of these are pointer to constant integer, but none of these are constant pointer:
In this case all are pointer to constant integer and ptr2 is constant pointer to constant integer. But ptr1 is not constant pointer:
C++ 中关于 const 正确性还有许多其他微妙之处。 我想这里的问题只是关于 C,但我会给出一些相关的例子,因为标签是 C++ :
你经常传递像字符串这样的大参数作为
TYPE const &
这会阻止对象被修改或复制。 示例:类型和类型 TYPE::operator=(const TYPE &rhs) { ... return *this; }
但是
类型& const
毫无意义,因为引用始终是 const。您应该始终将不修改类的类方法标记为
const
,否则您无法从TYPE const &
引用调用该方法。 示例:bool TYPE::operator==(const TYPE &rhs) const { ... }
在常见情况下,返回值和方法都应该是 const。 示例:
const TYPE TYPE::operator+(const TYPE &rhs) const { ... }
事实上,const 方法不得返回内部类数据作为对非 const 的引用。
因此,人们经常必须使用 const 重载来创建 const 和非 const 方法。 例如,如果您定义 T const& operator[] (unsigned i) const;,那么您可能还需要以下给出的非常量版本:
内联 T& 运算符[](无符号i){((*this)[](i)
返回 const_cast
static_cast
);
}
说一句,C 中没有 const 函数,C++ 中非成员函数本身不能是 const,const 方法可能有副作用,并且编译器不能使用 const 函数来避免重复的函数调用。 事实上,即使是一个简单的
int const &
引用也可能会见证它所引用的值在其他地方发生更改。There are many other subtle points surrounding const correctness in C++. I suppose the question here has simply been about C, but I'll give some related examples since the tag is C++ :
You often pass large arguments like strings as
TYPE const &
which prevents the object from being either modified or copied. Example :TYPE& TYPE::operator=(const TYPE &rhs) { ... return *this; }
But
TYPE & const
is meaningless because references are always const.You should always label class methods that do not modify the class as
const
, otherwise you cannot call the method from aTYPE const &
reference. Example :bool TYPE::operator==(const TYPE &rhs) const { ... }
There are common situations where both the return value and the method should be const. Example :
const TYPE TYPE::operator+(const TYPE &rhs) const { ... }
In fact, const methods must not return internal class data as a reference-to-non-const.
As a result, one must often create both a const and a non-const method using const overloading. For example, if you define
T const& operator[] (unsigned i) const;
, then you'll probably also want the non-const version given by :inline T& operator[] (unsigned i) {
return const_cast<char&>(
static_cast<const TYPE&>(*this)[](i)
);
}
Afaik, there are no const functions in C, non-member functions cannot themselves be const in C++, const methods might have side effects, and the compiler cannot use const functions to avoid duplicate function calls. In fact, even a simple
int const &
reference might witness the value to which it refers be changed elsewhere.简单地记住:
如果 const 位于 * 之前,则 value 是常量。
如果 const 在 * 之后,则地址是常量。
如果 const 在 * 之前和之后都可用,则值和地址都是常量。
例如
int * const var; //这里地址是不变的。
int const * var; //这里的值是常数。
int const * const var; // 值和地址都是常数。
To remember in easy way :
If const is before * then value is constant.
If const is after * then address is constant.
if const are available both before and after * then both value and address are constant.
e.g.
int * const var; //here address is constant.
int const * var; //here value is constant.
int const * const var; // both value and address are constant.
C 和 C++ 声明语法被原始设计者多次描述为失败的实验。
相反,让我们将类型命名为“指向
Type
的指针”; 我将其称为Ptr_
:现在
Ptr_
是一个指向char
的指针。Ptr_
是指向const char
的指针。而
const Ptr_
是一个指向const char
的const
指针。The C and C++ declaration syntax has repeatedly been described as a failed experiment, by the original designers.
Instead, let's name the type “pointer to
Type
”; I’ll call itPtr_
:Now
Ptr_<char>
is a pointer tochar
.Ptr_<const char>
is a pointer toconst char
.And
const Ptr_<const char>
is aconst
pointer toconst char
.我和你有同样的疑问,直到我遇到这本书 由 C++ 大师 Scott Meyers 撰写。 请参阅本书中的第三项,其中详细讨论了如何使用
const
。只要遵循这个建议
const
出现在星号的左侧,则指向的是常量const
出现在星号的右侧,则指针本身是常量const
,则都是常量I had the same doubt as you until I came across this book by the C++ Guru Scott Meyers. Refer the third Item in this book where he talks in details about using
const
.Just follow this advice
const
appears to the left of the asterisk, what's pointed to is constantconst
appears to the right of the asterisk, the pointer itself is constantconst
appears on both sides, both are constant对我来说,
const
的位置,即它相对于*
是否出现在左侧或右侧,或者同时出现在左侧和右侧,可以帮助我弄清楚实际含义。*
左边的const
表示指针指向的对象是一个const
对象。*
右侧的const
表示该指针是const
指针。下表摘自斯坦福 CS106L 标准 C++ 编程实验室课程读本。
For me, the position of
const
i.e. whether it appears to the LEFT or RIGHT or on both LEFT and RIGHT relative to the*
helps me figure out the actual meaning.A
const
to the LEFT of*
indicates that the object pointed by the pointer is aconst
object.A
const
to the RIGHT of*
indicates that the pointer is aconst
pointer.The following table is taken from Stanford CS106L Standard C++ Programming Laboratory Course Reader.
这很简单但很棘手。 请注意,我们可以将
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
是只读的,所以声明和定义应该在同一位置。It's simple but tricky. Please note that we can apply the
const
qualifier to any data type (int
,char
,float
, etc.).Let's see the below examples.
const int *p
==>*p
is read-only [p
is a pointer to a constant integer]int const *p
==>*p
is read-only [p
is a pointer to a constant integer]int *p const
==> Wrong Statement. Compiler throws a syntax error.int *const p
==>p
is read-only [p
is a constant pointer to an integer].As pointer
p
here is read-only, the declaration and definition should be in same place.const int *p const
==> Wrong Statement. Compiler throws a syntax error.const int const *p
==>*p
is read-onlyconst int *const p
==>*p
andp
are read-only [p
is a constant pointer to a constant integer]. As pointerp
here is read-only, the declaration and definition should be in same place.int const *p const
==> Wrong Statement. Compiler throws a syntax error.int const int *p
==> Wrong Statement. Compiler throws a syntax error.int const const *p
==>*p
is read-only and is equivalent toint const *p
int const *const p
==>*p
andp
are read-only [p
is a constant pointer to a constant integer]. As pointerp
here is read-only, the declaration and definition should be in same place.const 的简单使用。
最简单的用途是声明一个命名常量。 为此,可以像声明变量一样声明一个常量,但在其前面添加
const
。 人们必须立即在构造函数中初始化它,因为当然,我们不能稍后设置该值,因为这会改变它。 例如:将创建一个整型常量,毫无想象力地称为Constant1,其值为96。
此类常量对于程序中使用但在程序编译后不需要更改的参数很有用。 对于程序员来说,它比 C 预处理器
#define
命令有一个优势,因为它可以被理解和定义。 由编译器本身使用,而不仅仅是在到达主编译器之前由预处理器替换到程序文本中,因此错误消息更有帮助。它也适用于指针,但必须小心 const 以确定指针或其指向的内容是常量还是两者。 例如:
声明
Constant2
是指向常量整数的变量指针,并且:是具有相同功能的替代语法,而
声明
Constant3
是指向变量整数的常量指针并且声明
Constant4
是指向常量整数的常量指针。 基本上,“const”适用于其紧邻左侧的任何内容(除非那里没有任何内容,在这种情况下,它适用于其紧邻右侧的任何内容)。参考:http://duramecho.com/ComputerInformation/WhyHowCppConst.html
Simple Use of
const
.The simplest use is to declare a named constant. To do this, one declares a constant as if it was a variable but add
const
before it. One has to initialize it immediately in the constructor because, of course, one cannot set the value later as that would be altering it. For example:will create an integer constant, unimaginatively called
Constant1
, with the value 96.Such constants are useful for parameters which are used in the program but are do not need to be changed after the program is compiled. It has an advantage for programmers over the C preprocessor
#define
command in that it is understood & used by the compiler itself, not just substituted into the program text by the preprocessor before reaching the main compiler, so error messages are much more helpful.It also works with pointers but one has to be careful where
const
to determine whether the pointer or what it points to is constant or both. For example:declares that
Constant2
is variable pointer to a constant integer and:is an alternative syntax which does the same, whereas
declares that
Constant3
is constant pointer to a variable integer anddeclares that
Constant4
is constant pointer to a constant integer. Basically ‘const’ applies to whatever is on its immediate left (other than if there is nothing there in which case it applies to whatever is its immediate right).ref: http://duramecho.com/ComputerInformation/WhyHowCppConst.html
一般规则是
const
关键字适用于紧邻其前面的内容。 例外,起始const
适用于后面的内容。const int*
与int const*
相同,表示“指向常量 int 的指针”。const int* const
与int const* const
相同,表示“指向常量 int 的常量指针”。编辑:
对于注意事项,如果这个答案< /a> 还不够,你能更准确地说明你想要什么吗?
The general rule is that the
const
keyword applies to what precedes it immediately. Exception, a startingconst
applies to what follows.const int*
is the same asint const*
and means "pointer to constant int".const int* const
is the same asint const* const
and means "constant pointer to constant int".Edit:
For the Dos and Don'ts, if this answer isn't enough, could you be more precise about what you want?
这个问题准确地显示了为什么我喜欢按照我在问题中提到的方式做事类型 id 之后的 const 可以接受吗?
简而言之,我发现记住该规则的最简单方法是“const”位于它所应用的内容之后到。 因此,在您的问题中,“int const *”意味着 int 是常量,而“int * const”意味着指针是常量。
如果有人决定把它放在最前面(例如:“const int *”),作为一种特殊的例外,在这种情况下它适用于它后面的东西。
许多人喜欢使用这个特殊的例外,因为他们认为它看起来更好。 我不喜欢它,因为它是一个例外,因此使事情变得混乱。
This question shows precisely why I like to do things the way I mentioned in my question is const after type id acceptable?
In short, I find the easiest way to remember the rule is that the "const" goes after the thing it applies to. So in your question, "int const *" means that the int is constant, while "int * const" would mean that the pointer is constant.
If someone decides to put it at the very front (eg: "const int *"), as a special exception in that case it applies to the thing after it.
Many people like to use that special exception because they think it looks nicer. I dislike it, because it is an exception, and thus confuses things.
恒定参考:
对变量(此处为 int)的引用,该变量是常量。 我们主要将变量作为引用传递,因为引用的大小比实际值小,但有一个副作用,那就是它就像实际变量的别名。 我们可能会通过完全访问别名来意外更改主变量,因此我们将其设为常量以防止这种副作用。
常量指针
一旦常量指针指向一个变量,它就不能指向任何其他变量。
指向常量的指针
无法更改其所指向变量值的指针称为常量指针。
指向常量的常量指针
指向常量的常量指针是一个既不能更改其指向的地址,也不能更改保存在该地址的值的指针。
Constant reference:
A reference to a variable (here int), which is constant. We pass the variable as a reference mainly, because references are smaller in size than the actual value, but there is a side effect and that is because it is like an alias to the actual variable. We may accidentally change the main variable through our full access to the alias, so we make it constant to prevent this side effect.
Constant pointers
Once a constant pointer points to a variable then it cannot point to any other variable.
Pointer to constant
A pointer through which one cannot change the value of a variable it points is known as a pointer to constant.
Constant pointer to a constant
A constant pointer to a constant is a pointer that can neither change the address it's pointing to and nor can it change the value kept at that address.
就像几乎每个人都指出的那样:
之间有什么区别>const X* p
、X* const p
和const X* const p
?Like pretty much everyone pointed out:
What’s the difference between
const X* p
,X* const p
andconst X* const p
?我认为这里已经回答了所有问题,但我只想补充一点,您应该小心
typedef
! 它们不仅仅是文本替换。例如:
astring
的类型是char * const
,而不是const char *
。 这是我总是倾向于将 const 放在类型右侧而不是放在开头的原因之一。I think everything is answered here already, but I just want to add that you should beware of
typedef
s! They're NOT just text replacements.For example:
The type of
astring
ischar * const
, notconst char *
. This is one reason I always tend to putconst
to the right of the type, and never at the start.对于那些不了解顺时针/螺旋规则的人:
从变量名称开始,顺时针移动(在本例中为向后移动)到下一个指针或类型。 重复直到表达式结束。
这是一个演示:
For those who don't know about Clockwise/Spiral Rule:
Start from the name of the variable, move clockwisely (in this case, move backward) to the next pointer or type. Repeat until expression ends.
Here is a demo:
向后阅读(由顺时针/螺旋规则驱动):
int *
- 指向 int 的指针int const *
- 指向 const int 的指针int * const
- 指向 int 的 const 指针int const * const
- const 指向 const int 的指针现在第一个
const
可以位于类型的任一侧,因此:const int *
==int const *
int const * const
如果你想变得非常疯狂,你可以这样做:
int **
- 指向的指针指向 int 的指针int ** const
- 指向 int 指针的 const 指针int * const *
- 指向指向 int 的 const 指针int const **
- 指向 const int 的指针int * const * const
- 指向 int 的 const 指针的 const 指针如果您不确定,您可以可以使用类似 cdecl+ 自动将声明转换为散文。
为了确保我们清楚const的含义:
foo
是一个指向常量整数的变量指针。 这使您可以更改所指向的内容,但不能更改所指向的值。 最常见的是 C 风格的字符串,其中有一个指向const char
的指针。 您可以更改指向的字符串,但无法更改这些字符串的内容。 当字符串本身位于程序的数据段中且不应更改时,这一点很重要。bar
是一个常量或固定指针,指向可以更改的值。 这就像没有额外语法糖的参考。 因此,通常您会在使用T* const
指针的地方使用引用,除非您需要允许NULL
指针。Read it backwards (as driven by Clockwise/Spiral Rule):
int*
- pointer to intint const *
- pointer to const intint * const
- const pointer to intint const * const
- const pointer to const intNow the first
const
can be on either side of the type so:const int *
==int const *
const int * const
==int const * const
If you want to go really crazy you can do things like this:
int **
- pointer to pointer to intint ** const
- a const pointer to a pointer to an intint * const *
- a pointer to a const pointer to an intint const **
- a pointer to a pointer to a const intint * const * const
- a const pointer to a const pointer to an intIf you're ever uncertain, you can use a tool like cdecl+ to convert declarations to prose automatically.
To make sure we are clear on the meaning of
const
:foo
is a variable pointer to a constant integer. This lets you change what you point to but not the value that you point to. Most often this is seen with C-style strings where you have a pointer to aconst char
. You may change which string you point to but you can't change the content of these strings. This is important when the string itself is in the data segment of a program and shouldn't be changed.bar
is a constant or fixed pointer to a value that can be changed. This is like a reference without the extra syntactic sugar. Because of this fact, usually you would use a reference where you would use aT* const
pointer unless you need to allowNULL
pointers.