const int *,const int * const和int const *之间有什么区别?

问题描述 投票:1194回答:16

我总是搞砸如何正确使用const int*const int * constint const *。是否有一套规则来定义您能做什么和不能做什么?

我想知道在任务,传递到职能等方面所做的所有事情和所有不应做的事情。

c++ c pointers int const
16个回答
2049
投票

向后阅读(由Clockwise/Spiral Rule驱动):

  • int* - 指向int的指针
  • int const * - 指向const int的指针
  • int * const - const指向int的指针
  • 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的含义

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的指针。您可以更改指向的字符串,但不能更改这些字符串的内容。当字符串本身位于程序的数据段中且不应更改时,这很重要。

bar是一个常量或固定指针,指向可以更改的值。这就像没有额外语法糖的参考。因为这个事实,通常你会使用一个你将使用T* const指针的引用,除非你需要允许NULL指针。


6
投票

这很简单但很棘手。请注意,我们可以将const限定符与任何数据类型(intcharfloat等)交换。

我们来看下面的例子。


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 p1 ==> *pp是只读的[p是一个常量整数的常量指针]。由于这里的指针p是只读的,因此声明和定义应该在同一个地方。


int const *p const ==>错误的陈述。编译器抛出语法错误。

int const int *p ==>错误的陈述。编译器抛出语法错误。

int const const *p ==> *p是只读的,相当于int const *p

int const *const p ==> *pp是只读的[p是一个常量整数的常量指针]。由于这里的指针p是只读的,因此声明和定义应该在同一个地方。


5
投票

在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<char&>( static_cast<const TYPE&>(*this)[](i) ); }

Afaik,C中没有const函数,非成员函数本身不能是C ++中的const,const方法可能有副作用,而编译器不能使用const函数来避免重复函数调用。事实上,即使是一个简单的int const &参考也可能见证它在其他地方改变的价值。


5
投票

原始设计者反复将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的指针。

那里。

enter image description here


3
投票

对我来说,const的位置,即它是左侧还是右侧,或者左侧和右侧相对于*的位置,有助于我弄清楚实际意义。

  1. const*的左边表示指针指向的对象是const对象。
  2. const右侧的*表示指针是const指针。

下表摘自斯坦福CS106L标准C ++编程实验室课程阅读器。

enter image description here


2
投票

任何一方都带有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;

1
投票

这主要涉及第二行:最佳实践,作业,功能参数等。

一般做法。尝试使你可以做的一切const。或者换一种方式,让所有const开始,然后删除所需的最小consts集,以使程序运行。这对于获得const正确性是一个很大的帮助,并且有助于确保当人们尝试分配他们不应该修改的东西时不会引入细微的错误。

避免像瘟疫那样的const_cast <>。它有一两个合法用例,但它们很少,而且很少。如果你想改变一个const对象,你会做得更好,找到谁在第一步宣布它const并与他们谈论这个问题,以达成共识,应该发生什么。

这非常巧妙地导致了作业。只有在非const时才可以分配。如果要分配给const的内容,请参见上文。请记住,在声明int const *foo;int * const bar;不同的东西是const - 这里的其他答案令人钦佩地涵盖了这个问题,所以我不会进入它。

功能参数:

通过价值:例如void func(int param)你不在乎主叫站点的方式。可以认为有一些用例将函数声明为void func(int const param),但这对调用者没有影响,只对函数本身有影响,因为在调用期间函数无法更改传递的任何值。

通过引用传递:例如void func(int &param)现在确实有所作为。刚刚宣布func被允许改变param,任何呼叫站点应该准备好应对后果。将声明更改为void func(int const &param)会更改合同,并保证func现在无法更改param,这意味着传入的内容将会返回。正如其他人所说,这对于廉价传递一个你不想改变的大型物体非常有用。传递引用比按值传递大对象要便宜得多。

通过指针:例如void func(int *param)void func(int const *param)这两个几乎是他们的参考对应物的同义词,需要注意的是被叫函数现在需要检查nullptr,除非其他合同保证确保func它将永远不会在nullptr中获得param

关于该主题的意见。在这种情况下证明正确性是非常困难的,这太容易犯错了。所以不要冒险,并始终检查nullptr的指针参数。从长远来看,你将拯救自己的痛苦和痛苦,很难找到虫子。至于检查的成本,它是便宜的,并且在编译器内置的静态分析可以管理它的情况下,优化器无论如何都会忽略它。打开MSVC的链接时间代码生成,或者为GCC打开WOPR(我认为),你会得到它的程序范围,即使是跨越源代码模块边界的函数调用。

在一天结束时,以上所有内容都是一个非常可靠的案例,总是更喜欢对指针的引用。它们全面安全。


