const 修饰指针的细节

前言

文章内容为《C 专家编程》一书的阅读笔记。希望可以帮助大家解决指针赋值和 const 方面的问题。

指针的赋值

问题

将一个类型为 char** 的值赋值给一个 const char** 类型的对象是否合法呢?

先说结果,在 vs 2022 的环境下,「初始化」:无法从 “char **” 转换为 “const char **”。

gcc 8.3.1 编译会出现以下警告:warning: initialization of ‘const char **’ from incompatible pointer type ‘char **’ 。

为什么不能赋值呢,下面就让我们一步步探索这其中的奥妙。

ANSI C 有关简单赋值的标准

要使上述的赋值形式合法,必须满足下列条件:

  1. 两个操作数都是指向有限定符或无限定符的相容类型的指针

  2. 左边指针所指向的类型必须具有右边指针所指向类型的全部限定符

还有一个关于类型的说明:const float* 类型不是一个有限定符的类型——它的类型是指向一个具有 const 限定符的 float 类型的指针,也就是说 const 限定符是修饰指针所指向的类型,而不是指针本身。

问题解决

在解决问题之前,我们先来看一组简单的。

char* 和 const char*

char*const char* 是匹配的。

它之所以合法,是因为在下面的代码中:

char c = 'q';
char* cp = &c;
const char* cpp = NULL;
cpp = cp; 
  • 左操作数是一个指向 char 类型的指针
    • const 限定符修饰的 char
  • 右操作数是一个指向 char 类型的指针
    • 没有限定符修饰的 char
  • char 类型与 char 类型是相容的,左操作数所指向的类型具有右操作数所指向类型的限定符

注意,反过来就不能进行赋值。

char** 和 const char**

由上面的知识我们可以得知,char**const char** 都是没有限定符的指针类型,但他们的指向的类型不一样(前者指向 char*,后者指向 const char*),这违反了上面赋值标准的第一条,所以它们是不相容的。

用这种方式理解这个有一点困难,可以用下面这种方法进行理解:

char c = 'q';
char* cp = &c;
char** pp1 = &cp;
const char** pp2 = NULL;
pp2 = pp1;
  • 左操作数的类型是 const char**,它是一个指向 const char* 类型的指针
    • const char* 是一个没有限定符的指针,它指向一个带有 const 限定的 char 类型
  • 右操作数的类型是 char**,它是一个指向 char* 的指针
    • char* 是一个没有限定符的指针,它指向一个没有限定符的 char 类型

const char*char* 是相容的,而且他们本身没有限定符,所以符合标准的约束条件,两者之间的赋值是合法的。

const char**char** 之间的关系又有不同,虽然二者都没有限定符,但二者所指向的对象类型不相容,所以不能进行赋值。

const 修饰

const 修饰变量

首先,关键字 const 并不能把变量变成常量。在一个变量前加上 const 限定符只是表示这个变量不能被赋值。也就是说 const 修饰的变量是只读的,不可以被直接修改,但它不能防止被间接修改。例如:

#include <stdio.h>

int main() {
  const int i = 10;
  int* p = (int*)&i;
  
  printf("before:%d\n", i);
  *p = 20;
  // 这里打印值变成了 20,说明可以间接修改
  printf("after:%d\n", *p);
  return 0;
}

const 修饰指针

const 修饰指针变量有多种位置,下面我们将逐个介绍。

const int* p

注:const int* pint const *p 写法不同,作用是一样的。

这种写法的意思是:const 修饰 *p,不能通过解引用(*p)的方式直接修改所指向的变量,但可以通过改变指针指向的方式来修改 *p。例如:

#include <stdio.h>

int main() {
  // 通过下方直接解引用的方式来修改编译器会直接报错
  // int i = 10;
  // const int* p = &i;
  // *p = 20;
  
  int i = 10;
  const int* p = &i;
  
  printf("before:%d\n", *p);
  int j = 20;
  // 通过这样改变 p 的指向,可以间接修改 *p 值
  p = &j;
  printf("after:%d\n", *p);
  return 0;
}

int* const p

这种写法的意思是:const 修饰 p,不能通过改变指针指向的方式修改 *p 的值,但可以通过解引用(*p)的方式直接修改所指向的变量。例如:

#include <stdio.h>

int main() {
  int i = 10;
  int* const p = &i;
  
  printf("before:%d\n", *p);
  // 不能改变 p 的指向,但可以直接解引用修改值
  *p = 20;	
  printf("after:%d\n", *p);
  return 0;
}

const int* const p

这种写法是同时修饰 p 和 *p,既不能改变 p 的指向,也不能用解引用直接修改。

  • 44
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值