详解C语言中的常量指针和指针常量

概述
对于新手来说,指针在c语言里总是一个非常难以理解的概念。在这篇文章中,我们将解释常量指针,指针常量,const pointer to const(ps:楼主以为这可以翻译成指向常量的常量指针)的区别

常量指针
让我们先来理解什么是常量指针。常量指针是指指针指向的地址是常量。换句话说,一旦常量指针指向了一个变量,你不能让该常量指针指向其他变量了

常量指针的声明方法如下:

  <type of pointer> * const <name of pointer>

常量指针声明示例:

  int * const ptr;

让我们用一小段代码来说明常量指针:

  #include <stdio.h> 

  int main(void)
  {
    int var1 = 0, var2 = 0; 

    int * const ptr = &var1; 

    ptr = &var2; 

    printf("%d\n", *ptr); 

    return 0;
  }

在上面的例子中:

  • 我们定义了两个变量var1和var2
  • 声明一个常量指针ptr,并让其指向var1
  • 然后,我们让ptr指向var2
  • 最后,我们试图打印这个指针指向的内容

简而言之,我们定义了一个常量指针,并且试图修改常量指针指向的地址

现在,让我们来编译这个程序:

所以,一旦常量指针指向了某一地址,我们不能更改常量指针的地址(ps:但是可以修改常量指针指向的内容)

指针常量
从名字中就可以明显得出,一个指针,我们无法修改指针指向的内容,这种指针就叫做指针常量。对于这类指针,你可以修改指针指向的地址,但是不能修改指针指向的内容

指针常量的定义如下:

  const <type of pointer> * <name of pointer>

或者

  <type of pointer> const * <name of pointer>

指针常量示例如下:

  const int *ptr; 

  int const *ptr;

让我们用一小段代码来解释指针常量:

#include <stdio.h> 

  int main(void)
  {
    int var1 = 0; 

    const int *ptr = &var1; 

    *ptr = 2; 

    printf("%d\n", *ptr); 

    return 0;
  }

在上面代码里:

  • 我们定义了一个变量var1,并将其赋值为0
  • 我们定义了一个指针常量并让它指向变量var1
  • 我们试图修改指针常量指向的值
  • 打印指针常量指向的值

然后我们编译上面的程序:

我们可以看到*ptr是只读属性。这意味着,如果指针常量指向了一个变量,我们不能修改该指针常量指向的值

指向常量的常量指针
如果你理解了上面两种指针类型,那作为上述两种指针的混合形态,你也应该非常好理解。指向常量的常量指针,你既不能修改指针的地址,也不能修改指针指向的内容

指向常量的常量指针定义如下:

  const <type of pointer> * const <name of pointer>

示例:

  const int * const ptr;

让我们写一小段代码来理解指向常量的常量指针:

  #include <stdio.h> 

  int main(void)
  {
    int var1 = 0, var2 = 0; 

    const int * const ptr = &var1; 

    *ptr = 1; 

    ptr = &var2; 

    printf("%d\n", *ptr); 

    return 0;
  }

在上面代码中:

  • 我们声明了两个变量var1和var2
  • 我们声明了一个指向常量的常量指针ptr,并让ptr指向变量var1
  • 我们试图修改指针的地址和指针指向的内容

当我们编译这段代码的时候:

而C/C++中常把指针和常量混合起来使用,其最大的用途就是作为函数的形式参数,保证实参在被调函数中的不可改变的特性,那到底常量指针和指针常量有什么区别呢?

总结
好了,现在来总结一下 常量指针 和 指针常量 的区别

首先一定要明白哪种定义方式是常量指针,哪种是指针常量,这里可以记住三句话加深记忆:

* (指针)和 const(常量) 谁在前先读谁 ;*象征着地址,const象征着内容;谁在前面谁就不允许改变。

好吧,让我们来看这个例子:

int a =3;
int b = 1;
int c = 2;
int const *p1 = &b;//const 在前,定义为常量指针
int *const p2 = &c;//*在前,定义为指针常量

