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

发布于 2024-07-27 23:44:17 字数 166 浏览 12 评论 0原文

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

我想知道在分配、传递给函数等方面所有该做和不该做的事情。

I always mess up how to use const int *, const int * const, and int * const correctly. Is there a set of rules defining what you can and cannot do?

I want to know all the do's and all don'ts in terms of assignments, passing to the functions, etc.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(23

⒈起吃苦の倖褔 2024-08-03 23:44:56

很多人都回答正确,我会在这里好好组织一下,并添加一些给定答案中缺少的额外信息。

Const是C语言中的关键字,也称为限定符。 常量可以
应用于任何变量的声明以指定其值
不会改变

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

i。 * 开始时

*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 at start

int const *p      type a. 
int * const p     type b. p is const ptr to int

主要类型 2. const const int*

将 4 件物品排列在 4 个位置,其中 2 件相似的方法 4!/2!=12

i。 * 开始时

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

ii. int at start

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 at start

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) 的 ptr

const int *p
int const *p

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

类型 b。 p 是 int (2) 的 const ptr

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 const * 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 * const p,p2 ;

similarly 
here p is const ptr to int  (type b.)   
but p2 is just int not even cost int

int const * const p,p2 ; >

here p is const ptr to const int  (type c.)
but p2 is just const int. 

完成

Lot of people answered correctly I will just organize well here and put some Extra info which is missing in given Answers.

Const is keyword in C language also known as qualifier. Const can
applied to the declaration of any variable to specify that it's value
will not changed

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.

How to read ?

just read from right to left every statement works smoothly

3 main things

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

[Error]

if * comes before int 

two types

1. const int *

2. const const int *

we look first

Major type 1. const int*

ways to arrange 3 things at 3 places 3!=6

i. * at start

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

ii. const at start

const int *p      type a. p is ptr to const int 
const *int p      [Error]

iii. int at start

int const *p      type a. 
int * const p     type b. p is const ptr to int

Major type 2. const const int*

ways to arrange 4 things at 4 places in which 2 are alike 4!/2!=12

i. * at start

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

ii. int at start

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 at start

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]

squeezing all in one

type a. p is ptr to const int (5)

const int *p
int const *p

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

type b. p is const ptr to int (2)

int * const p
int * const const p;

type c. p is const ptr to const int (2)

int const * const p
const int * const p

just little calculation

1. const int * p        total arrangemets (6)   [Errors] (3)
2. const const int * p  total arrangemets (12)  [Errors] (6)

little Extra

int const * 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 * const p,p2 ;

similarly 
here p is const ptr to int  (type b.)   
but p2 is just int not even cost int

int const * const p,p2 ;

here p is const ptr to const int  (type c.)
but p2 is just const int. 

Finished

天气好吗我好吗 2024-08-03 23:44:54

简单助记符:

type指针<- * -> pointee name


我喜欢将 int *i 视为声明“i 的取消引用是 int”; 从这个意义上说,const int *i 的意思是“i 的 deref 是 const int”,而 int *const i code> 表示“const i 的 deref 为 int”。

(这样思考的一个危险是它可能会导致人们倾向于 int const *i 声明风格,人们可能会讨厌/不允许这种风格)

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)

双手揣兜 2024-08-03 23:44:52

没有人提到 Kernighan 和 Ritchie 在他们的 C 书中指出的系统底层声明:

声明模仿表达式。

我会重复这一点,因为它非常重要,并且给出了清晰的策略解析最复杂的声明:

声明模仿表达式。

声明包含与声明的标识符稍后可以出现的表达式相同的运算符,并且具有与表达式中相同的优先级。 这就是为什么“顺时针螺旋规则”是错误的:求值顺序严格由运算符优先级决定,完全不考虑左、右或旋转方向。

以下是一些示例,按复杂性递增的顺序排列:

  • int i;:当按原样使用 i 时,它是以下表达式类型int。 因此,i 一个 int。
  • int *p;:当使用*取消引用p时,表达式的类型为int< /代码>。 因此,p是一个指向int的指针。
  • const int *p;:当用*取消引用p时,表达式的类型为const int。 因此,p是一个指向const int的指针。
  • int *const p;p 是 const。 如果使用 * 取消引用此常量表达式,则该表达式的类型为 int。 因此,p 是一个指向 int 的 const 指针。
  • const int *const p;p 是 const。 如果使用 * 取消引用此常量表达式,则该表达式的类型为 const int。 因此,p 是一个指向 const int 的 const 指针。

