const与指针

本文使用gcc版本:
编译环境:gcc version 11.1.0

0. 引言

C/C++语言支持const关键字,const意为“常数,不变的”,C++中可用于定义真正的常量,但在C语言中使用const修饰的标识符并不是真正意义上的常量,为只读变量,本文讨论C语言const关键字的常见用法。

1. const定义的标识符不是常量

1.1 使用const修饰的标识符定义数组

const.c

#include <stdio.h>

int main()
{
    int arr[2] = {0};

    return 0;
}

以上代码段只在main函数中定义了一个长度为2的int数组,C规定数组在编译时必须确定长度,这里显式地用2这个字面常量定义数组长度,编译运行是没有问题的。

$ gcc const.c 
$ ./a.out 

如果使用const修饰的变量定义数组,编译是否通过?是编不过的!

    const int i = 2;
    int arr[i] = {0};//编译报错

程序在编译阶段就停止了,const修饰的标识符i在编译阶段无法确定其值,它不是个常量。

1.2 使用指针变量修改const修饰的“常量”

    const int i = 2;

    printf("after, i = %d\n", i);

    int *p = (int *)&i;

    *p = 3;

    printf("after, i = %d\n", i);

编译运行:

$ gcc const.c
$ ./a.out 
before, i = 2
after,  i = 3

可以看到使用const修饰的“常量”i的值被改变了。在当前上下文中,如果此时想直接通过赋值修改i的值,如:

i = 4;/* 编译报错,const修饰的变量量不能被赋值 */

编译:

const.c:15:7: error: assignment of read-only variable ‘i’
     i = 4;
       ^

编译是不能通过的,编译器提示这是个“只读变量”。

因此,C语言中使用const关键字修饰的标识符本质是只读变量,不能显式给const修饰的变量赋值,const关键字修饰的标识符并不是真正意义上的常量。

2. const关键字的常见声明

:个人觉得关于“常量指针”或“指针常量”或…………这些中文叫法不重要,who cares,可能由于翻译的问题,每个人的理解不同,关于这些叫法可能我本人是错的,关键在于写代码目前用的是英文,以英文去理解最准确,理解const用于声明中表示的意义,会用才是王道。

2.1 const int a

const int a = 1;/* 等价于int const a = 1; */

常用的只读变量的定义方式,不希望a被修改时添加const修饰。

2.2 const int *p

const(常量先)->*(指针后),按顺序就是“常量指针”,常量指针即常量的指针,强调指针,p指针(认为自己)指向的量是一个常量(在C里是只读变量)。(中文叫法可忽略)

int a = 1;
const int *p = &a;
  1. 从标识符p开始往左看,声明中有*号,则p为指针,p指向的类型为const intint const(等价);
  2. 标识符p旁边没有const关键字,则指针的指向可以被改变(可以对p赋值);
  3. const关键字贴近数据类型int,则指针指向的数据不可变(不能对*p赋值);

2.3 int * const p

*(指针先)->const(常量后),按顺序就是“指针常量”,指针常量强调常量,即p本身是常量,因此p不能赋值,p指针(认为自己)指向的是一个整形数的地址,但自己不能被改变。(中文叫法可忽略)

int a = 1;
int * const p = &a;
  1. 从标识符p开始往左看,声明中有*号,则p为指针;
  2. const关键字贴近标识符p,则指针的指向不可被改变(不能对p赋值);
  3. 数据类型int旁边没有const关键字,则指针指向的数据可以被改变(可以对*p赋值);
  4. 由于指针的指向不可被改变,p在定义时必须完成初始化,后续再对p赋值将编译失败。

2.4 const int * const p

int a = 1;
const int * const p = &a;
  1. 从标识符p开始往左看,声明中有*号,则p为指针;
  2. const关键字贴近标识符p,则指针的指向不可被改变(不能对p赋值);
  3. const关键字贴近数据类型int,则指针指向的数据不可被改变(不能对*p赋值);
  4. 由于指针的指向不可被改变,p在定义时必须完成初始化,后续再对p赋值将编译失败。

2.5 const int * const *pp

int a = 1;
const int * const p = &a;
const int * const *pp = &p;
  1. 从标识符pp开始往左看,声明中有*号,则pp为指针;
  2. 声明中有两个*号,则pp为指针的指针;
  3. const int * const *pp分为两部分:const int * const*pp,可以发现ppconst int * const类型的指针,而const int * const 类型本身就是个指针类型,这个类型指针指向的数据不可被改变指针的指向不可被改变
  4. const int * const为“指向常量的常量指针”类型 (中文叫法可忽略),则const int * const *为“指向常量的常量指针的指针”类型 (中文叫法可忽略)
  5. pp本身只是用于容纳const int * const类型的指针,因此pp本身不必须在定义时完成初始化,后续可以对pp赋值,pp的值为const int * const类型,因此不能对*pp赋值,也不可对**pp赋值。

2.6 const关键字修饰函数参数

 void *memcpy(void *dest, const void *src, size_t n);
  1. src标识符往左看,声明中有*号,因此src为指针;
  2. src标识符旁没有const关键字,则指针的指向可以被改变(可以对src赋值);
  3. const关键字贴近数据类型void *,则指针指向的数据不可被改变(不能对*src赋值);

从以上对memcpy函数形参src的分析中可知,使用const关键字修饰形参指针,可以保护源数据在函数运行过程中不被意外修改,从而增强程序的健壮性。

3. 总结

本文首先从两个方面论证了const关键字在C语言中只能定义只读变量,接着分析了几种C语言中常用的const声明,const和指针混在一起时,经常会混淆这些声明,基本上只要记住:声明中靠近const的量不可变(不能赋值) 这一原则去分析,就能分析出所以然:

const int a;			//const靠近int,a不能变
const int *p;			//const靠近int,p指向的内存数据不能变,p本身可以变
int * const p;			//const靠近p,p本身不能变,p指向的内存数据可以变
const int * const p;	//const既靠近int也靠近p,则p指向的内存数据不能变,p本身也不能变
const int * const *pp;	//是二级指针(简称套娃),指向const int * const类型,pp可以变,*pp和**pp都不能变
const int * const * const pp;//这时pp本身也不能变了,不过,正常人会写这个???
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值