常量指针p1:指向的地址可以变,但内容不可以重新赋值,内容的改变只能通过修改地址指向后变换。

  • p1 = &a是正确的,但 *p1 = a是错误的。
  • 指针常量p2:指向的地址不可以重新赋值,但内容可以改变,必须初始化,地址跟随一生。
  • p2= &a是错误的,而*p2 = a 是正确的。
  • 对于常量指针p1,我们可以改变它指向的地址,但不能改变指向的内容,如果改变了,就会出错,下面是18行代码取消注释后编译器提示的错误:

下面是在Vim编辑器中的调试结果

上述代码在注释 18行 和 24行 代码后才能正确输出,下图是正确结果

输出结果可以看出,对于常量指针p1,改变其地址指向,内容也随着地址的改变而变化了。

而对于指针常量p2,初始化后地址就固定了,内容可以随时重新赋值。

对于常量指针p1,我们可以改变它指向的地址,但不能改变指向的内容,如果改变了,就会出错,下面是18行代码取消注释后编译器提示的错误:

输出结果可以看出,对于常量指针p1,改变其地址指向,内容也随着地址的改变而变化了。

而对于指针常量p2,初始化后地址就固定了,内容可以随时重新赋值。

对于常量指针p1,我们可以改变它指向的地址,但不能改变指向的内容,如果改变了,就会出错,下面是18行代码取消注释后编译器提示的错误:


经过上面的介绍,我想大家应该知道常量指针和指针常量的区别了。

(0)