到目前为止,我们还没有遇到任何运算符优先级问题:我们只是从右到左进行计算。 当我们享受指针数组和指向数组的指针的乐趣时,情况就会改变。 您可能需要打开备忘单

  • int a[3];:当我们将数组索引运算符应用于a时,结果是一个int 。 因此,a是一个int数组。
  • int *a[3];:这里索引运算符具有更高的优先级,因此我们首先应用它:当我们将数组索引运算符应用于a,结果是 int *。 因此,a 是一个指向 int 的指针数组。 这并不罕见。
  • int (*a)[3];:此处运算符优先级被圆括号覆盖,与任何表达式中的情况完全相同。 因此,我们首先取消引用。 现在我们知道 a 是指向某种类型的指针。 *a,解引用的指针,是该类型的一个表达式。当我们将数组索引运算符应用于*a时,我们得到一个简单的int,这意味着 *a 是一个由三个 int 组成的数组,而 a 是指向该数组的指针。 这在 C++ 模板之外相当罕见,这就是为什么运算符优先级不适合这种情况。 请注意如何使用此类指针作为其声明的模型:int i = (*a)[1];。 首先必须使用括号来取消引用。
  • int (*a)[3][2];:没有什么可以阻止任何人拥有指向多维数组的指针,这是一种顺时针循环螺旋建议变得显而易见的情况废话。

现实生活中有时会出现的一个东西是函数指针。 我们还需要括号,因为函数调用运算符(C++ 中的 operator()(),C 中的简单语法规则)比取消引用的 operator*() 具有更高的优先级,同样是因为函数返回指针比函数指针更常见:

  • int *f();:首先调用函数,因此 f< /code> 是一个函数。 该调用必须取消引用才能产生 int,因此返回值是指向 int 的指针。 用法:int i = *f();


  • int (*fp)();:括号更改运算符应用程序的顺序。 因为我们必须首先取消引用,所以我们知道 fp 是一个指向某个东西的指针。 因为我们可以将函数调用运算符应用于 *fp,所以我们知道(在 C 语言中)fp 是一个指向函数的指针; 在 C++ 中,我们只知道它是为它定义的 operator()() 。 由于该调用不带参数并返回 int,因此在 C++ 中,fp 是指向具有该签名的函数的指针。 (在 C 中,空参数列表表示对参数一无所知,但未来的 C 规范可能会禁止这种过时的使用。)

  • int *(*fp)(); :当然,我们可以从指向的函数返回指向 int 的指针。

  • int (*(*fp)())[3];:首先取消引用,因此是一个指针; 接下来应用函数调用运算符,因此是指向函数的指针; 再次取消引用返回值,因此是指向返回指针的函数的指针; 将索引运算符应用于that:指向返回数组指针的函数的指针。 结果是一个 int,因此指向函数的指针返回指向 int 数组的指针。-

    所有括号都是必需的:正如所讨论的,我们必须在发生其他事情之前优先使用 (*fp) 取消对函数指针的引用。 显然,我们需要函数调用; 并且由于该函数返回一个指向数组的指针(而不是指向其第一个元素!),因此在对其进行索引之前我们也必须取消引用它。 我承认我写了一个测试程序来检查这一点,因为我不确定,即使使用这种万无一失的方法;-)。 这是:

#include <iostream>
using namespace std;

int (*f())[3]
{
  static int arr[3] = {1,2,3};
  return &arr;
}

int (*(*fp)())[3] = &f;

int main()
{
  for(int i=0; i<3; i++)
  {
    cout << (*(*fp)())[i] << endl;
  }
}

注意声明对表达式的模仿是多么漂亮!

Nobody has mentioned the system underlying declarations which Kernighan and Ritchie pointed out in their C book:

Declarations mimic expressions.

I'll repeat this because it so essential and gives a clear strategy to parse even the most complicated declarations:

Declarations mimic expressions.

