我总是搞砸如何正确使用 const int*
、const int * const
和 int const *
。是否有一套规则来定义你能做什么和不能做什么?
我想知道在分配、传递给函数等方面的所有注意事项。
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
。在像 Pascal 这样易于阅读的语言中,等效的单行声明会是什么?
function(x:^char):^int
。函数类型意味着指向函数的指针,因此无需指定它,并且 Pascal 不强制 const 正确性。它可以从左到右阅读。
向后阅读(由 Clockwise/Spiral Rule 驱动):
int* - 指向 int 的指针
int const * - 指向 const int 的指针
int * const - 指向 int 的 const 指针
int const * const - 指向 const int 的 const 指针
现在第一个 const
可以在类型的任一侧,所以:
常量 int * == int 常量 *
const int * const == int const * const
如果您想真正发疯,可以执行以下操作:
int ** - 指向 int 的指针
int ** const - 一个指向 int 指针的 const 指针
int * const * - 指向 const 的指针 指向 int 的指针
int const ** - 指向 const int 的指针
int * const * const - 一个 const 指针,一个 const 指针,一个 int 指针
...
并确保我们清楚 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
指针。
对于那些不知道顺时针/螺旋规则的人:从变量的名称开始,顺时针移动(在这种情况下,向后移动)到下一个指针或类型。重复直到表达式结束。
这是一个演示:
https://i.stack.imgur.com/sT6ng.png
https://i.stack.imgur.com/Zt0G2.png
https://i.stack.imgur.com/kXH8P.png
https://i.stack.imgur.com/UeqZO.png
https://i.stack.imgur.com/f5ftV.png
void (*signal(int, void (*fp)(int)))(int);
我认为这里已经回答了所有问题,但我只想补充一点,您应该小心typedef
!它们不仅仅是文本替换。
例如:
typedef char *ASTRING;
const ASTRING astring;
astring
的类型是 char * const
,而不是 const char *
。这是我总是倾向于将 const
放在类型右侧的原因之一,而不是放在开头。
typedef int* PINT
这样的好处(我认为它来自 C 中的实践,并且许多开发人员一直在这样做)。太好了,我用 P
替换了那个 *
,它不会加快打字速度,而且还引入了您提到的问题。
PINT
确实是 typedef 的一个相当愚蠢的用法,特别是因为它让我认为系统存储使用啤酒作为内存。不过 typedef 对于处理指向函数的指针非常有用。
PVOID
、LPTSTR
的东西!
就像几乎每个人都指出的那样:
What’s the difference between const X* p
, X* const p
and 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 对象。
const X* p;
== X const * p;
和 "p points to an X that is const": the X object can't be changed via p.
常量引用:对变量(此处为 int)的引用,它是常量。我们主要将变量作为引用传递,因为引用的大小比实际值小,但有一个副作用,那就是因为它就像实际变量的别名。我们可能会通过对别名的完全访问而意外更改主变量,因此我们将其设为常量以防止这种副作用。 int var0 = 0;常量 int &ptr1 = var0; ptr1 = 8; // 错误 var0 = 6; // OK 常量指针 一旦常量指针指向一个变量,它就不能指向任何其他变量。 int var1 = 1; int var2 = 0; int *const ptr2 = &var1; ptr2 = &var2; // 指向常量的错误指针 不能通过它改变它所指向的变量的值的指针称为指向常量的指针。 int const * ptr3 = &var2; *ptr3 = 4; // 错误常量 指向常量的指针 指向常量的常量指针是一个既不能改变它所指向的地址也不能改变保存在该地址的值的指针。 int var3 = 0; int var4 = 0;常量 int * 常量 ptr4 = &var3; *ptr4 = 1; // 错误 ptr4 = &var4; // 错误
一般规则是 const
关键字适用于紧接在其前面的内容。例外,起始 const
适用于以下内容。
const int* 与 int const* 相同,意思是“指向常量 int 的指针”。
const int* const 与 int const* const 相同,意思是“指向常量 int 的常量指针”。
编辑:对于注意事项,如果 this answer 还不够,您能否更准确地说明您想要什么?
这个问题准确地说明了为什么我喜欢按照我在问题 is const after type id acceptable? 中提到的方式做事
简而言之,我发现记住规则的最简单方法是“const”跟随它适用的事物。所以在你的问题中,“int const *”意味着int是常数,而“int * const”意味着指针是常数。
如果有人决定把它放在最前面(例如:“const int *”),作为这种情况下的一个特殊例外,它适用于它后面的东西。
许多人喜欢使用这个特殊的例外,因为他们认为它看起来更好。我不喜欢它,因为它是一个例外,从而使事情变得混乱。
const T*
并且它变得更加自然。无论如何,您多久使用一次 T* const
,通常引用就可以了。当我想要一个 boost::shared_ptr<const T>
时,我被这一切所困扰,而是写了 const boost::shared_ptr<T>
。在略有不同的上下文中的相同问题。
const
左侧的所有内容都是 const 的类型,而其右侧的所有内容实际上都是 const。以 int const * const * p;
为例。不,我通常不会那样写,这只是一个例子。第一个const
:类型int,而int即const是const指针的内容,即p
的内容。第二个 const:type 是指向 const
int 的指针,const oblect 是 p
的内容
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 的这个 book 之前,我和你有同样的疑问。请参阅本书的第三项,其中他详细介绍了使用 const
。
只需遵循此建议
如果 const 字出现在星号的左边,则指向的是常量 如果 const 字出现在星号的右边,则指针本身是常量 如果 const 出现在两边,则两者都是常量
以简单的方式记住:
如果 const 在 * 之前,则 value 是常量。
如果 const 在 * 之后,则地址是常量。
如果 const 在 * 之前和之后都可用,则 value 和 address 都是常量。
例如
int * 常量变量; //这里的地址是不变的。 int 常量 * 变量; //这里的值是常数。 int 常量 * 常量变量; // 值和地址都是常量。
C 和 C++ 声明语法一再被原始设计者描述为失败的实验。
相反,让我们命名类型“指向Type
的指针”;我称之为Ptr_
:
template< class Type >
using Ptr_ = Type*;
现在 Ptr_<char>
是指向 char
的指针。
Ptr_<const char>
是指向 const char
的指针。
const Ptr_<const char>
是指向 const char
的 const
指针。
对我来说,const
的位置,即它相对于 *
出现在 LEFT 或 RIGHT 或同时出现在 LEFT 和 RIGHT 上,这有助于我弄清楚实际含义。
* LEFT 的 const 表示指针指向的对象是 const 对象。 * 右侧的 const 表示指针是 const 指针。
下表摘自斯坦福 CS106L 标准 C++ 编程实验室课程阅读器。
https://i.stack.imgur.com/aijhB.png
在 C++ 中,围绕 const 正确性还有许多其他细微之处。我想这里的问题只是关于 C,但我会给出一些相关的例子,因为标签是 C++:
您经常将像字符串这样的大参数作为 TYPE const & 传递,这可以防止对象被修改或复制。示例: TYPE& TYPE::operator=(const TYPE &rhs) { ... return *this;但是 TYPE & 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;,那么您可能还需要由以下给出的非 const 版本:inline T& operator[] (unsigned i) { return const_cast
Afaik,C 中没有 const 函数,非成员函数本身在 C++ 中不能是 const,const 方法可能有副作用,编译器不能使用 const 函数来避免重复的函数调用。事实上,即使是一个简单的 int const &
引用也可能见证它所引用的值在其他地方发生了变化。
两边都有 int 的 const 将指向常量 int:
const int *ptr=&i;
或者:
int const *ptr=&i;
*
之后的 const
将使 指向 int 的常量指针:
int *const ptr=&i;
在这种情况下,所有这些都是指向常量整数的指针,但这些都不是常量指针:
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
集。这将大大有助于实现 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(我认为),你会得到它的程序范围,即即使在跨越源代码模块边界的函数调用中。
归根结底,以上所有内容都提供了一个非常可靠的案例,即始终更喜欢对指针的引用。他们只是更安全。
只是为了在其他解释之后 C 的完整性,对于 C++ 不确定。
pp - 指向指针的指针
- 指针
数据 - 在示例 x 中指出的东西
粗体 - 只读变量
指针
数据 - 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);
级取消引用
继续前进,但愿人类将你逐出教会。
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 制作的 Clockwise/Spiral Rule;但不要与 Ross J. Anderson 的 Anderson`s Rule 混淆,这是非常不同的。
简单的助记符:
type
指针 <- *
->指针name
我喜欢将 int *i
视为声明“i
的取消引用是 int
”;在这个意义上,const int *i
表示“i
的 deref 是 const int
”,而 int *const i
表示“const i
的 deref 是 int
”。
(这样思考的一个危险是它可能会导致偏爱 int const *i
的声明风格,人们可能会讨厌/不允许)
我在下面画了一张图片来解释这一点,也许会有所帮助。
int const v
和 const int v
是相同的。
https://i.stack.imgur.com/JwWMu.jpg
很多人回答正确我会在这里组织好并添加一些在给定答案中缺少的额外信息。
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.
如何阅读 ?
只需从右到左阅读每个语句都可以顺利运行
主要内容
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.
完成的
不定期副业成功案例分享
const int x = 0; const int *const px = &x; const int *const *const p = &px;
工作得很好。