C语言:指针的基础详解
目录
1. 内存
2. 取地址&
3. 指针变量
4. 解引用
4.1 *解引用
4.2 []解引用
4.3 ->解引用
5. 指针变量的大小
5.1 结论
6. 指针运算
7. void* 指针
8. const修饰指针
8.1 const修饰变量
8.2 const修饰指针变量
8.3 结论
9. 野指针
9.1 为什么会出现野指针?
9.1.1 指针未初始化
9.1.2 指针越界访问
9.1.3 指针指向的空间释放
9.2 如何规避野指针
10. assert断言
11. 指针的作用
1. 内存
我们都知道,手机,电脑里都是有内存的,电脑CPU在处理数据时,需要的数据从内存中读取,处理后再放回内存中
每个内存单元为1个字节
具体的换算单位如下:
1byte = 8bit
1KB = 1024byte
1MB = 1024KB
1GB = 1024MB
1TB = 1024GB
1PB = 1024TB
CPU为了更好的处理数据,找到这个数据,就需要有地址,有了地址才能找到所需要的数据
每个字节都有属于自己的名字(地址),有了这个名字才能更好的找到它
那我们如何获得地址呢?
2. 取地址&
&符号当然可以取地址了
int a = 10;
printf("%p\n", &a);
//%p是打印地址的
我们知道 int 有4个字节,这4个字节都有地址

如上图中,如果a是占这4个字节
那么打印出来的地址将会是较小的那个地址0x006FFD70
我们只要得到了较小的那个地址,剩下的三个地址就知道了
3. 指针变量
我们获得了地址之后,我们怎么存储起来方便我们后续使用呢?
所以就有了指针变量起作用的时候了
#include <stdio.h>int main()
{int a = 10;int* p = &a;return 0;
}
在int的后面加上了个 * 号,它就是指针类型了,而这个p就是指针变量,且是整型的指针变量
我们看待这个指针的时候要把 int 和 * 看成一对,int* 就和上面的 int类似,都是个类型,它两组成了一个整型指针类型,这个p就是个名字,和 a 是一样的
然后我们就可以把整型a的地址放进这个指针变量里了
4. 解引用
我们现在有了地址,当然可以到地址里面去使用地址内部的东西了
具体使用方法需要解引用
当我们解引用后,就可以拿到内部的东西了
4.1 *解引用
#include <stdio.h>int main()
{int a = 10;int* p = &a;printf("%d\n", *p);return 0;
}
输出:10
对我们刚刚创建的指针变量名字前面加上个*即可解引用
4.2 []解引用
#include <stdio.h>int main()
{int a = 10;int* p = &a;printf("%d\n", p[0]);return 0;
}
输出:10
是不是很像数组?其实指针变量也可以算是一个数组,这与我们直接创建一个数组是一样的
#include <stdio.h>int main()
{int p[] = {1,2,3,4,5};printf("%d\n", p[0]);return 0;
}
数组名本质就是地址,地址加上解引用符号才能获得值,大同小异
4.3 ->解引用
可以把它叫做箭头解引用,要用到它得学到结构体指针才会用到它
#include <stdio.h>struct s
{int n;
};int main()
{struct s s1 = { 10 };struct s* s2 = &s1;printf("%d\n", s2->n);return 0;
}
输出:10
把结构体指针解引用的符号就是使用箭头->
5. 指针变量的大小
指针变量也是有大小的,并且这个和运行环境有着部分关系
#include <stdio.h>int main()
{printf("%zd\n", sizeof(char*));printf("%zd\n", sizeof(short*));printf("%zd\n", sizeof(int*));printf("%zd\n", sizeof(double*));return 0;
}
在x86的环境下结果是4,当我们改成x64后