The declarations contain the same operators as expressions the declared identifier can appear in later, with the same priority they have in expressions. This is why the "clockwise spiral rule" is wrong: The evaluation order is strictly determined by the operator precedences, with complete disregard for left, right or rotational directions.

Here are a few Examples, in order of increasing complexity:

  • int i;: When i is used as-is, it is an expression of type int. Therefore, i is an int.
  • int *p;: When p is dereferenced with *, the expression is of type int. Therefore, p is a pointer to int.
  • const int *p;: When p is dereferenced with *, the expression is of type const int. Therefore, p is a pointer to const int.
  • int *const p;: p is const. If this constant expression is dereferenced with *, the expression is of type int. Therefore, p is a const pointer to int.
  • const int *const p;: p is const. If this constant expression is dereferenced with *, the expression is of type const int. Therefore, p is a const pointer to const int.

So far we didn't have any issues with operator precedence yet: We simply evaluated right-to-left. This changes when we have fun with arrays of pointers and pointers to arrays. You may want to have a cheat sheet open.

  • int a[3];: When we apply the array indexing operator to a, the result is an int. Therefore, a is an array of int.
  • int *a[3];: Here the indexing operator has higher precedence, so we apply it first: When we apply the array indexing operator to a, the result is an int *. Therefore, a is an array of pointers to int. This is not uncommon.
  • int (*a)[3];: Here the operator precedence is overridden by round parentheses, exactly as in any expression. Consequently, we dereference first. We know now that a is a pointer to some type. *a, the dereferenced pointer, is an expression of that type. When we apply the array indexing operator to *a, we obtain a plain int, which means that *a is an array of three ints, and a is a pointer to that array. This is fairly uncommon outside of C++ templates, which is why the operator precedences are not catering to this case. Note how the use of such a pointer is the model for its declaration: int i = (*a)[1];. The parentheses are mandatory to dereference first.
  • int (*a)[3][2];: There is nothing preventing anybody from having pointers to multi-dimensional arrays, one case where circular spiral clockwise advice becomes obvious nonsense.

A thing that sometimes comes up in real life are function pointers. We need parentheses there as well because the function call operator (operator()() in C++, simple syntax rule in C) has higher priority than the dereferencing operator*(), again because it's more common to have functions returning pointers than pointers to functions:

  • int *f();: Function call first, so f is a function. The call must be dereferenced to result in an int, so the return value is a pointer to int. Usage: int i = *f();.

  • int (*fp)();: Parentheses change order of operator application. Because we must dereference first we know that fp is a pointer to something. Because we can apply the function call operator to *fp we know (in C) that fp is a pointer to a function; in C++ we only know that it is something for which operator()() is defined. Since the call takes no parameters and returns an int, fp is in C++ a pointer to a function with that signature. (In C, an empty parameter list indicates that nothing is known about the parameters, but future C specifications may forbid that obsolete use.)

  • int *(*fp)();: Of course we can return pointers to int from a function pointed to.

  • int (*(*fp)())[3];: Dereference first, hence a pointer; apply function call operator next, hence a pointer to function; dereference the return value again, hence a pointer to a function returning a pointer; apply the indexing operator to that: pointer to function returning pointer to array. The result is an int, hence pointer to function returning pointer to array of ints.-

    All parentheses are necessary: As discussed, we must prioritize dereferencing of the function pointer with (*fp) before anything else happens. Obviously, we need the function call; and since the function returns a pointer to an array (not to its first element!), we must dereference that as well before we can index it. I admit that I wrote a test program to check this because I wasn't sure, even with this fool-proof method ;-). Here it is:

#include <iostream>
using namespace std;

int (*f())[3]
{
  static int arr[3] = {1,2,3};
  return &arr;
}

int (*(*fp)())[3] = &f;

int main()
{
  for(int i=0; i<3; i++)
  {
    cout << (*(*fp)())[i] << endl;
  }
}

Note how beautifully the declaration mimics the expression!

初心未许 2024-08-03 23:44:52
  1. const int* - 指向常量 int 对象的指针。

可以改变指针的值; 您无法更改指针所指向的 int 对象的值。


  1. const int * const - 指向常量 int 对象的常量指针。

您不能更改指针的值,也不能更改指针指向的 int 对象的值。


  1. int const * - 指向常量int对象的指针。

