常量指针 vs 常值指针

155

以下声明有什么区别?

char * const a;
const char * a;
为了理解差异,我编写了这个小程序:
#include <stdio.h>
#include <stdlib.h>


int main (int argc, char **argv)
{
    char a = 'x';
    char b = 'y';

    char * const pc1 = &a;
    const char * pc2 = &a;

    printf ("Before\n");
    printf ("pc1=%p\n", pc1);
    printf ("*pc1=%c\n", *pc1);
    printf ("pc2=%p\n", pc2);
    printf ("*pc2=%c\n", *pc2);

    *pc1 = b;
/*     pc1 = &b; */

/*     *pc2 = b; */
    pc2 = &b;

    printf ("\n\n");

    printf ("After\n");
    printf ("pc1=%p\n", pc1);
    printf ("*pc1=%c\n", *pc1);
    printf ("pc2=%p\n", pc2);
    printf ("*pc2=%c\n", *pc2);

    return EXIT_SUCCESS;
}

我使用gcc 3.4编译了程序并运行了它。输出很好地突出了差异:

Before
pc1=ffbfd7e7
*pc1=x
pc2=ffbfd7e7
*pc2=x


After
pc1=ffbfd7e7
*pc1=y
pc2=ffbfd7e6
*pc2=x

但是,我必须编写一个小程序来得到答案。假如我不在电脑旁(例如在面试时),我将无法回答这个问题。

请问有人能通过评论上述示例来解释一下 const 关键字的操作吗?


9
以下是更完整的答案,但我认为 const “绑定到下一个标记”。因此,在 char * const a 中,不能修改的是变量本身即 a。在 const char * a 中,不能修改的是所指向的字符。在C语言中,const 关键字用于声明常量或指针常量(指向常量)。当应用于指针类型时,它通常被放置在星号(*)之前或之后。如上所述,const 的位置决定了它所适用的内容。 在 char * const a 中,const 修饰的是 a ,表示 a 是一个指向字符数组的不可变指针,也就是说该指针不能再指向其他地址,但可以修改数组元素的值。而在 const char * a 中,const 修饰的是 *a 即指针所指向的字符,表示所指向的字符是不可修改的,指针本身可以指向其他地址。总之,const关键字用于指定变量或指针是否可以被修改,根据其位置不同对应的对象也不同。 - davmac
我认为标题应该写成“常量指针 vs 指向常量值的指针”。 - RBT
11个回答

190
char * const a;

意味着指针是常量且不可变的,但所指向的数据并非如此。
在这种情况下,您可以使用 const_cast (在C ++中)或c样式转换来消除constness,因为数据本身不是常量。

const char * a;

这意味着指向的数据不能使用指针a进行写入。 在这种情况下,使用const_cast(C++) 或 c风格的转换来消除常量性会导致未定义行为


63
const char * a并不意味着“所指向的数据是常量且不可改变”。它只是限制了指针 a 不能对其进行写操作,但是所指向的数据可以被其他人写入。例如:https://gist.github.com/andyli/b4107c8910208fe54764 - Andy Li
4
指定的数据有时可以由其他人编写,或者它可以是真正固定的,例如字符串文字。 - user207421

114

为解析复杂类型,您可以从变量开始,向左前进,并向外螺旋。如果没有任何需要担心的数组或函数(因为它们位于变量名的右侧),则这成为从右到左阅读的情况。

所以对于 char *const a;,您有一个指向 charconst 指针 (*),其名称为 a。换句话说,您可以更改 a 指向的 char,但无法使 a 指向其他内容。

相反,对于 const char* b;,您有一个指向 char 的指针 (*),该 char 是 const 的。您可以使 b 指向任何 char,但不能使用 *b = ...; 更改该 char 的值。

当然,您也可以同时拥有这两种 const 属性: const char *const c;


17
一流的回答。它不仅提供了答案,而且还教育读者如何解析这些声明类型。我也推荐使用http://cdecl.org/作为一个将声明转换成有意义的英文文本(例如,int (*(*foo)(void))[3]转换为“声明foo为指向函数(void)返回指向长度为3的整型数组的指针”)的网站。 - jarmod
我更喜欢在 char 后面放置 const: char const * const a; - Braden Best

66
char * const a;

*a 是可写的,但 a 不是;换句话说,你可以修改 a 指向的值,但你不能修改 a 本身。 a 是一个指向常量字符的常量指针。

const char * a; 

a是可写的,但*a不是;换句话说,你可以修改a(指向一个新位置),但你不能修改a所指向的值。

注意,这与以下内容相同:

char const * a;

在这种情况下,a 是一个指向 const char 的指针


28

现在您知道了char * const aconst char *a之间的区别。许多时候,我们会对它是常量指针还是指向常量变量的指针感到困惑。

如何阅读它?按照以下简单步骤来区分上面两个:

让我们看看如何阅读以下声明:

char * const a;

从右到左阅读

现在从a开始,

1. 紧邻a有一个const

char * (const a);

---> 因此,a是一个常量(????)

2. 沿着你会得到*

