我总是搞砸如何正确使用const int*
,const int * const
和int const *
。是否有一套规则来定义你能做什么和不能做什么?
我想知道在分配、传递给函数等方面的所有注意事项。
向后阅读(由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 的指针,指向 intint const **
- 指向指向 const int 的指针int * const * const
- 一个指向 int 的 const 指针的 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
指针。
对于那些不了解顺时针/螺旋规则的人:从变量的名称开始,顺时针移动(在这种情况下,向后移动)到下一个指针或类型。重复直到表达式结束。
这是一个演示:
我认为这里已经回答了所有问题,但我只想补充一点,您应该提防typedef
s!它们不仅仅是文本替换。
例如:
typedef char *ASTRING;
const ASTRING astring;
的类型astring
是char * const
,不是const char *
。这是我总是倾向于将const
类型放在右侧的原因之一,而不是放在开头。
就像几乎每个人都指出的那样:
const X* p
和X* const p
和有什么区别const X* const p
?
您必须从右到左阅读指针声明。
const X* p
意思是“p 指向一个 const 的 X”:X 对象不能通过 p 改变。
X* const p
表示“p 是指向非 const 的 X 的 const 指针”:您不能更改指针 p 本身,但可以通过 p 更改 X 对象。
const X* const p
意思是“p 是一个指向 const 的 X 的 const 指针”:你不能改变指针 p 本身,也不能通过 p 改变 X 对象。
常量参考:
对变量(此处为 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
关键字适用于它之前的内容。例外,开始const
适用于以下内容。
const int*
与“指向常量 int的指针”相同int const*
并且表示“指向常量 int”的意思。const int* const
与“指向常量 int 的常量指针”相同int const* const
,意思是“常量指针” 。编辑: 对于注意事项,如果这个答案还不够,你能更准确地说明你想要什么吗?
这个问题准确地说明了为什么我喜欢按照我在问题中提到的方式做事,即类型 id 可接受后的 const ?
简而言之,我发现记住规则的最简单方法是“const”跟随它适用的事物。所以在你的问题中,“int const *”意味着int是常数,而“int * const”意味着指针是常数。
如果有人决定把它放在最前面(例如:“const int *”),作为这种情况下的一个特殊例外,它适用于它后面的东西。
许多人喜欢使用这个特殊的例外,因为他们认为它看起来更好。我不喜欢它,因为它是一个例外,从而使事情变得混乱。
的简单使用const
。
最简单的用法是声明一个命名常量。为此,需要声明一个常量,就好像它是一个变量一样,但要const
在它之前添加。必须立即在构造函数中初始化它,因为当然,以后不能设置值,因为那样会改变它。例如:
const int Constant1=96;
将创建一个整数常量,难以想象地称为Constant1
,其值为 96。
这些常量对于程序中使用但在程序编译后不需要更改的参数很有用。对于程序员来说,它比 C 预处理器#define
命令的优势在于它可以被编译器本身理解和使用,而不仅仅是在到达主编译器之前由预处理器替换到程序文本中,因此错误消息更有帮助。
它也适用于指针,但必须小心在哪里const
确定指针或其指向的内容是常量还是两者兼而有之。例如:
const int * Constant2
声明它Constant2
是指向常量整数的变量指针,并且:
int const * Constant2
是一种替代语法,它做同样的事情,而
int * const Constant3
声明它Constant3
是指向变量整数的常量指针,并且
int const * const Constant4
声明它Constant4
是指向一个常量整数的常量指针。基本上,“const”适用于其最左边的任何东西(除非那里没有任何东西,在这种情况下,它适用于它最右边的任何东西)。
参考:http ://duramecho.com/ComputerInformation/WhyHowCppConst.html
这很简单但很棘手。请注意,我们可以将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
出现在两边,两者都是常数C 和 C++ 声明语法一再被原始设计者描述为失败的实验。
相反,让我们将类型命名为“pointer to Type
”;我会这样称呼它Ptr_
:
template< class Type >
using Ptr_ = Type*;
现在Ptr_<char>
是指向char
.
Ptr_<const char>
是指向 的指针const char
。
并且const Ptr_<const char>
是const
指向const char
.
在 C++ 中,围绕 const 正确性还有许多其他细微之处。我想这里的问题只是关于 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 和非 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 &
引用也可能见证它所引用的值在其他地方发生了变化。
如果 const 在 * 之前,则 value 是常量。
如果 const 在 * 之后,则地址是常量。
如果 const 在 * 之前和之后都可用,则 value 和 address 都是常量。
例如
int * 常量变量;//这里的地址是不变的。
int 常量 * 变量;//这里的值是常数。
int 常量 * 常量变量;// 值和地址都是常量。
两边都有 int 的 const 将指向常量 int:
const int *ptr=&i;
或者:
int const *ptr=&i;
const
after*
将创建指向 int 的常量指针:
int *const ptr=&i;
在这种情况下,所有这些都是指向常量 integer的指针,但这些都不是常量指针:
const int *ptr1=&i, *ptr2=&j;
在这种情况下,所有都是指向常量整数的指针,而 ptr2 是指向常量整数的常量指针。但是 ptr1 不是常量指针:
int const *ptr1=&i, *const ptr2=&j;
const
在的左边,*
它指的是值(不管是 还是 都没有const int
关系int const
)const
在的右边,*
它指的是指针本身重要的一点:const int *p
并不意味着您所指的值是恒定的!. 这意味着您无法通过该指针更改它(意味着您无法分配 $*p = ...`)。值本身可能会以其他方式更改。例如
int x = 5;
const int *p = &x;
x = 6; //legal
printf("%d", *p) // prints 6
*p = 7; //error
这主要用于函数签名,以保证函数不会意外更改传递的参数。
这主要涉及第二行:最佳实践、分配、函数参数等。
一般做法。尝试做const
你能做的一切。或者换一种说法,让一切const
从头开始,然后准确地删除const
允许程序运行所需的最小 s 集。这将大大有助于实现 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
它永远不会收到nullptr
in param
。
关于该主题的意见。在这种情况下证明正确性非常困难,犯错太容易了。所以不要冒险,总是检查指针参数nullptr
。从长远来看,您将避免痛苦和痛苦,并且很难找到错误。至于检查的成本,它非常便宜,而且在编译器内置的静态分析可以管理它的情况下,优化器无论如何都会忽略它。打开 MSVC 的链接时间代码生成,或 GCC 的 WOPR(我认为),你会得到它的程序范围,即即使在跨越源代码模块边界的函数调用中。
归根结底,以上所有内容都提供了一个非常可靠的案例,即始终更喜欢对指针的引用。他们只是更安全。
只是为了在其他解释之后 C 的完整性,对于 C++ 不确定。
x
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 *pp;
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);
继续前进,但愿人类将你逐出教会。
int x = 10;
int *p = &x;
int **pp = &p;
int ***ppp = &pp;
int ****pppp = &ppp;
printf("%d \n", ****pppp);
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. 安德森提出的安德森规则相混淆,这是非常不同的。
simple mnemonic:
type
pointer <- *
-> pointee name
I like to think of int *i
as declaring "the dereference of i
is int
"; in this sense, const int *i
means "the deref of i
is const int
", while int *const i
means "deref of const i
is int
".
(the one danger of thinking like this is it may lead to favoring int const *i
style of declaration, which people might hate/disallow)
很多人回答正确我会在这里组织好并添加一些在给定答案中缺少的额外信息。
const 是 C 语言中的关键字,也称为限定符。const 可以应用于任何变量的声明,以指定它的值不会改变
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. const int *
在 3 个地方安排 3 件事情的方法 3!=6
一世。* 在开始时
*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 *
在 2 个相似的地方安排 4 个东西的方法 4!/2!=12
一世。* 在开始时
* 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 是指向 const int (5) 的指针
const int *p
int const *p
int const const *p
const const int *p
const int const *p
b型。p 是 const ptr 到 int (2)
int * const p
int * const const p;
c型。p 是 const 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 常量 * 常量 p,p2 ;
here p is const ptr to const int (type c.)
but p2 is just const int.
完成的