此语句相当于 1. const int* - 您可以更改指针的值,但不能更改指针所指向的 int 对象的值。


实际上,还有第四个选项:

  1. int * const - 指向int对象的常量指针。

您可以更改指针指向的对象的值,但不能更改指针本身的值。 指针将始终指向同一个 int 对象,但该 int 对象的值可以更改。


如果您想确定某种类型的 C 或 C++ 构造,可以使用 Clockwise/Spiral大卫·安德森 (David Anderson) 制定的规则; 但不要与 Ross J. Anderson 制定的安德森规则混淆,这是非常独特的。

  1. const int* - pointer to constant int object.

You can change the value of the pointer; you can not change the value of the int object, the pointer points to.


  1. const int * const - constant pointer to constant int object.

You can not change the value of the pointer nor the value of the int object the pointer points to.


  1. int const * - pointer to constant int object.

This statement is equivalent to 1. const int* - You can change the value of the pointer but you can not change the value of the int object, the pointer points to.


Actually, there is a 4th option:

  1. int * const - constant pointer to int object.

You can change the value of the object the pointer points to but you can not change the value of the pointer itself. The pointer will always point to the same int object but this value of this int object can be changed.


If you want to determine a certain type of C or C++ construct you can use the Clockwise/Spiral Rule made by David Anderson; but not to confuse with Anderson`s Rule made by Ross J. Anderson, which is something quite distinct.

稀香 2024-08-03 23:44:50

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

  • pp - 指向指针的指针
  • p - 指针
  • 数据 - 所指向的对象,在示例中 x
  • bold - 只读变量

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< /strong> p data - int ** const pp;
  3. pp p data - int * const *pp;
  4. pp p data< /strong> - int const **pp;
  5. pp p data - int * const * const pp;
  6. < strong>pp p 数据 - int const ** const pp;
  7. pp p 数据 - < code>int const * const *pp;
  8. pp p 数据 - int const * const * const pp;< /code>
// 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);

Just for the sake of completeness for C following the others explanations, not sure for C++.

  • pp - pointer to pointer
  • p - pointer
  • data - the thing pointed, in examples x
  • bold - read-only variable

Pointer

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

Pointer to pointer

  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

Just keep going, but may the humanity excommunicate you.

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

printf("%d \n", ****pppp);
韶华倾负 2024-08-03 23:44:49

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

一般实践。 尽量将所有内容都设为 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(我认为),您将在程序范围内获得它,即即使在跨越源代码模块边界的函数调用中。

归根结底,以上所有内容都为我们总是更喜欢引用而不是指针提供了一个非常可靠的理由。 他们只是更安全。

This mostly addresses the second line: best practices, assignments, function parameters etc.

General practice. Try to make everything const that you can. Or to put that another way, make everything const to begin with, and then remove exactly the minimum set of consts necessary to allow the program to function. This will be a big help in attaining const-correctness, and will help ensure that subtle bugs don't get introduced when people try and assign into things they're not supposed to modify.

Avoid const_cast<> like the plague. There are one or two legitimate use cases for it, but they are very few and far between. If you're trying to change a const object, you'll do a lot better to find whoever declared it const in the first pace and talk the matter over with them to reach a consensus as to what should happen.

Which leads very neatly into assignments. You can assign into something only if it is non-const. If you want to assign into something that is const, see above. Remember that in the declarations int const *foo; and int * const bar; different things are const - other answers here have covered that issue admirably, so I won't go into it.

Function parameters:

Pass by value: e.g. void func(int param) you don't care one way or the other at the calling site. The argument can be made that there are use cases for declaring the function as void func(int const param) but that has no effect on the caller, only on the function itself, in that whatever value is passed cannot be changed by the function during the call.

Pass by reference: e.g. void func(int ¶m) Now it does make a difference. As just declared func is allowed to change param, and any calling site should be ready to deal with the consequences. Changing the declaration to void func(int const ¶m) changes the contract, and guarantees that func can now not change param, meaning what is passed in is what will come back out. As other have noted this is very useful for cheaply passing a large object that you don't want to change. Passing a reference is a lot cheaper than passing a large object by value.

Pass by pointer: e.g. void func(int *param) and void func(int const *param) These two are pretty much synonymous with their reference counterparts, with the caveat that the called function now needs to check for nullptr unless some other contractual guarantee assures func that it will never receive a nullptr in param.

Opinion piece on that topic. Proving correctness in a case like this is hellishly difficult, it's just too damn easy to make a mistake. So don't take chances, and always check pointer parameters for nullptr. You will save yourself pain and suffering and hard to find bugs in the long term. And as for the cost of the check, it's dirt cheap, and in cases where the static analysis built into the compiler can manage it, the optimizer will elide it anyway. Turn on Link Time Code Generation for MSVC, or WOPR (I think) for GCC, and you'll get it program wide, i.e. even in function calls that cross a source code module boundary.

At the end of the day all of the above makes a very solid case to always prefer references to pointers. They're just safer all round.

宁愿没拥抱 2024-08-03 23:44:48

我在下面画了一张图来解释这一点,也许有帮助。

int const vconst int v 是相同的。

输入图片此处描述

I drew an image below to explain this, maybe helpful.

int const v and const int v are identical.

enter image description here

机场等船 2024-08-03 23:44:45
  • 如果 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 

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

  • if const is to the left of *, it refers to the value (it doesn't matter whether it's const int or int const)
  • if const is to the right of *, it refers to the pointer itself
  • it can be both at the same time

An important point: const int *p does not mean the value you are referring to is constant!!. It means that you can't change it through that pointer (meaning, you can't assign $*p = ...`). The value itself may be changed in other ways. Eg

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