相关推荐

  • 详解C语言中的符号常量、变量与算术表达式

    C语言中的符号常量 在结束讨论温度转换程序前,我们再来看一下符号常量.在程序中使用 300.20 等类似的"幻数"并不是一个好习惯,它们几乎无法向以后阅读该程序的人提供什么信息,而且使程序的修改变得更加困难.处理这种幻数的一种方法是赋予它们有意义的名字.#define 指令可以把符号名(或称为符号常量)定义为一个特定的字符串: #define 名字 替换文本 在该定义之后,程序中出现的所有在 #define 中定义的名字(既没有用引号引起来,也不是其它名字的一部分)都将用相应的替换文本

  • C语言中判断int,long型等变量是否赋值的方法详解

    当然,如果你不赋值给局部变量,这样会导致整个程序的崩溃,因为,它的内容被系统指向了垃圾内存.下面我们看一段代码: 复制代码 代码如下: #include <stdio.h>#include <string.h>#include <stdlib.h>int globle_value;int my_sum(int value1, int value2);long my_sub(long value1, long value2);int main(void){ int aut

  • C语言 变量详解及示例代码

    C 变量 变量其实只不过是程序可操作的存储区的名称.C 中每个变量都有特定的类型,类型决定了变量存储的大小和布局,该范围内的值都可以存储在内存中,运算符可应用于变量上. 变量的名称可以由字母.数字和下划线字符组成.它必须以字母或下划线开头.大写字母和小写字母是不同的,因为 C 是大小写敏感的.基于前一章讲解的基本类型,有以下几种基本的变量类型: 类型 描述 char 通常是一个八位字节(一个字节).这是一个整数类型. int 对机器而言,整数的最自然的大小. float 单精度浮点值. doub

  • c语言全局变量和局部变量问题及解决汇总

    1.局部变量能否和全局变量重名? 答:能,局部会屏蔽全局.要用全局变量,需要使用"::" 局部变量可以与全局变量同名,在函数内引用这个变量时,会用到同名的局部变量,而不会用到全局变量.对于有些编译器而言,在同一个函数内可以定义多个同名的局部变量,比如在两个循环体内都定义一个同名的局部变量,而那个局部变量的作用域就在那个循环体内. 2.如何引用一个已经定义过的全局变量? 答:extern 可以用引用头文件的方式,也可以用extern关键字,如果用引用头文件方式来引用某个在头文件中声明的全

  • C语言 常量,变量及数据详细介绍

    一.数据 图片文字等都是数据,在计算机中以0和1存储. (一)分类 数据分为静态数据和动态数据. ①. 静态数据:一些永久性的的数据,一般存储在硬盘中,只要硬盘没坏数据都是存在的.一般以文件的形式存储在硬盘上,电脑关机重启后依然存在. ②. 动态数据:程序运行过程中,动态产生的的临时数据,一般存储在内存中,内存的存储空间一般较小,计算机关闭后这些数据就会被清除.软件或者电脑关闭则这些临时数据会被清除. ③. 静态数据和动态数据可以转换. ④. 注意:为什么不把动态数据存放到硬盘?因为直接访问内存

  • 解析C语言中空指针、空指针常量、NULL & 0的详解

    什么是空指针常量(null pointer constant)?[6.3.2.3-3] An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant. 这里告诉我们:0.0L.'\0'.3 - 3.0 * 17 (它们都是"integer constant expression")以及 (void*

  • C语言中常量指针与指针常量区别浅析

    常量指针是指--指向常量的指针,顾名思义,就是指针指向的是常量,即,它不能指向变量,它指向的内容不能被改变,不能通过指针来修改它指向的内容,但是指针自身不是常量,它自身的值可以改变,从而指向另一个常量.指针常量是指--指针本身是常量.它指向的地址是不可改变的,但地址里的内容可以通过指针改变.它指向的地址将伴其一生,直到生命周期结束.有一点需要注意的是,指针常量在定义时必须同时赋初值.注:也有人将这两个名称的定义与含义反过来认为:"指针常量:顾名思义它的中心词是"常量"这是重点

  • 详解C语言中二级指针与链表的应用

    目录 前言 二级指针讲解 链表的应用 定义双链表的结构体 创建双链表 前言 这篇文章即将解决你看不懂或者不会写链表的基本操作的问题,对于初学者而言,有很多地方肯定是费解的.比如函数的参数列表的多样化,动态分配内存空间函数malloc等,其实这些知识和指针联系紧密,尤其是二级指针.那么开始好好的学习这篇文章吧! 二级指针讲解 简述:其实就是一个指针指向另一个指针的地址. 我们都知道指针指向地址,但是指针自身也是一个变量,当然也可以被二级指针所指向. 语法:形如 int x = 10; int *q

  • 详解R语言中的表达式、数学公式、特殊符号

      在R语言的绘图函数中,如果文本参数是合法的R语言表达式,那么这个表达式就被用Tex类似的规则进行文本格式化. y <- function(x) (exp(-(x^2)/2))/sqrt(2*pi) plot(y, -5, 5, main = expression(f(x) == frac(1,sqrt(2*pi))*e^(-frac(x^2,2))), lwd = 3, col = "blue") library(ggplot2) x <- seq(0, 2*pi, b

  • 详解Go语言中的数据类型及类型转换

    目录 1.基本数据类型 2.基础数据类型转换 3.基本数据类型转为字符串 4.strconv的使用 5.字符串转为基础类型 1.基本数据类型 数据类型有很多,先研究一下基础的,例如:布尔型.数字类型.字符串类型. 数字类型有uint8.uint16.uint32.uint64.int8.int16.int32.int64(uint和int区别在于uint为无符号整数,即只支持正数,不支持负数形式) 数字浮点型有fload32.float64.complex64.complex126(后面两个均为

  • 详解Go语言中泛型的实现原理与使用

    目录 前言 问题 解决方法 类型约束 重获类型安全 泛型使用场景 性能 虚拟方法表 单态化 Go 的实现 结论 前言 原文:A gentle introduction to generics in Go byDominik Braun 万俊峰Kevin:我看了觉得文章非常简单易懂,就征求了作者同意,翻译出来给大家分享一下. 本文是对泛型的基本思想及其在 Go 中的实现的一个比较容易理解的介绍,同时也是对围绕泛型的各种性能讨论的简单总结.首先,我们来看看泛型所解决的核心问题. 问题 假设我们想实现

  • 详解Go语言中配置文件使用与日志配置

    目录 项目结构调整 配置文件使用 日志配置 小结 接着上一篇的文章构建的项目:Go语学习笔记 - 环境安装.接口测试 只是简单的把GET和POST接口的使用测试了一下. 我还是想按照正常的项目结构调整一下,这篇笔记主要是三个部分:调整项目目录结构.增加配置文件使用.增加日志配置,很常规而且也是每个项目都需要用到的. 项目地址:github地址 项目结构调整 说先对项目目录结构调整一下,按照我自己的开发习惯,增加了几个目录. 项目结构如下图: 解释一下目录结构 app/constants:主要放置

  • 详解C语言中双向循环链表的实现

    目录 实现细节 辅助理解图 具体实现代码 1.对链表进行初始化 2.任意位置前的插入 3.任意位置的删除 4.头插和尾删 完整代码 头文件 具体函数 测试 实现细节 1.带一个哨兵位(哨兵节点,初始节点,不存储有效数据,用来方便后期数据的存储与查找) 2.与单向链表不同的是,双向链表中每个数据节点包含两个指针,分别指向前后两个节点 3.双向链表是循环的,其尾节点后不是空指针,而是与头部的哨兵节点通过指针相连 辅助理解图 具体实现代码 1.对链表进行初始化 初始化:哨兵位的前后指针均指向哨兵节点本

  • 详解C语言中结构体的使用

    目录 结构体的声明 结构体成员的类型 结构体成员的访问 结构体的声明 结构体的定义:结构体是一些值的集合,这些值称为成员变量,结构体的每个成员可以是不同类型的变量. 举例: //定义结构体类型 struct tag//struct结构体关键字 tag结构体标签 struct tag结构体类型 { //成员变量 char name[20]; short age; char telphone[12]; char sex[5]; }s1,s2,s3;//s1,s2,s3是三个全局结构体变量 int m

  • 详解C语言中动态内存管理及柔性数组的使用

    目录 一.malloc 二.free 三.calloc 四.realloc 1.realloc在扩容时的情况 2.realloc也能实现malloc功能 五.使用动态内存的常见错误 1.free空指针 2.对动态开辟的空间越界访问 3.对非动态开辟内容free 4.只free动态开辟空间的一部分 5.对同一块内存多次free 6.动态内存空间忘记释放(内存泄漏) 六.柔性数组 1.柔性数组的概念 2.柔性数组的特点 3.柔性数组的使用场景 4.柔性数组的优点 一.malloc 这个函数向堆区申请

  • 一文详解C语言中文件相关函数的使用

    目录 一.文件和流 1.程序文件 2.数据文件 3.流 二.文件组成 三.文件的打开和关闭 1.文件的打开fopen 2.文件关闭fclose 四.文件的顺序读写 1.使用fputc和fgetc写入/读取单个字符 2.使用fputs和fgets写入/读取一串字符 3.使用fprintf和fscanf按照指定的格式写入/读取 4.使用fwrite和fread按照二进制的方式写入/读取 5.使用sprintf和sscanf将格式化数据和字符串互相转换(文件无关) 五.文件的随机读写 1.fseek(

  • 详解C语言中双指针算法的使用

    目录 前言 一.最长不含重复字符的子字符串 1.题目要求 2.个人题解 二.和为S的两个数字 1.题目要求 2.个人题解 前言 双指针算法 算法思想 双指针,指的是在遍历对象的过程中,不是普通的使用单个指针进行访问,而是使用两个相同方向(快慢指针)或者相反方向(对撞指针)的指针进行扫描,从而达到相应的目的. 换言之,双指针法充分使用了数组有序这一特征,从而在某些情况下能够简化一些运算. 今天带大家来学习算法中双指针的应用场景. 一.最长不含重复字符的子字符串 1.题目要求 2.个人题解 2.1

随机推荐