char (* (const a));

---> 因此,a是一个指向(????)常量指针

3. 继续往下,有char

(char (* (const a)));

---> a是一个指向字符变量常量指针

a is constant pointer to character variable. 

阅读起来很容易,不是吗?

第二个声明同样如此。

const char * a;

现在再从a开始,

1. a 旁边有一个 *

---> 所以 a 是一个指向 (????) 的 指针

2. 现在是 char

---> 所以 a 是一个 字符 指针

这没有任何意义!请交换 指针字符

---> 所以 a 是一个指向 (?????) 的 字符 指针

3. 现在又出现了 constant

---> 所以 a 是一个指向 常量 变量的 字符 指针

虽然你可以理解声明的含义,但让我们让它听起来更有意义。

a is pointer to constant character variable

很好的方式来澄清事情!:) - LordTitiKaka
1
这不是AAT答案的更冗长版本吗? - MestreLion
2
哦,我没看到。在我的工程课上,我的教授教过我这种方法。希望能对初学者有所帮助。 - Sagar Sakre

16

理解它们之间的区别最简单的方法是考虑不同的可能性。需要考虑两个对象:指针和被指向的对象(在这种情况下,“a”是指针的名称,被指向的对象是未命名的char类型)。可能性如下:

  1. 没有const
  2. 指针是const
  3. 被指向的对象是const
  4. 指针和被指向的对象都是const。

在C语言中,可以如下表达这些不同的可能性:

  1. char * a;
  2. char * const a;
  3. const char * a;
  4. const char * const a;

希望这能说明可能的区别。


12
第一个是指向字符的常量指针,第二个是指向常量字符的指针。你的代码没有涵盖所有情况:
char * const pc1 = &a; /* You can't make pc1 point to anything else */
const char * pc2 = &a; /* You can't dereference pc2 to write. */

*pc1 = 'c' /* Legal. */
*pc2 = 'c' /* Illegal. */

pc1 = &b; /* Illegal, pc1 is a constant pointer. */
pc2 = &b; /* Legal, pc2 itself is not constant. */

谢谢您的解释,但我已经得出了这些结果(如我在问题中粘贴的代码所示)。我正在寻找有人能够解释const运算符的工作原理以及如何解密受限实体,而无法在机器上运行代码进行测试。 - rahmu

6
我将首先口头解释,然后再举例说明:
指针对象可以声明为const指针或指向const对象的指针(或两者兼备):
const指针不能被重新分配以指向与其最初分配的对象不同的对象,但它可以用于修改其所指向的对象(称为“指针所指对象”)。因此,引用变量是const指针的另一种语法。
另一方面,指向const对象的指针可以被重新分配以指向相同类型或可转换类型的另一个对象,但不能用于修改任何对象。
还可以声明const指针指向const对象,既不能用于修改指针所指对象,也不能被重新分配以指向其他对象。
例如:
void Foo( int * ptr,
         int const * ptrToConst,
         int * const constPtr,
         int const * const constPtrToConst ) 
{ 
    *ptr = 0; // OK: modifies the "pointee" data 
    ptr = 0; // OK: modifies the pointer 

    *ptrToConst = 0; // Error! Cannot modify the "pointee" data
     ptrToConst = 0; // OK: modifies the pointer 

    *constPtr = 0; // OK: modifies the "pointee" data 
    constPtr = 0; // Error! Cannot modify the pointer 

    *constPtrToConst = 0; // Error! Cannot modify the "pointee" data 
    constPtrToConst = 0; // Error! Cannot modify the pointer 
}

很高兴为您提供帮助!祝您好运!


5
上面有很好的答案。这里有一个简单的记忆方法:
a是指针
*a是值
现在,如果你说“const a”,那么指针就是const。(即char * const a;)
如果你说“const *a”,那么值就是const。(即const char * a;)

2
您可以使用cdecl实用程序或其在线版本,例如https://cdecl.org/
例如: void(* x)(int(* [])()); 是一个 声明x为指向函数的指针(返回int的函数指针数组)返回void

1
尝试简单回答:
char * const a;  => a is (const) constant (*) pointer of type char {L <- R}. =>( Constant Pointer )
const char * a;  => a is (*) pointer to char constant             {L <- R}. =>( Pointer to Constant)

常量指针:

指针是常量!也就是说,它所持有的地址不能被改变。它将被存储在只读内存中。

让我们尝试更改指针的地址以了解更多:

char * const a = &b; 
char c;
a = &c; // illegal , you can't change the address. `a` is const at L-value, so can't change. `a` is read-only variable.

它意味着一旦常量指针指向某个东西,它就永远指向该东西。

指针a只指向b

但是,您可以更改b的值,例如:

char b='a';
char * const a =&b;

printf("\n print a  : [%c]\n",*a);
*a = 'c';
printf("\n now print a  : [%c]\n",*a);

指向常量的指针:

指针所指向的值不能被修改。

const char *a;
char b = 'b';
const char * a =&b;
char c;
a=&c; //legal

*a = 'c'; // illegal , *a is pointer to constant can't change!.

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