This is meant to be used mostly in function signatures, to guarantee that the function can't accidentally change the arguments passed.

晚风撩人 2024-08-03 23:44:44

两边带有 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;

The const with the int on either sides will make pointer to constant int:

const int *ptr=&i;

or:

int const *ptr=&i;

const after * will make constant pointer to int:

int *const ptr=&i;

In this case all of these are pointer to constant integer, but none of these are constant pointer:

 const int *ptr1=&i, *ptr2=&j;

In this case all are pointer to constant integer and ptr2 is constant pointer to constant integer. But ptr1 is not constant pointer:

int const *ptr1=&i, *const ptr2=&j;
皇甫轩 2024-08-03 23:44:43

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

  • 你经常传递像字符串这样的大参数作为 TYPE const & 这会阻止对象被修改或复制。 示例:

    类型和类型 TYPE::operator=(const TYPE &rhs) { ... return *this; }

    但是类型& 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;,那么您可能还需要以下给出的非常量版本:

    内联 T& 运算符[](无符号i){
    返回 const_cast(
    static_cast(*this)[](i)
    );
    }

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

There are many other subtle points surrounding const correctness in C++. I suppose the question here has simply been about C, but I'll give some related examples since the tag is C++ :

  • You often pass large arguments like strings as TYPE const & which prevents the object from being either modified or copied. Example :

    TYPE& TYPE::operator=(const TYPE &rhs) { ... return *this; }

    But TYPE & const is meaningless because references are always const.

  • You should always label class methods that do not modify the class as const, otherwise you cannot call the method from a TYPE const & reference. Example :

    bool TYPE::operator==(const TYPE &rhs) const { ... }

  • There are common situations where both the return value and the method should be const. Example :

    const TYPE TYPE::operator+(const TYPE &rhs) const { ... }

    In fact, const methods must not return internal class data as a reference-to-non-const.

  • As a result, one must often create both a const and a non-const method using const overloading. For example, if you define T const& operator[] (unsigned i) const;, then you'll probably also want the non-const version given by :

    inline T& operator[] (unsigned i) {
    return const_cast<char&>(
    static_cast<const TYPE&>(*this)[](i)
    );
    }

Afaik, there are no const functions in C, non-member functions cannot themselves be const in C++, const methods might have side effects, and the compiler cannot use const functions to avoid duplicate function calls. In fact, even a simple int const & reference might witness the value to which it refers be changed elsewhere.

酷遇一生 2024-08-03 23:44:42

简单地记住:

如果 const 位于 * 之前,则 value 是常量。

如果 const 在 * 之后,则地址是常量。

如果 const 在 * 之前和之后都可用,则值和地址都是常量。

例如

  1. int * const var; //这里地址是不变的。

  2. int const * var; //这里的值是常数。

  3. int const * const var; // 值和地址都是常数。

