char * const和const char *有什么区别?

366

两者之间有何不同:

char * const 

而且

const char *

4
可能是const int*, const int * const, 和 int const * 有什么不同?的重复问题。请注意,这个链接指向英语的stackoverflow网站。 - emlai
15
在“const”左侧的第一件事情是常量。如果“const”是最左边的东西,那么它右边的第一件事就是常量。 - Cupcake
9
友情提示,永远不要忘记cdecl这个工具。 - Braden Best
1
还有另一个char const*,它是exception::what()的返回类型。 - Zhang
19个回答

458

const char *是指向const char的指针,而char * const是指向char的常量指针。

第一种情况下,指针所指向的值不能被更改,但可以改变指针本身。第二种情况下,指针所指向的值可以被更改,但指针本身无法修改(类似于引用)。

还有一个

const char * const

这是一个指向常量字符的常量指针(因此它所指向的任何内容均不可更改)。

注意:

以下两种形式是等效的:

const char *

并且

char const *

具体原因在C++标准中有所描述,但重要的是要注意和避免混淆。我知道有几种编码标准更喜欢:

char const

结束

const char

创建一个带或不带指针的函数,以便const元素的放置方式与使用指针const时相同。


9
如果在同一个声明中指定多个变量,是否值得注意其发生的情况?我相信 const int *foo,*bar; 会将 foobar 都声明为 int const *。但是 int const *foo,*bar 则会将 foo 声明为 int const *bar 声明为 int *。我认为 typedef int * intptr; const intptr foo, bar; 会将这两个变量都声明为 int * const。我不知道有没有办法使用组合声明来创建两个这种类型的变量,除非使用 typedef。 - supercat
2
@supercat “我认为const int foo,bar;会声明foo和bar都是int const *”:是的。“但是int const foo,bar会声明foo为int const *,bar为int *”:不!它与前面的情况完全相同(请参见http://ideone.com/RsaB7n,在那里你会得到foo和bar的相同错误)。 “我认为typedef int * intptr; const intptr foo,bar;会声明两个变量都是int * const”:是的。“我不知道有什么方法可以使用组合声明来创建两个该类型的变量而不使用typedef”:好吧,“int const foo,const bar”。C声明符号语法... - gx_
@gx_: 所以我错了——我的不确定性是为什么我建议说出规则可能有帮助的原因。int const *foo, *volatile barbar 会产生什么影响?使它既成为 const 又成为 volatile 吗?我想念 Pascal 中声明变量名和类型之间的清晰分离(指向整数指针数组的指针将是 var foo: ^Array[3..4] of ^Integer; 在 C 中,这将是一些有趣的嵌套括号的事情,我想。 - supercat
4
这段话主要讲述了C语言中声明语法的规则,由类型部分和声明符组成。在"int const *foo, *volatile bar"中,类型部分是int const(在*之前停止),而声明符包括*foo(表达式*foo表示一个int const类型)和*volatile bar。按照从右到左的顺序来阅读(对于_限定词_,这是个好规则),foo是一个指向常量整数的指针,而bar是一个指向常量整数的_volatile_指针(指针本身是_volatile_的,所指向的整数是[作为]常量访问的)。 - gx_
1
@supercat 至于“指向整数指针数组的指针”(我不知道 Pascal,也不确定 [3..4] 语法,所以我们来看一个有 10 个元素的数组): int *(*foo)[10];。它反映了它(未来)作为表达式的使用方式:*(*foo)[i](其中 i 是范围为 [0, 10)[0, 9] 的整数)将首先对 foo 进行反引用以访问数组,然后访问索引 i 处的元素(因为后缀 [] 比前缀 * 绑定更紧密),然后再对此元素进行反引用,最终得到一个 int(请参见 http://ideone.com/jgjIjR)。但是 typedef 使其变得更容易(请参见 http://ideone.com/O3wb7d)。 - gx_
显示剩余5条评论

141
为了避免混淆,请始终追加const限定符。
int       *      mutable_pointer_to_mutable_int;
int const *      mutable_pointer_to_constant_int;
int       *const constant_pointer_to_mutable_int;
int const *const constant_pointer_to_constant_int;

12
为什么?“为避免混淆”并没有向我解释混淆是什么。 - Andrew Weir
25
@Andrew: 我在暗示一致性和易读性。我通常使用将全部类型限定符都写在它们左边的方式,以便对其进行修改。 - diapir
3
这实际上是我在SO上发现的关于这个主题最好的答案。 - Trap
8
作为一种代码规范,我很少遇到这种风格,因此不太可能采用它。但是作为一种学习工具,这个回答非常有帮助!(所以我猜这种风格不太常见,有点可惜。) - natevw
11
@Alla说:p与类型(const int *const)无关。无论好坏(在我看来更糟),C和C++中的const限定符都是后缀,例如const成员函数void foo(int a) const;。声明const int的可能性是例外而不是规则。 - diapir
显示剩余7条评论

63

const始终修改它前面的内容(在它左侧),除非它是类型声明中的第一项,那么它将修改它后面的内容(在它右侧)。

所以这两个是相同的:

int const *i1;
const int *i2;

它们定义了指向 const int 的指针。你可以改变 i1i2 指向的位置,但是不能改变它们所指向的值。

这样做:

int *const i3 = (int*) 0x12345678;

定义了一个指向整数的const指针,并将其初始化为指向内存位置12345678。你可以更改地址为12345678的int值,但是无法更改i3指向的地址。


28

经验法则:从右往左读定义!


const int *foo;

表示 "foo 指向(*)一个不能改变的 int 值"。
对程序员来说,这意味着 "我不会改变 foo 所指向的值"。

  • 执行 *foo = 123;foo[0] = 123; 将是无效的。
  • 执行 foo = &bar; 是允许的。

int *const foo;

表示 "foo 不能改变 (const),且指向 (*) 一个 int 值"。
对程序员来说,这意味着 "我不会改变 foo 所引用的内存地址"。

  • 执行 *foo = 123;foo[0] = 123; 是允许的。
  • 执行 foo = &bar; 将是无效的。

const int *const foo;

表示 "foo 不能改变 (const),且指向 (*) 一个不能改变的 int 值"。
对程序员来说,这意味着 "我不会改变 foo 所指向的值,也不会改变 foo 引用的内存地址"。

  • 执行 *foo = 123;foo[0] = 123; 将是无效的。
  • 执行 foo = &bar; 将是无效的。

27

const char* 是指向常量字符的指针
char* const 是指向字符的常量指针
const char* const 是指向常量字符的常量指针


23

12
  1. const char* x:这里的X基本上是字符指针,指向一个常量值。

  2. char* const x:是指指向一个常量的字符指针,但它所指的位置可以改变。

  3. const char* const x:结合了1和2的含义,表示一个指向常量值的常量字符指针。

  4. const *char x:会导致编译错误,不能声明。

  5. char const * x:与第一条相等。

经验法则是,如果const与变量名一起使用,则指针将是常量,但指向的位置可以更改;否则指针将指向常量位置,指针可以指向另一个位置,但指向的位置内容不能更改。


2
"char* const x" 指的是一个字符指针,它是常量,但它所指向的位置可以改变。不对,位置本身不能改变,只有位置上的值可以改变。 - PleaseHelp

7

另一个规则是检查 const 的位置:

  1. *之前 => 存储的常量
  2. *之后 => 指针本身是常量

4
很多答案提供了具体的技巧、经验法则等来理解变量声明。但是有一种通用的技巧可以理解任何声明:

顺时针/螺旋规则

A)

const char *a;

根据顺时针/螺旋规则,a指向常量字符。这意味着字符是常量,但指针可以改变。即a = "other string";是可以的,但a[2] = 'c';将无法编译。
B)
char * const a;

根据规则,a是指向字符的const指针。也就是说,你可以执行a[2] = 'c';,但不能执行a = "other string";

2
也被称为右左规则(至少这是我学习的方式): http://jdurrett.ba.ttu.edu/3345/handouts/RL-rule.html - Tomas Pruzina
1
(最好不要隐藏答案的本质在链接后面,这里的文本甚至没有引用或至少提到任何具体细节,只是一个通用的“按照规则”) - Sz.
@Sz,你有什么具体的疑问需要我解答吗?一旦了解了规则,实际上并没有太多需要注意的地方。 - JamesWebbTelescopeAlien

4
第一个错误是语法错误。也许你是想表达以下两者之间的差异:
const char * mychar

并且

char * const mychar

在这种情况下,第一个是指向不可更改数据的指针,而第二个则是始终指向相同地址的指针。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接