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

我想知道在赋值、传递给函数等方面的所有注意事项。


当前回答

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

pp-指针到指针p-指针数据-在示例x中指出的内容粗体-只读变量

指针

p数据-int*p;p数据-int常量*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常量**pp;pp p数据-int*const*const pp;pp p数据-int const**const pp;pp p数据-int const*const*pp;pp p数据-int常量*常量*常量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级取消引用

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

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

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

其他回答

反向阅读(由顺时针/螺旋法则驱动):

int*-指向int的指针int const*-指向const int的指针int*const-指向int的const指针int const*const-指向const int的const指针

现在,第一个常量可以位于类型的任一侧,因此:

常量int*==int常量*const int*const==int const*const

如果你想变得疯狂,你可以这样做:

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

bar是指向可更改值的常量或固定指针。这就像没有额外语法糖的引用。由于这一事实,通常您会在使用T*常量指针的地方使用引用,除非您需要允许NULL指针。

这个问题确切地说明了为什么我喜欢用我在问题中提到的在类型id可接受之后是常量的方式做事?

简而言之,我发现记住这个规则最简单的方法是“const”跟在它应用的对象后面。所以在你的问题中,“int const*”表示int是常量,而“int*const”表示指针是常量。

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

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

很多人都答对了,我会在这里整理好,并在给出的答案中添加一些缺失的额外信息。

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。常量int*

在3个地方安排3件事的方法3=6.

i.*开始时

*const int p      [Error]
*int const p      [Error]

二。开始时常量

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。常量常量int*

在4个地方安排4件事情的方法,其中2件是相同的4件/2!=12

i.*开始时

* int const const p     [Error]
* const int const p     [Error]
* const const int p     [Error]
 

二。开始时为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是常量int(5)的指针

const int *p
int const *p

int const const *p
const const int  *p
const int  const *p

类型b.p是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. 

完成了

C++中围绕常量正确性还有许多其他微妙之处。我想这里的问题只是关于C,但我会给出一些相关的例子,因为标记是C++:

通常将字符串等大参数作为TYPE const&传递,这会防止对象被修改或复制。例子:TYPE&TYPE::operator=(const TYPE&rhs){…return*this;}但是TYPE&const是没有意义的,因为引用总是常量。您应该始终将不修改类的类方法标记为const,否则不能从TYPE const引用调用该方法。例子:bool TYPE::运算符==(const TYPE&rhs)const{…}在一些常见情况下,返回值和方法都应该是常量。例子:const TYPE TYPE::运算符+(const TYPE&rhs)const{…}事实上,const方法不能返回内部类数据作为对非常量的引用。因此,必须经常使用常量重载创建常量和非常量方法。例如,如果定义T const&operator[](unsigned i)const;,那么您可能还需要以下给出的非常量版本:内联T运算符[](无符号i)(&O){返回const_cast<char&>(static_cast<const TYPE&>(*this)[](i));}

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

这主要涉及第二行:最佳实践、分配、功能参数等。

一般做法。尽可能使一切保持稳定。或者换一种说法,从一开始就让所有的常量都是常量,然后完全删除允许程序运行所需的最小常量集。这将大大有助于实现常量正确性,并有助于确保在人们尝试分配不应该修改的内容时不会引入细微的错误。

避免像瘟疫一样的const_cast<>。它有一两个合法的使用案例,但它们非常少。如果你试图改变一个常量对象,你会做得更好,找到第一步中声明它常量的人,并与他们讨论此事,以就应该发生的事情达成共识。

这很好地引导到作业中。只有当它是非常量时,才能将其赋值。如果您想分配给常量,请参见上文。请记住,在声明中int const*foo;和int*const bar;不同的东西是恒定的——这里的其他答案都很好地涵盖了这个问题,所以我就不赘述了。

功能参数:

传递值:例如,voidfunc(int-param),在调用站点,你不在乎这一种方式或另一种方式。可以这样做,即存在将函数声明为void func(int const param)的用例,但这对调用方没有影响,只对函数本身没有影响,因为函数在调用期间不能更改传递的任何值。

通过引用传递:例如,void func(int&param)现在它确实起了作用。正如刚才声明的,func可以更改参数,任何调用站点都应该准备好处理结果。将声明更改为void func(int const&param)将更改约定,并保证func现在不能更改param,这意味着传入的内容将返回。正如其他人所指出的,这对于便宜地传递不想更改的大型对象非常有用。传递引用比按值传递大型对象要便宜得多。

传递指针:例如,void func(int*param)和void func(int const*param)这两个与它们的引用对应项几乎同义,但需要注意的是,被调用的函数现在需要检查nullptr,除非其他契约保证func不会在param中收到nullpter。

关于这个话题的评论文章。在这种情况下证明正确性非常困难,犯错误太容易了。所以不要冒险,总是检查指针参数的nullptr。从长远来看,你会让自己免于痛苦和痛苦,并且很难找到bug。至于检查的成本,它非常便宜,在编译器中内置的静态分析可以管理它的情况下,优化器无论如何都会取消它。为MSVC启用“链接时间代码生成”,或为GCC启用“WOPR(我认为)”,您将在程序范围内获得它,即即使在跨越源代码模块边界的函数调用中。

在一天结束时,以上所有内容都证明了一个非常可靠的理由,即总是倾向于引用指针。他们只是更安全。