To remember in easy way :

If const is before * then value is constant.

If const is after * then address is constant.

if const are available both before and after * then both value and address are constant.

e.g.

  1. int * const var; //here address is constant.

  2. int const * var; //here value is constant.

  3. int const * const var; // both value and address are constant.

白云不回头 2024-08-03 23:44:41

C 和 C++ 声明语法被原始设计者多次描述为失败的实验。

相反,让我们将类型命名为“指向 Type 的指针”; 我将其称为 Ptr_

template< class Type >
using Ptr_ = Type*;

现在 Ptr_ 是一个指向 char 的指针。

Ptr_ 是指向 const char 的指针。

const Ptr_ 是一个指向 const charconst 指针。

The C and C++ declaration syntax has repeatedly been described as a failed experiment, by the original designers.

Instead, let's name the type “pointer to Type”; I’ll call it Ptr_:

template< class Type >
using Ptr_ = Type*;

Now Ptr_<char> is a pointer to char.

Ptr_<const char> is a pointer to const char.

And const Ptr_<const char> is a const pointer to const char.

不交电费瞎发啥光 2024-08-03 23:44:39

我和你有同样的疑问,直到我遇到这本书 由 C++ 大师 Scott Meyers 撰写。 请参阅本书中的第三项,其中详细讨论了如何使用 const

只要遵循这个建议

  1. 如果单词 const 出现在星号的左侧,则指向的是常量
  2. 如果单词 const 出现在星号的右侧,则指针本身是常量
  3. 如果两边都出现const,则都是常量

I had the same doubt as you until I came across this book by the C++ Guru Scott Meyers. Refer the third Item in this book where he talks in details about using const.

Just follow this advice

  1. If the word const appears to the left of the asterisk, what's pointed to is constant
  2. If the word const appears to the right of the asterisk, the pointer itself is constant
  3. If const appears on both sides, both are constant
清风挽心 2024-08-03 23:44:39

对我来说,const 的位置,即它相对于 * 是否出现在左侧或右侧,或者同时出现在左侧和右侧,可以帮助我弄清楚实际含义。

  1. *左边的const表示指针指向的对象是一个const对象。

  2. * 右侧的 const 表示该指针是 const 指针。

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

输入图像描述这里

For me, the position of const i.e. whether it appears to the LEFT or RIGHT or on both LEFT and RIGHT relative to the * helps me figure out the actual meaning.

  1. A const to the LEFT of * indicates that the object pointed by the pointer is a const object.

  2. A const to the RIGHT of * indicates that the pointer is a const pointer.

The following table is taken from Stanford CS106L Standard C++ Programming Laboratory Course Reader.

enter image description here

是伱的 2024-08-03 23:44:37

这很简单但很棘手。 请注意,我们可以将 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 p ==> *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是只读的,所以声明和定义应该在同一位置。

It's simple but tricky. Please note that we can apply the const qualifier to any data type (int, char, float, etc.).

Let's see the below examples.


const int *p ==> *p is read-only [p is a pointer to a constant integer]

int const *p ==> *p is read-only [p is a pointer to a constant integer]


int *p const ==> Wrong Statement. Compiler throws a syntax error.

int *const p ==> p is read-only [p is a constant pointer to an integer].
As pointer p here is read-only, the declaration and definition should be in same place.


const int *p const ==> Wrong Statement. Compiler throws a syntax error.

const int const *p ==> *p is read-only

const int *const p ==> *p and p are read-only [p is a constant pointer to a constant integer]. As pointer p here is read-only, the declaration and definition should be in same place.


int const *p const ==> Wrong Statement. Compiler throws a syntax error.

int const int *p ==> Wrong Statement. Compiler throws a syntax error.

int const const *p ==> *p is read-only and is equivalent to int const *p

int const *const p ==> *p and p are read-only [p is a constant pointer to a constant integer]. As pointer p here is read-only, the declaration and definition should be in same place.

再见回来 2024-08-03 23:44:36

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

Simple Use of const.

The simplest use is to declare a named constant. To do this, one declares a constant as if it was a variable but add const before it. One has to initialize it immediately in the constructor because, of course, one cannot set the value later as that would be altering it. For example:

const int Constant1=96; 