结果变成了8
直接说结论
5.1 结论
1.指针变量的大小和类型无关,只要是指针类型变量,在相同的平台下,大小都是相同的
2.32位平台下地址是32个bit位,指针大小是4个字节
3.64位平台下地址是64个bit位,指针大小是8个字节
6. 指针运算
指针也是可以进行加减运算的,但是不能进行乘除运算,因为乘除运算没有意义
如果是int类型的指针,那么它每+1就跳过4个字节,char类型指针+1就跳过一个字节,由指针类型决定
#include <stdio.h>int main()
{int a = 10;char b = 'a';printf("&a = %p\n", &a);printf("&a+1 = %p\n", &a+1);printf("&b = %p\n", &b);printf("&b+1 = %p\n", &b + 1);return 0;
}
7. void* 指针
void*指针是一种特殊的指针,并且它不能进行指针运算和解引用运算,但是它可以接受任意类型的地址
例如:
#include <stdio.h>int main()
{int a = 10;int* pa = &a;char* pc = &a;return 0;
}
这样想在char* 里存一个整型地址会报警告
但是我们可以使用void*放
#include <stdio.h>int main()
{int a = 10;int* pa = &a;void* pc = &a;return 0;
}
8. const修饰指针
我们知道const修饰的变量是不可改变的,但是如果硬要改还是能改的
8.1 const修饰变量
#include <stdio.h>int main()
{const int a = 10;a = 5;return 0;
}

但是如果我们使用指针变量来改变呢?
#include <stdio.h>int main()
{const int a = 10;int* p = &a;*p = 5;printf("%d\n", a);return 0;
}
![]()
我们会发现它还是改变了
这是因为我们const的不够彻底,我们应该要让p就算拿到了a的地址也不能改变
8.2 const修饰指针变量
const的放置位置是有几种情况的
const int* p = &a;
int const * p = &a;
int* const p = &a;
第一行和第二行的const都放在了*p的前面,所以限制的是*p,作用是*p不能改变
#include <stdio.h>int main()
{int a = 10;const int* p = &a;//int const * p = &a;*p = 5;//不可改变printf("%d", *p);return 0;
}
第三行const仅仅只放在p的前面,那么它只能限制p,p的地址就不能改变
#include <stdio.h>int main()
{int a = 10;int b = 9;int* const p = &a;//int const * p = &a;p = &b; //不可改变printf("%d", *p);return 0;
}
8.3 结论
主要是看const的位置在哪里,如果const是在*p的前面,限制的是*p,*p不可改变,如果是在*的后面p的前面,限制的是p,那么p不可改变
9. 野指针
概念:指针指向一片未知的区域
9.1 为什么会出现野指针?
9.1.1 指针未初始化
#include <stdio.h>int main()
{int* p;*p = 20;return 0;
}
*p并不知道指向了哪里,默认为随机值,会随机在一片地址把值改成20
9.1.2 指针越界访问
#include <stdio.h>int main()
{int a[5] = { 1,2,3,4,5 };int* p = a; //等同于int* p = &a[0];for (int i = 0; i < 6; i++){*(p++) = i;}return 0;
}
p一直加到了超越数组的位置,那么就是一片未知的区域,所以就变成了野指针
9.1.3 指针指向的空间释放
#include <stdio.h>int* test()
{int n = 100;return &n;
}int main()
{int* p = test();printf("%d\n", *p);return 0;
}
这样*p虽然能获得值并且打印出来,函数每次调用都是建立栈空间,但是使用过后,函数建立的栈帧会消失, 那么地址就不存在了,p原本还指向这个函数,但函数消失后就变成了未知的区域,所以p就变成了野指针
9.2 如何规避野指针
1. 注意指针的初始化
2.小心指针越界访问
3.指针不使用时及时置空NULL
4.避免返回局部变量,如上述9.1.3
10. assert断言
#include <assert.h>
assert(p != NULL);
assert函数的作用是如果括号内为真,这个函数不会有任何反应,但如果为假,那么会报错,并且告诉你在第几行代码出错
assert()的使用对程序员是非常友好的,其一就是能精确报错,其二就是如果已经确定程序没有任何问题,不需要再做任何断言,我们是可以一键注释的
#define NDEBUG
#include <assert.h>
我们只需要在头文件前包括上这个宏定义即可
11. 指针的作用
在我们使用一个函数的时候如果不使用指针变量的话,只能传值,不能改变本身
例如:
#include <stdio.h>void Swap1(int a, int b)
{int tmp = a;a = b;b = tmp;
}int main()
{int x = 5;int y = 6;Swap1(x, y);printf("%d %d\n", x, y);return 0;
}
这样是无法使两个值改变的,因为我们这里传的x和y只是它的值,并不是x和y的本身,所以交换a和b是不起交换x和y的作用的
但如果我们使用指针传它的名字(地址)过去,就可以找到x和y本身,然后将它们交换了
#include <stdio.h>void Swap(int* a, int* b)
{int tmp = *a;*a = *b;*b = tmp;
}int main()
{int x = 5;int y = 6;Swap(&x, &y);printf("%d %d\n", x, y);return 0;
}

