我总是搞砸如何正确使用 const int* , const int * const 和 int const * . 是否有一套规则定义了您能做什么和不能做什么?
const int*
const int * 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
对于那些不了解顺时针/螺旋规则的人:从变量名称开始,顺时针移动(在这种情况下,向后移动)到下一个 pointer 或 type . 重复直到表达结束 .
这是一个演示:
任何一方都带有int的const将生成 pointer to constant int .
const int *ptr=&i;
要么
int const *ptr=&i;
' * '之后的const将使 constant pointer to int .
int *const ptr=&i;
在这种情况下,所有这些都是 pointer to constant integer ,但这些都不是常量指针 .
const int *ptr1=&i, *ptr2=&j;
在这种情况下,所有都是 pointer to constant integer ,ptr2是 constant pointer to constant integer . 但是ptr1不是常量指针 .
int const *ptr1=&i, *const ptr2=&j;
向后读(由Clockwise/Spiral Rule驱动):
int* - 指向int的指针
int*
int const * - 指向const int的指针
int * const - 指向int的const指针
int * const
int const * const - const指向const int的指针
int const * const
现在第一个 const 可以在类型的任何一侧,所以:
const
const int * == int const *
const int *
const int * const == int const * const
如果你想真的疯了,你可以做这样的事情:
int ** - 指向int的指针
int **
int ** const - 指向int的指针的const指针
int ** const
int * const * - 指向int的const指针的指针
int * const *
int const ** - 指向const int指针的指针
int const **
int * const * const - 指向int的const指针的const指针
int * const * const
......
并确保我们清楚const的含义
const int* foo; int *const bar; //note, you actually need to set the pointer //here because you can't change it later ;)
foo 是指向常量整数的变量指针 . 这使您可以更改指向的内容,但不能更改指向的值 . 大多数情况下会看到C风格的字符串,其中有一个指向 const char 的指针 . 您可以更改指向的字符串,但不能更改 .
foo
const char
bar 是指向可以更改的值的常量或固定指针 . 这就像没有额外语法糖的参考 . 因为这个事实,通常你会使用一个你将使用 T* const 指针的引用,除非你需要允许 NULL 指针 .
bar
T* const
NULL
我和你有同样的疑问,直到我被C大师斯科特迈耶斯看到这个book . 请参阅本书中的第三个项目,其中详细介绍了如何使用 const .
请遵循这个建议
如果单词 const 出现在星号的左侧,则指向的是常量
如果单词 const 出现在星号的右侧,则指针本身是常量
如果双方都出现 const ,则两者都是不变的
这很简单但很棘手 . 请注意,我们可以将 const 限定符与任何数据类型交换( int , char , float 等) .
int
char
float
我们来看下面的例子 .
const int *p ==> *p 是只读的[ p 是一个指向常量整数的指针]
const int *p
*p
p
int const *p ==> *p 是只读的[ p 是一个指向常量整数的指针]
int const *p
int *p const ==> Wrong 声明 . 编译器抛出语法错误 .
int *p const
int *const p ==> p 是只读的[ p 是一个指向整数的常量指针] . 由于指针 p 在这里是只读的,因此声明和定义应该在同一个地方 .
int *const p
const int *p const ==> Wrong 声明 . 编译器抛出语法错误 .
const int *p const
const int const *p ==> *p 是只读的
const int const *p
const int *const p1 ==> *p 和 p 是只读的[ p 是一个指向常量整数的常量指针] . 由于指针 p 在这里是只读的,因此声明和定义应该在同一个地方 .
const int *const p1
int const *p const ==> Wrong 声明 . 编译器抛出语法错误 .
int const *p const
int const int *p ==> Wrong 声明 . 编译器抛出语法错误 .
int const int *p
int const const *p ==> *p 是只读的,相当于 int const *p
int const const *p
int const *const p ==> *p 和 p 是只读的[ p 是一个指向常量整数的常量指针] . 由于指针 p 在这里是只读的,声明和定义应该在同一个地方 .
int const *const p
一般规则是 const 关键字立即适用于它之前的内容 . 例外情况,起始 const 适用于以下内容 .
const int* 与 int const* 相同,表示 "pointer to constant int" .
int const*
const int* const 与 int const* const 相同,表示 "constant pointer to constant int" .
const int* const
int const* const
Edit: 对于Dos和Don'ts,如果this answer还不够,你能更准确地了解你想要的吗?
原始设计者一再将C和C声明语法描述为失败的实验 .
相反,我们将类型命名为“指向 Type 的指针”;我叫它 Ptr_ :
Type
Ptr_
template< class Type > using Ptr_ = Type*;
现在 Ptr_<char> 是指向 char 的指针 .
Ptr_<char>
Ptr_<const char> 是指向 const char 的指针 .
Ptr_<const char>
const Ptr_<const char> 是指向 const char 的 const 指针 .
const Ptr_<const char>
那里 .
我想这里已经回答了所有问题,但我只是想补充一点,你应该提防 typedef !它们不仅仅是文本替换 .
typedef
例如:
typedef char *ASTRING; const ASTRING astring;
astring 的类型是 char * const ,而不是 const char * . 这是我总是倾向于将 const 放在类型右侧的一个原因,而且从不在开始时 .
astring
char * const
const char *
这个问题显示 precisely 为什么我喜欢按照我在问题中提到的方式做事is const after type id acceptable?
简而言之,我发现记住规则的最简单方法是"const"追求它应用的东西 . 所以在你的问题中,"int const *"意味着int是常量,而"int * const"意味着指针是常量 .
如果有人决定把它放在最前面(例如:“const int *”),作为一种特殊的例外情况,它适用于它后面的东西 .
许多人喜欢使用这个特殊的例外,因为他们认为它看起来更好 . 我不喜欢它,因为它是一个例外,因此混淆了事情 .
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 TYPE TYPE::operator+(const TYPE &rhs) 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 & 参考也可能见证它在其他地方被改变的 Value .
int const &
几乎所有人都指出:
What’s the difference between const X* p, X* const p and const X* const p?
你必须从右到左阅读指针声明 . const X * p表示“p指向一个X为const”:X对象不能通过p改变 . X * const p表示“p是指向非const的X的const指针”:您不能更改指针p本身,但可以通过p更改X对象 . const X * const p表示“p是一个指向X的const指针”:你不能改变指针p本身,也不能通过p改变X对象 .
这主要涉及第二行:最佳实践,作业,功能参数等 .
一般做法 . 尝试尽你所能 const . 或者换句话说,首先让所有内容 const 开始,然后准确删除允许程序运行所需的最小 const 集 . 这对于获得const正确性将是一个很大的帮助,并将有助于确保不应该修改微妙的错误 .
避免像瘟疫那样的const_cast <> . 它有一两个合法的用例,但它们很少,而且很少 . 如果你想要改变一个 const 对象,那么你可以更好地找到第一步宣称它的人,并与他们讨论这个问题,以便就应该发生什么达成共识 .
这非常巧妙地导致了作业 . 只有在非const的情况下才可以分配 . 如果要分配到const的内容,请参阅上文 . 请记住,在声明 int const *foo; 和 int * const bar; 不同的事情是 const - 这里的其他答案令人钦佩地涵盖了这个问题,所以我不会进入它 .
int const *foo;
int * const bar;
功能参数:
通过 Value :例如 void func(int param) 你在调用网站上并不关心这种或那种方式 . 可以认为有一些用例将函数声明为 void func(int const param) ,但这对调用者没有影响,仅在函数本身上,因为在调用期间函数无法更改传递的任何值 .
void func(int param)
void func(int const param)
通过引用传递:例如 void func(int ¶m) 现在确实有所作为 . 刚刚宣布 func 被允许更改 param ,任何调用站点都应该准备好应对后果 . 将声明更改为 void func(int const ¶m) 会更改 Contract ,并保证 func 现在无法更改 param ,这意味着传入的内容将会返回 . 正如其他人所说,这对于廉价传递一个你不想改变的大型物体非常有用 . 传递引用比按值传递大对象要便宜得多 .
void func(int ¶m)
func
param
void func(int const ¶m)
通过指针:例如 void func(int *param) 和 void func(int const *param) 这两个几乎是他们的参考对应物的同义词,但需要注意的是,被叫函数现在需要检查 nullptr ,除非其他 Contract 保证确保 func 在 param 中永远不会收到 nullptr .
void func(int *param)
void func(int const *param)
nullptr
关于该主题的意见 . 在这样的情况下证明正确性是非常困难的,它有机会,并且总是检查 nullptr 的指针参数 . 从长远来看,你将拯救自己的痛苦和苦难 . 至于检查的成本,它会使程序范围广泛,即使在跨越源代码模块边界的函数调用中也是如此 .
在一天结束时,以上所有内容都是一个非常可靠的案例,总是更喜欢对指针的引用 . 它们全面安全 .
简单使用'const'
最简单的用法是声明一个命名常量 . 为此,我们声明一个常量,好像它是一个变量,但在它之前加上'const' . 必须立即在构造函数中初始化它,因为当然,之后无法设置值,因为这会改变它 . 例如,
const int Constant1=96;
将创建一个整数常量,无法想象地称为“Constant1”,值为96 .
这些常量对于程序中使用的参数很有用,但在编译程序后不需要更改 . 对于程序员而言,它优于C预处理器'#define'命令,因为编译器本身可以理解和使用它,而不是在到达主编译器之前由预处理器替换为程序文本,因此错误消息更有帮助 .
它也适用于指针但是必须要小心'const'来确定指针或它指向的是否是常量或两者 . 例如,
const int * Constant2
声明Constant2是指向常量整数的变量指针
int const * Constant2
是一种替代语法,它做同样的事情,而
int * const Constant3
声明Constant3是一个指向变量integer的常量指针
int const * const Constant4
声明Constant4是指向常量整数的常量指针 . 基本上'const'适用于其左边的任何东西(除非没有任何东西,在这种情况下它适用于它的直接权利) .
参考:http://duramecho.com/ComputerInformation/WhyHowCppConst.html
14 回答
对变量(此处为int)的引用,它是常量 . 我们主要将变量作为引用传递,因为引用的大小小于实际值,但是存在副作用,这是因为它就像实际变量的别名 . 我们可能会通过完全访问别名来意外更改主变量,因此我们将其设置为常量以防止此副作用 .
一旦常量指针指向变量,它就不能指向任何其他变量 .
一个指针,通过它可以不改变它指向的变量的值,称为指向常量的指针 .
指向常量的常量指针是一个指针,既不能改变它指向的地址,也不能改变保存在该地址的值 .
对于那些不了解顺时针/螺旋规则的人:从变量名称开始,顺时针移动(在这种情况下,向后移动)到下一个 pointer 或 type . 重复直到表达结束 .
这是一个演示:
任何一方都带有int的const将生成 pointer to constant int .
要么
' * '之后的const将使 constant pointer to int .
在这种情况下,所有这些都是 pointer to constant integer ,但这些都不是常量指针 .
在这种情况下,所有都是 pointer to constant integer ,ptr2是 constant pointer to constant integer . 但是ptr1不是常量指针 .
向后读(由Clockwise/Spiral Rule驱动):
int*
- 指向int的指针int const *
- 指向const int的指针int * const
- 指向int的const指针int const * const
- const指向const int的指针现在第一个
const
可以在类型的任何一侧,所以:const int *
==int const *
const 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指针......
并确保我们清楚const的含义
foo
是指向常量整数的变量指针 . 这使您可以更改指向的内容,但不能更改指向的值 . 大多数情况下会看到C风格的字符串,其中有一个指向const char
的指针 . 您可以更改指向的字符串,但不能更改 .bar
是指向可以更改的值的常量或固定指针 . 这就像没有额外语法糖的参考 . 因为这个事实,通常你会使用一个你将使用T* const
指针的引用,除非你需要允许NULL
指针 .我和你有同样的疑问,直到我被C大师斯科特迈耶斯看到这个book . 请参阅本书中的第三个项目,其中详细介绍了如何使用
const
.请遵循这个建议
如果单词
const
出现在星号的左侧,则指向的是常量如果单词
const
出现在星号的右侧,则指针本身是常量如果双方都出现
const
,则两者都是不变的这很简单但很棘手 . 请注意,我们可以将
const
限定符与任何数据类型交换(int
,char
,float
等) .我们来看下面的例子 .
const int *p
==>*p
是只读的[p
是一个指向常量整数的指针]int const *p
==>*p
是只读的[p
是一个指向常量整数的指针]int *p const
==> Wrong 声明 . 编译器抛出语法错误 .int *const p
==>p
是只读的[p
是一个指向整数的常量指针] . 由于指针p
在这里是只读的,因此声明和定义应该在同一个地方 .const int *p const
==> Wrong 声明 . 编译器抛出语法错误 .const int const *p
==>*p
是只读的const int *const p1
==>*p
和p
是只读的[p
是一个指向常量整数的常量指针] . 由于指针p
在这里是只读的,因此声明和定义应该在同一个地方 .int const *p const
==> Wrong 声明 . 编译器抛出语法错误 .int const int *p
==> Wrong 声明 . 编译器抛出语法错误 .int const const *p
==>*p
是只读的,相当于int const *p
int const *const p
==>*p
和p
是只读的[p
是一个指向常量整数的常量指针] . 由于指针p
在这里是只读的,声明和定义应该在同一个地方 .一般规则是
const
关键字立即适用于它之前的内容 . 例外情况,起始const
适用于以下内容 .const int*
与int const*
相同,表示 "pointer to constant int" .const int* const
与int const* const
相同,表示 "constant pointer to constant int" .Edit: 对于Dos和Don'ts,如果this answer还不够,你能更准确地了解你想要的吗?
原始设计者一再将C和C声明语法描述为失败的实验 .
相反,我们将类型命名为“指向
Type
的指针”;我叫它Ptr_
:现在
Ptr_<char>
是指向char
的指针 .Ptr_<const char>
是指向const char
的指针 .const Ptr_<const char>
是指向const char
的const
指针 .那里 .
我想这里已经回答了所有问题,但我只是想补充一点,你应该提防
typedef
!它们不仅仅是文本替换 .例如:
astring
的类型是char * const
,而不是const char *
. 这是我总是倾向于将const
放在类型右侧的一个原因,而且从不在开始时 .这个问题显示 precisely 为什么我喜欢按照我在问题中提到的方式做事is const after type id acceptable?
简而言之,我发现记住规则的最简单方法是"const"追求它应用的东西 . 所以在你的问题中,"int const *"意味着int是常量,而"int * const"意味着指针是常量 .
如果有人决定把它放在最前面(例如:“const int *”),作为一种特殊的例外情况,它适用于它后面的东西 .
许多人喜欢使用这个特殊的例外,因为他们认为它看起来更好 . 我不喜欢它,因为它是一个例外,因此混淆了事情 .
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 TYPE TYPE::operator+(const TYPE &rhs) const { ... }
实际上,const方法不能将内部类数据作为引用返回非const .
T const& operator[] (unsigned i) const;
,那么您可能还需要以下给出的非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 &
参考也可能见证它在其他地方被改变的 Value .几乎所有人都指出:
What’s the difference between const X* p, X* const p and const X* const p?
这主要涉及第二行:最佳实践,作业,功能参数等 .
一般做法 . 尝试尽你所能
const
. 或者换句话说,首先让所有内容const
开始,然后准确删除允许程序运行所需的最小const
集 . 这对于获得const正确性将是一个很大的帮助,并将有助于确保不应该修改微妙的错误 .避免像瘟疫那样的const_cast <> . 它有一两个合法的用例,但它们很少,而且很少 . 如果你想要改变一个
const
对象,那么你可以更好地找到第一步宣称它的人,并与他们讨论这个问题,以便就应该发生什么达成共识 .这非常巧妙地导致了作业 . 只有在非const的情况下才可以分配 . 如果要分配到const的内容,请参阅上文 . 请记住,在声明
int const *foo;
和int * const bar;
不同的事情是const
- 这里的其他答案令人钦佩地涵盖了这个问题,所以我不会进入它 .功能参数:
通过 Value :例如
void func(int param)
你在调用网站上并不关心这种或那种方式 . 可以认为有一些用例将函数声明为void func(int const param)
,但这对调用者没有影响,仅在函数本身上,因为在调用期间函数无法更改传递的任何值 .通过引用传递:例如
void func(int ¶m)
现在确实有所作为 . 刚刚宣布func
被允许更改param
,任何调用站点都应该准备好应对后果 . 将声明更改为void func(int const ¶m)
会更改 Contract ,并保证func
现在无法更改param
,这意味着传入的内容将会返回 . 正如其他人所说,这对于廉价传递一个你不想改变的大型物体非常有用 . 传递引用比按值传递大对象要便宜得多 .通过指针:例如
void func(int *param)
和void func(int const *param)
这两个几乎是他们的参考对应物的同义词,但需要注意的是,被叫函数现在需要检查nullptr
,除非其他 Contract 保证确保func
在param
中永远不会收到nullptr
.关于该主题的意见 . 在这样的情况下证明正确性是非常困难的,它有机会,并且总是检查
nullptr
的指针参数 . 从长远来看,你将拯救自己的痛苦和苦难 . 至于检查的成本,它会使程序范围广泛,即使在跨越源代码模块边界的函数调用中也是如此 .在一天结束时,以上所有内容都是一个非常可靠的案例,总是更喜欢对指针的引用 . 它们全面安全 .
简单使用'const'
最简单的用法是声明一个命名常量 . 为此,我们声明一个常量,好像它是一个变量,但在它之前加上'const' . 必须立即在构造函数中初始化它,因为当然,之后无法设置值,因为这会改变它 . 例如,
将创建一个整数常量,无法想象地称为“Constant1”,值为96 .
这些常量对于程序中使用的参数很有用,但在编译程序后不需要更改 . 对于程序员而言,它优于C预处理器'#define'命令,因为编译器本身可以理解和使用它,而不是在到达主编译器之前由预处理器替换为程序文本,因此错误消息更有帮助 .
它也适用于指针但是必须要小心'const'来确定指针或它指向的是否是常量或两者 . 例如,
声明Constant2是指向常量整数的变量指针
是一种替代语法,它做同样的事情,而
声明Constant3是一个指向变量integer的常量指针
声明Constant4是指向常量整数的常量指针 . 基本上'const'适用于其左边的任何东西(除非没有任何东西,在这种情况下它适用于它的直接权利) .
参考:http://duramecho.com/ComputerInformation/WhyHowCppConst.html