will create an integer constant, unimaginatively called Constant1, with the value 96.

Such constants are useful for parameters which are used in the program but are do not need to be changed after the program is compiled. It has an advantage for programmers over the C preprocessor #define command in that it is understood & used by the compiler itself, not just substituted into the program text by the preprocessor before reaching the main compiler, so error messages are much more helpful.

It also works with pointers but one has to be careful where const to determine whether the pointer or what it points to is constant or both. For example:

const int * Constant2 

declares that Constant2 is variable pointer to a constant integer and:

int const * Constant2

is an alternative syntax which does the same, whereas

int * const Constant3

declares that Constant3 is constant pointer to a variable integer and

int const * const Constant4

declares that Constant4 is constant pointer to a constant integer. Basically ‘const’ applies to whatever is on its immediate left (other than if there is nothing there in which case it applies to whatever is its immediate right).

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

忘年祭陌 2024-08-03 23:44:35

一般规则是 const 关键字适用于紧邻其前面的内容。 例外,起始 const 适用于后面的内容。

  • const int*int const* 相同,表示“指向常量 int 的指针”
  • const int* constint const* const 相同,表示“指向常量 int 的常量指针”

编辑:
对于注意事项,如果这个答案< /a> 还不够,你能更准确地说明你想要什么吗?

The general rule is that the const keyword applies to what precedes it immediately. Exception, a starting const applies to what follows.

  • const int* is the same as int const* and means "pointer to constant int".
  • const int* const is the same as int const* const and means "constant pointer to constant int".

Edit:
For the Dos and Don'ts, if this answer isn't enough, could you be more precise about what you want?

街道布景 2024-08-03 23:44:33

这个问题准确地显示了为什么我喜欢按照我在问题中提到的方式做事类型 id 之后的 const 可以接受吗?

简而言之,我发现记住该规则的最简单方法是“const”位于它所应用的内容之后到。 因此,在您的问题中,“int const *”意味着 int 是常量,而“int * const”意味着指针是常量。

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

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

This question shows precisely why I like to do things the way I mentioned in my question is const after type id acceptable?

In short, I find the easiest way to remember the rule is that the "const" goes after the thing it applies to. So in your question, "int const *" means that the int is constant, while "int * const" would mean that the pointer is constant.

If someone decides to put it at the very front (eg: "const int *"), as a special exception in that case it applies to the thing after it.

Many people like to use that special exception because they think it looks nicer. I dislike it, because it is an exception, and thus confuses things.

一杯敬自由 2024-08-03 23:44:31
  1. 恒定参考:

    对变量(此处为 int)的引用,该变量是常量。 我们主要将变量作为引用传递,因为引用的大小比实际值小,但有一个副作用,那就是它就像实际变量的别名。 我们可能会通过完全访问别名来意外更改主变量,因此我们将其设为常量以防止这种副作用。

    int var0 = 0; 
      const int &ptr1 = var0; 
      ptr1 = 8;   // 错误 
      var0 = 6;   // 好的 
      
  2. 常量指针

    一旦常量指针指向一个变量,它就不能指向任何其他变量。

    int var1 = 1; 
      int var2 = 0; 
    
      int *const ptr2 = &var1; 
      ptr2 = &var2;   // 错误 
      
  3. 指向常量的指针

    无法更改其所指向变量值的指针称为常量指针。

    int const * ptr3 = &var2; 
      *ptr3 = 4;   // 错误 
      
  4. 指向常量的常量指针

    指向常量的常量指针是一个既不能更改其指向的地址,也不能更改保存在该地址的值的指针。

    int var3 = 0; 
      int var4 = 0; 
      const int * const ptr4 = &var3; 
      *ptr4 = 1;   // 错误 
       ptr4 = &var4;   // 错误 
      
  1. Constant reference:

    A reference to a variable (here int), which is constant. We pass the variable as a reference mainly, because references are smaller in size than the actual value, but there is a side effect and that is because it is like an alias to the actual variable. We may accidentally change the main variable through our full access to the alias, so we make it constant to prevent this side effect.

    int var0 = 0;
    const int &ptr1 = var0;
    ptr1 = 8; // Error
    var0 = 6; // OK
    
  2. Constant pointers

    Once a constant pointer points to a variable then it cannot point to any other variable.

    int var1 = 1;
    int var2 = 0;
    
    int *const ptr2 = &var1;
    ptr2 = &var2; // Error
    
  3. Pointer to constant

    A pointer through which one cannot change the value of a variable it points is known as a pointer to constant.

    int const * ptr3 = &var2;
    *ptr3 = 4; // Error
    
  4. Constant pointer to a constant

    A constant pointer to a constant is a pointer that can neither change the address it's pointing to and nor can it change the value kept at that address.

    int var3 = 0;
    int var4 = 0;
    const int * const ptr4 = &var3;
    *ptr4 = 1;     // Error
     ptr4 = &var4; // Error
    