1
投票

只是为了C的完整性,遵循其他解释,对C ++不确定。

  • pp - 指针指针
  • p指针
  • 数据 - 事实指出,在例子x
  • 粗体 - 只读变量

Pointer

  • p data - int *p;
  • p data - int const *p;
  • p data - int * const p;
  • p data - int const * const p;

指向指针

  1. pp p data - int **pp;
  2. pp p data - int ** const pp;
  3. pp p data - int * const *pp;
  4. pp p data - int const **pp;
  5. pp p data - int * const * const pp;
  6. pp p data - int const ** const pp;
  7. pp p data - int const * const *pp;
  8. pp p data - 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-levels of Dereference

继续前进,但人类可能会将你逐出教会。

int x = 10;
int *p = &x;
int **pp = &p;
int ***ppp = &pp;
int ****pppp = &ppp;

printf("%d \n", ****pppp);

0
投票
  • 如果const位于*的左边,则指的是值(无论是const int还是int const都无关紧要)
  • 如果const*的右边,它指的是指针本身
  • 它可以同时出现

重要的一点:const int *p并不意味着你所指的价值是不变的!!这意味着您无法通过该指针进行更改。值本身可能会更改。例如

int x = 5;
const int *p = &x;
x = 6; //legal
printf("%d", *p) // prints 6
*p = 7; //error 

这主要用于函数签名,以保证函数不能更改传递的参数。


315
投票

对于那些不了解顺时针/螺旋规则的人:从变量的名称开始,顺时针移动(在这种情况下,向后移动)到下一个指针或类型。重复直到表达结束。

这是一个演示:


136
投票

我想这里的一切都已经回答了,但我只想补充一点,你应该提防typedefs!它们不仅仅是文本替换。

例如:

typedef char *ASTRING;
const ASTRING astring;

astring的类型是char * const,而不是const char *。这是我总是倾向于将const放在类型右侧的一个原因,而且从不在开始时。


49
投票

几乎所有人都指出:

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的常量指针”:你不能改变指针p本身,也不能通过p改变X对象。

44
投票
  1. 常量参考: 对变量(此处为int)的引用,它是常量。我们主要将变量作为引用传递,因为引用的大小小于实际值,但是存在副作用,这是因为它就像实际变量的别名。我们可能会通过完全访问别名来意外更改主变量,因此我们将其设置为常量以防止此副作用。 int var0 = 0; const int &ptr1 = var0; ptr1 = 8; // Error var0 = 6; // OK
  2. 常量指针 一旦常量指针指向变量,它就不能指向任何其他变量。 int var1 = 1; int var2 = 0; int *const ptr2 = &var1; ptr2 = &var2; // Error
  3. 指向常数的指针 一个指针,通过它可以不改变它指向的变量的值,这个指针称为指向常量的指针。 int const * ptr3 = &var2; *ptr3 = 4; // Error
  4. 指向常量的常量指针 指向常量的常量指针是一个指针,既不能改变它指向的地址,也不能改变保存在该地址的值。 int var3 = 0; int var4 = 0; const int * const ptr4 = &var3; *ptr4 = 1; // Error ptr4 = &var4; // Error

18
投票

一般规则是const关键字立即适用于它之前的内容。例外,一个起始的const适用于以下内容。

  • const int*int const*相同,意思是“指向常数int”。
  • const int* constint const* const相同,意思是“常量指向常量int”。

编辑:对于Dos和Don'ts,如果this answer还不够,你能更精确地了解你想要的吗?


17
投票

这个问题正是为什么我喜欢按照我在问题is const after type id acceptable?中提到的方式做事

简而言之,我发现记住规则的最简单方法是“const”在它适用的东西之后。所以在你的问题中,“int const *”意味着int是常量,而“int * const”意味着指针是常量。

如果有人决定把它放在最前面(例如:“const int *”),作为一种特殊的例外情况,它适用于它后面的东西。

许多人喜欢使用这个特殊的例外,因为他们认为它看起来更好。我不喜欢它,因为它是一个例外,因此混淆了事情。


15
投票

简单使用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'适用于其左边的任何东西(除非没有任何东西,在这种情况下它适用于它的直接权利)。

ref:http://duramecho.com/ComputerInformation/WhyHowCppConst.html


7
投票

在我遇到C ++ Guru Scott Meyers的book之前,我有同样的疑问。请参阅本书中的第三个项目,其中详细介绍了如何使用const

请遵循这个建议

  1. 如果const这个词出现在星号的左边,那么指向的是不变的
  2. 如果单词const出现在星号的右侧,则指针本身是常量
  3. 如果const出现在两边,两者都是不变的
© www.soinside.com 2019 - 2024. All rights reserved.