在函数内部要注意解引用
完
相关文章:
C语言:指针的基础详解
目录 1. 内存 2. 取地址& 3. 指针变量 4. 解引用 4.1 *解引用 4.2 []解引用 4.3 ->解引用 5. 指针变量的大小 5.1 结论 6. 指针运算 7. void* 指针 8. const修饰指针 8.1 const修饰变量 8.2 const修饰指针变量 8.3 结论 9. 野指针 9.1 为什么会出现野指…...
PHP+vue+mysql校园学生社团管理系统574cc
运行环境:phpstudy/wamp/xammp等 开发语言:php 后端框架:Thinkphp 前端框架:vue.js 服务器:apache 数据库:mysql 数据库工具:Navicat/phpmyadmin 前台功能: 首页:展示社团信息和活动…...
VS Code中主程序C文件引用了另一个.h头文件,编译时报错找不到函数
目录 一、问题描述二、问题原因三、解决方法四、扩展五、通过CMake进行配置 一、问题描述 VS Code中主程序C文件引用了另一个.h头文件,编译时报错找不到函数 主程序 main.c #include <stdio.h> #include "sumaa.h"int main(int, char**){printf(&q…...
边缘计算:重塑数字世界的未来
引言 随着物联网(IoT)设备的激增和5G网络的普及,我们正站在一个计算模式的新纪元门槛上——边缘计算。这一技术范式将数据处理和分析推向网络的边缘,即设备或终端,为实时性要求较高的应用提供了前所未有的可能性。 目…...
2024 前端面试题 附录3
这里记录的是昨天和今天原篇的知识点补充 原篇地址: 2024 前端面试题(GPT回答 示例代码 解释)No.41 - No.60 2024 前端面试题(GPT回答 示例代码 解释)No.61 - No.100 2024 前端面试题(GPT回答 示例代…...
[Vue warn]: Duplicate keys detected: ‘1‘. This may cause an update error.
[Vue warn]: Duplicate keys detected: ‘1‘. This may cause an update error.——> Vue报错,key关键字不唯一: 解决办法:修改一下重复的id值!!!...
Docker-Learn(二)保存、导入、使用Docker镜像
1.保存镜像 根据上一节内容,将创建好镜像进行保存,需要退出当前的已经在运行的docer命令行中断里面,可以通过在终端里面输入指令exit或者按下键盘上的 ctrlD建退出: 回到自己的终端里面,输入指令: docker…...
第三百一十五回
文章目录 1. 概念介绍2. 基本用法3. 补充用法4. 内容总结 我们在上一章回中介绍了"再谈ListView中的分隔线",本章回中将介绍showMenu的用法.闲话休提,让我们一起Talk Flutter吧。 1. 概念介绍 我们在第一百六十三回中介绍了showMenu相关的内容…...
区块链(一): 以太坊基础知识
目录 什么是区块链?什么是以太坊?什么是加密货币?以太坊与比特币有什么不同?以太坊能做什么?什么是智能合约?以太坊社区以太坊白皮书 什么是区块链? 区块链是一个交易数据库,在网络…...
高级FPGA开发之基础协议PCIe
基础协议之PCIe部分 一、TLP包的包头 在PCIe的系统中,tlp包的包头的结构有许多部分是相似的,通过掌握这些常规的包头,能帮助理解在PCIe总线上各个设备之间如何进行数据的收发。 通用的字段 通用字段作用Fmt决定了包头是3DW还是3DWÿ…...
Vue核心基础1:数据代理
1 回顾Object.defineProperty方法 let str hello const person {name: 张三,age: 18 } Object.defineProperty(person, sex, {// value: 男,// enumerable: true, // 控制属性是否可以枚举,默认值是false// writable: true, // 控制属性是否可以被修改࿰…...
12 ABC串口接收原理与思路
1. 串口接收原理 基本原理:通过数据起始位判断要是否要开始接收的数据,通过采样的方式确定每一位数据是0还是1。 如何判断数据起始位到来:通过边沿检测电路检测起始信号的下降沿 如何采样:一位数据采多次,统计得到高…...
leetcode(二分查找)34.在排序数组中查找元素的第一个和最后一个位置(C++详细解释)DAY11
文章目录 1.题目示例提示 2.解答思路3.实现代码结果 4.总结 1.题目 给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。 如果数组中不存在目标值 target,返回 [-1, -1]。 你必须设计…...
算法刷题框架
前言:最近积累了一些算法题量,正在刷东神的算法笔记,监督自己记录下读后启发,顺便帮助道友们阅读 数据结构 这一部分老生常谈,数据的存储方式只有顺序存储和链式存储。 最基本的数组和链表对应这两者,栈…...
跟着cherno手搓游戏引擎【24】开启2D引擎前的项目总结(包括前置知识汇总)
前置技术: c动态链接和静态链接: 隐藏的细节:编译与链接_哔哩哔哩_bilibili 【底层】动态链接库(dll)是如何工作的?_哔哩哔哩_bilibili 预编译,编译,汇编,链接 预编译头文件: 为…...
石子合并+环形石子合并+能量项链+凸多边形的划分——区间DP
一、石子合并 (经典例题) 设有 N 堆石子排成一排,其编号为 1,2,3,…,N。 每堆石子有一定的质量,可以用一个整数来描述,现在要将这 N 堆石子合并成为一堆。 每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,…...
IMX6ULL移植U-Boot 2022.04
目录 目录 1.编译环境以及uboot版本 2.默认编译测试 3.uboot中新增自己的开发板 3.编译测试 4.烧录测试 5.patch文件 1.编译环境以及uboot版本 宿主机Debian12u-boot版本lf_v2022.04 ; git 连接GitHub - nxp-imx/uboot-imx: i.MX U-Boot交叉编译工具gcc-arm-10.3-2021.0…...
ES实战-高级聚合
多桶型聚合 1.词条聚合–terms 2.范围聚合–range 3,直方图聚合–histogram/日期直方图 4.嵌套聚合 5.地理距离聚合 include(包含)exclude(不包含) GET /get-together/_search?pretty {"size": 0,"aggs": {"tags": {"terms": {"…...
网络安全产品之认识蜜罐
文章目录 一、什么是蜜罐二、蜜罐的主要类型三、蜜罐的主要功能四、蜜罐的主要组成及核心技术五、蜜罐的优缺点六、蜜罐如何与其他安全工具协同工作?七、什么是“蜜网”?与蜜罐的联系和区别是什么? 蜜罐的概念首次由Clifford Stoll在其1988年…...
推荐《架构探险:从零开始写Java Web框架》
版权声明 本文原创作者:谷哥的小弟作者博客地址:http://blog.csdn.net/lfdfhl 春节读了《架构探险:从零开始写Java Web框架》,一本大概10年前的好书。 本书的作者是阿里巴巴架构师黄勇。黄勇对分布式服务架构与大数据技术有深入…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...
Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...
汽车生产虚拟实训中的技能提升与生产优化
在制造业蓬勃发展的大背景下,虚拟教学实训宛如一颗璀璨的新星,正发挥着不可或缺且日益凸显的关键作用,源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例,汽车生产线上各类…...
linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...
MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...
mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...
七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...
【JavaSE】多线程基础学习笔记
多线程基础 -线程相关概念 程序(Program) 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存…...