生生漫 2024-08-03 23:44:30

就像几乎每个人都指出的那样:

之间有什么区别>const X* pX* const pconst X* const p

你必须阅读指针声明
从右到左。

  • const X* p 表示“p 指向 const 的 X”:不能通过 p 更改 X 对象。

  • X* const p 表示“p 是指向非常量 X 的 const 指针”:您无法更改指针 p 本身,但可以更改 X通过 p 对象。

  • const X* const p 表示“p 是指向 const X 的 const 指针”:您不能更改指针 p 本身,也不能更改 X 对象通过 p.

Like pretty much everyone pointed out:

What’s the difference between const X* p, X* const p and const X* const p?

You have to read pointer declarations
right-to-left.

  • const X* p means "p points to an X that is const": the X object can't be changed via p.

  • X* const p means "p is a const pointer to an X that is non-const": you can't change the pointer p itself, but you can change the X object via p.

  • const X* const p means "p is a const pointer to an X that is const": you can't change the pointer p itself, nor can you change the X object via p.

揽月 2024-08-03 23:44:28

我认为这里已经回答了所有问题,但我只想补充一点,您应该小心 typedef! 它们不仅仅是文本替换。

例如:

typedef char *ASTRING;
const ASTRING astring;

astring的类型是char * const,而不是const char *。 这是我总是倾向于将 const 放在类型右侧而不是放在开头的原因之一。

I think everything is answered here already, but I just want to add that you should beware of typedefs! They're NOT just text replacements.

For example:

typedef char *ASTRING;
const ASTRING astring;

The type of astring is char * const, not const char *. This is one reason I always tend to put const to the right of the type, and never at the start.

执妄 2024-08-03 23:44:26

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

这是一个演示:

指向 int 的指针

const 指向 int const 的指针

指向 int const 的指针

const int 指针

const 指向 int 的指针

For those who don't know about Clockwise/Spiral Rule:
Start from the name of the variable, move clockwisely (in this case, move backward) to the next pointer or type. Repeat until expression ends.

Here is a demo:

pointer to int

const pointer to int const

pointer to int const

pointer to const int

const pointer to int

望喜 2024-08-03 23:44:25

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

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

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

  • const int * == int const *
  • < code>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 指针
  • ...

如果您不确定,您可以可以使用类似 cdecl+ 自动将声明转换为散文。

为了确保我们清楚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 指针。

Read it backwards (as driven by Clockwise/Spiral Rule):

  • int* - pointer to int
  • int const * - pointer to const int
  • int * const - const pointer to int
  • int const * const - const pointer to const int

Now the first const can be on either side of the type so:

  • const int * == int const *
  • const int * const == int const * const

If you want to go really crazy you can do things like this:

  • int ** - pointer to pointer to int
  • int ** const - a const pointer to a pointer to an int
  • int * const * - a pointer to a const pointer to an int
  • int const ** - a pointer to a pointer to a const int
  • int * const * const - a const pointer to a const pointer to an int
  • ...

If you're ever uncertain, you can use a tool like cdecl+ to convert declarations to prose automatically.

To make sure we are clear on the meaning of 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 is a variable pointer to a constant integer. This lets you change what you point to but not the value that you point to. Most often this is seen with C-style strings where you have a pointer to a const char. You may change which string you point to but you can't change the content of these strings. This is important when the string itself is in the data segment of a program and shouldn't be changed.

bar is a constant or fixed pointer to a value that can be changed. This is like a reference without the extra syntactic sugar. Because of this fact, usually you would use a reference where you would use a T* const pointer unless you need to allow NULL pointers.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文