当前位置: 首页 > news >正文

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等 开发语言&#xff1a;php 后端框架&#xff1a;Thinkphp 前端框架&#xff1a;vue.js 服务器&#xff1a;apache 数据库&#xff1a;mysql 数据库工具&#xff1a;Navicat/phpmyadmin 前台功能&#xff1a; 首页&#xff1a;展示社团信息和活动…...

VS Code中主程序C文件引用了另一个.h头文件,编译时报错找不到函数

目录 一、问题描述二、问题原因三、解决方法四、扩展五、通过CMake进行配置 一、问题描述 VS Code中主程序C文件引用了另一个.h头文件&#xff0c;编译时报错找不到函数 主程序 main.c #include <stdio.h> #include "sumaa.h"int main(int, char**){printf(&q…...

边缘计算:重塑数字世界的未来

引言 随着物联网&#xff08;IoT&#xff09;设备的激增和5G网络的普及&#xff0c;我们正站在一个计算模式的新纪元门槛上——边缘计算。这一技术范式将数据处理和分析推向网络的边缘&#xff0c;即设备或终端&#xff0c;为实时性要求较高的应用提供了前所未有的可能性。 目…...

2024 前端面试题 附录3

这里记录的是昨天和今天原篇的知识点补充 原篇地址&#xff1a; 2024 前端面试题&#xff08;GPT回答 示例代码 解释&#xff09;No.41 - No.60 2024 前端面试题&#xff08;GPT回答 示例代码 解释&#xff09;No.61 - No.100 2024 前端面试题&#xff08;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报错&#xff0c;key关键字不唯一&#xff1a; 解决办法&#xff1a;修改一下重复的id值&#xff01;&#xff01;&#xff01;...

Docker-Learn(二)保存、导入、使用Docker镜像

1.保存镜像 根据上一节内容&#xff0c;将创建好镜像进行保存&#xff0c;需要退出当前的已经在运行的docer命令行中断里面&#xff0c;可以通过在终端里面输入指令exit或者按下键盘上的 ctrlD建退出&#xff1a; 回到自己的终端里面&#xff0c;输入指令&#xff1a; docker…...

第三百一十五回

文章目录 1. 概念介绍2. 基本用法3. 补充用法4. 内容总结 我们在上一章回中介绍了"再谈ListView中的分隔线"&#xff0c;本章回中将介绍showMenu的用法.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 我们在第一百六十三回中介绍了showMenu相关的内容…...

区块链(一): 以太坊基础知识

目录 什么是区块链&#xff1f;什么是以太坊&#xff1f;什么是加密货币&#xff1f;以太坊与比特币有什么不同&#xff1f;以太坊能做什么&#xff1f;什么是智能合约&#xff1f;以太坊社区以太坊白皮书 什么是区块链&#xff1f; 区块链是一个交易数据库&#xff0c;在网络…...

高级FPGA开发之基础协议PCIe

基础协议之PCIe部分 一、TLP包的包头 在PCIe的系统中&#xff0c;tlp包的包头的结构有许多部分是相似的&#xff0c;通过掌握这些常规的包头&#xff0c;能帮助理解在PCIe总线上各个设备之间如何进行数据的收发。 通用的字段 通用字段作用Fmt决定了包头是3DW还是3DW&#xff…...

Vue核心基础1:数据代理

1 回顾Object.defineProperty方法 let str hello const person {name: 张三,age: 18 } Object.defineProperty(person, sex, {// value: 男,// enumerable: true, // 控制属性是否可以枚举&#xff0c;默认值是false// writable: true, // 控制属性是否可以被修改&#xff0…...

12 ABC串口接收原理与思路

1. 串口接收原理 基本原理&#xff1a;通过数据起始位判断要是否要开始接收的数据&#xff0c;通过采样的方式确定每一位数据是0还是1。 如何判断数据起始位到来&#xff1a;通过边沿检测电路检测起始信号的下降沿 如何采样&#xff1a;一位数据采多次&#xff0c;统计得到高…...

leetcode(二分查找)34.在排序数组中查找元素的第一个和最后一个位置(C++详细解释)DAY11

文章目录 1.题目示例提示 2.解答思路3.实现代码结果 4.总结 1.题目 给你一个按照非递减顺序排列的整数数组 nums&#xff0c;和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。 如果数组中不存在目标值 target&#xff0c;返回 [-1, -1]。 你必须设计…...

算法刷题框架

前言&#xff1a;最近积累了一些算法题量&#xff0c;正在刷东神的算法笔记&#xff0c;监督自己记录下读后启发&#xff0c;顺便帮助道友们阅读 数据结构 这一部分老生常谈&#xff0c;数据的存储方式只有顺序存储和链式存储。 最基本的数组和链表对应这两者&#xff0c;栈…...

跟着cherno手搓游戏引擎【24】开启2D引擎前的项目总结(包括前置知识汇总)

前置技术&#xff1a; c动态链接和静态链接&#xff1a; 隐藏的细节&#xff1a;编译与链接_哔哩哔哩_bilibili 【底层】动态链接库(dll)是如何工作的&#xff1f;_哔哩哔哩_bilibili 预编译&#xff0c;编译&#xff0c;汇编&#xff0c;链接 预编译头文件&#xff1a; 为…...

石子合并+环形石子合并+能量项链+凸多边形的划分——区间DP

一、石子合并 (经典例题) 设有 N 堆石子排成一排&#xff0c;其编号为 1,2,3,…,N。 每堆石子有一定的质量&#xff0c;可以用一个整数来描述&#xff0c;现在要将这 N 堆石子合并成为一堆。 每次只能合并相邻的两堆&#xff0c;合并的代价为这两堆石子的质量之和&#xff0c;…...

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": {"…...

网络安全产品之认识蜜罐

文章目录 一、什么是蜜罐二、蜜罐的主要类型三、蜜罐的主要功能四、蜜罐的主要组成及核心技术五、蜜罐的优缺点六、蜜罐如何与其他安全工具协同工作&#xff1f;七、什么是“蜜网”&#xff1f;与蜜罐的联系和区别是什么&#xff1f; 蜜罐的概念首次由Clifford Stoll在其1988年…...

推荐《架构探险:从零开始写Java Web框架》

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 春节读了《架构探险&#xff1a;从零开始写Java Web框架》&#xff0c;一本大概10年前的好书。 本书的作者是阿里巴巴架构师黄勇。黄勇对分布式服务架构与大数据技术有深入…...

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)

HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

python打卡day49

知识点回顾&#xff1a; 通道注意力模块复习空间注意力模块CBAM的定义 作业&#xff1a;尝试对今天的模型检查参数数目&#xff0c;并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议&#xff08;EPSFD 2025&#xff09;将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会&#xff0c;EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

循环冗余码校验CRC码 算法步骤+详细实例计算

通信过程&#xff1a;&#xff08;白话解释&#xff09; 我们将原始待发送的消息称为 M M M&#xff0c;依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)&#xff08;意思就是 G &#xff08; x ) G&#xff08;x) G&#xff08;x) 是已知的&#xff09;&#xff0…...

Docker 运行 Kafka 带 SASL 认证教程

Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明&#xff1a;server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具

文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

EtherNet/IP转DeviceNet协议网关详解

一&#xff0c;设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络&#xff0c;本网关连接到EtherNet/IP总线中做为从站使用&#xff0c;连接到DeviceNet总线中做为从站使用。 在自动…...

什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南

文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/55aefaea8a9f477e86d065227851fe3d.pn…...