const char * , char const * , char * const 的区别

把一个 const 声明从右向左读

Posted by Edger on January 20, 2019

本文转载自菜鸟教程 【const char * , char const * , char * const 的区别】,并修正部分错误。

const char * , char const * , char * const 有什么区别?它们的含义分别是什么?这是我们常常搞混的三种表示。

关于这三种表示的含义,Bjarne 在他的 The C++ Programming Language 里面给出过一个助记的方法:

“把一个声明从右向左读”

1
2
3
4
5
6
7
8
char * const cp; // ( * 读成 pointer to ) 
// cp is a const pointer to char 

const char * p; 
// p is a pointer to const char; 

char const * p; 
// 同上,因为 C++ 里面没有 const * 的运算符,所以 const 只能属于前面的类型。

C++ 标准规定,**const 关键字放在类型或变量名之前和之后是等价的 **。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const int n = 5;    //same as below
int const m = 10;

const int *p;    //same as below  const (int) * p
int const *q;    // (int) const *p

char ** p1; 
// pointer to    pointer to    char 

const char **p2;
// pointer to    pointer to    const char 

char * const * p3;
// pointer to    const pointer to    char 

const char * const * p4;
// pointer to    const pointer to    const char 

char ** const p5;
// const pointer to    pointer to    char 

const char ** const p6;
// const pointer to    pointer to    const char 

char * const * const p7;
// const pointer to    const pointer to    char 

const char * const * const p8;
// const pointer to    const pointer to    const char

说到这里,我们可以看一道以前 Google 的笔试题:

1
2
const char *p = "hello";       
foo(&p);  // 函数 foo(const char **pp) 下面说法正确的是[]
  • A. 函数 foo() 不能改变 p 指向的字符串内容。
  • B. 函数 foo() 不能使指针 p 指向 malloc 生成的地址。
  • C. 函数 foo() 可以使 p 指向新的字符串常量。
  • D. 函数 foo() 可以把 p 赋值为 NULL。

至于这道题的答案是众说纷纭。针对上面这道题,我们可以用下面的程序测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
using namespace std;

void foo(const char **pp)
{
    // *pp = NULL;
    // *pp = "Hello world!";
    *pp = (char *)malloc(10);
    // snprintf(*pp, 10, "hi google!"); // argument of type "const char *" 
                                        // is incompatible with parameter 
                                        // of type "char *"
    // (*pp)[1] = 'x'; // expression must be a modifiable lvalue
}

int main()
{
    const char *p = "hello";
    printf("before foo: %s\n", p);
    foo(&p);
    // free(p); // argument of type "const char *" is 
                // incompatible with parameter of type "void *"
    printf("after foo: %s\n", p);
    // p[1] = 'x'; // expression must be a modifiable lvalue
    return 0;
}

结论如下:

  1. foo 函数中,可以使 main 函数中 p 指向的新的字符串常量。
  2. foo 函数中,可以使 main 函数中的 p 指向 NULL
  3. foo 函数中,可以使 main 函数中的 p 指向由 malloc 函数生成的内存块,不可以并可以main 中用 free 释放,但是会有警告但是注意,即使在 foo 中让 p 指向了由 malloc 生成的内存块,但是仍旧不能用 p[1]=’x’; 这样的语句改变 p 指向的内容。
  4. 在 foo 中,不能用 (*pp)[1]=’x’; 这样的语句改变 p 的内容。

所以,感觉 gcc 只是根据 const 的字面的意思对其作了限制,即对于 const char *p 这样的指针,不管后来 p 实际指向 malloc 的内存或者常量的内存,均不能用 p[1]=’x’; 这样的语句改变其内容。但是很奇怪,在 foo 里面,对 p 指向 malloc 的内存后,可以用 snprintf 之类的函数修改其内容。