【C语言 | 指针】C指针详解(经典,非常详细)

news2024/10/5 16:22:06

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭介绍🍭
😎金句分享😎:🍭🍭

本文未经允许,不得转发!!!

目录

  • 🎄一、复杂类型阅读技巧
  • 🎄二、了解指针,明确这四个方面
    • ✨2.1 指针变量所在的内存地址
    • ✨2.2 指针变量的值
    • ✨2.3 指针变量的类型
    • ✨2.4 指针变量所指向的类型
  • 🎄三、运算符 `&`和 `*`
  • 🎄四、指针表达式
  • 🎄五、指针算数运算
  • 🎄六、


在这里插入图片描述

🎄一、复杂类型阅读技巧

要了解指针,多多少少会出现一些比较复杂的类型,这小节先介绍怎么阅读复杂类型。

下面是一些复杂类型,先自己尝试是否可以清楚变量是什么类型的:

int p;
int *p;
int *p[3];
int (*p)[3];
int **p; 
int p(int); 
Int (*p)(int);
int *(*p(int))[3]; 

这里需要使用一个右左法则来阅读复杂类型:

右左法则

  • 1、从变量名(没变量名的,从最里层的圆括号)开始,先看右边,再看左边;
  • 2、如果右边是() 则是函数,如果是 [] 则是数组。
  • int p; //这是一个普通的整型变量
  • int *p; //首先从P 处开始,先与*结合,所以说明P 是一个指针,然后再与int 结合,说明指针所指向的内容的类型为int 型。所以P是一个返回整型数据的指针
  • int p[3]; //首先从P 处开始,先与[]结合,说明P 是一个数组,然后与int 结合,说明数组里的元素是整型的,所以P 是一个由整型数据组成的数组
  • int *p[3]; //首先从P 处开始,先与[]结合,因为其优先级比*高,所以P 是一个数组,然后再与*结合,说明数组里的元素是指针类型,然后再与int 结合,说明指针所指向的内容的类型是整型的,所以P 是一个由返回整型数据的指针所组成的数组
  • int (*p)[3]; //首先从P 处开始,先与*结合,说明P 是一个指针然后再与[]结合(与"()"这步可以忽略,只是为了改变优先级),说明指针所指向的内容是一个数组,然后再与int 结合,说明数组里的元素是整型的。所以P 是一个指向由整型数据组成的数组的指针
  • int **p; //首先从P 开始,先与*结合,说是P 是一个指针,然后再与*结合,说明指针所指向的元素是指针,然后再与int 结合,说明该指针所指向的元素是整型数据。由于二级指针以及更高级的指针极少用在复杂的类型中,所以后面更复杂的类型我们就不考虑多级指针了,最多只考虑一级指针。
  • int p(int); //从P 处起,先与()结合,说明P 是一个函数,然后进入()里分析,说明该函数有一个整型变量的参数,然后再与外面的int 结合,说明函数的返回值是一个整型数据
  • int (*p)(int); //从P 处开始,先与指针结合,说明P 是一个指针,然后与()结合,说明指针指向的是一个函数,然后再与()里的int 结合,说明函数有一个int 型的参数,再与最外层的int 结合,说明函数的返回类型是整型,所以P 是一个指向有一个整型参数且返回类型为整型的函数的指针。
  • int *(*p(int))[3]; //可以先跳过,不看这个类型,过于复杂。从P 开始,先与()结合,说明P 是一个函数,然后进入()里面,与int 结合,说明函数有一个整型变量参数,然后再与外面的*结合,说明函数返回的是一个指针,然后到最外面一层,先与[]结合,说明返回的指针指向的是一个数组,然后再与*结合,说明数组里的元素是指针,然后再与int 结合,说明指针指向的内容是整型数据。所以P 是一个参数为一个整数据且返回一个指向由整型指针变量组成的数组的指针变量的函数。

在这里插入图片描述

🎄二、了解指针,明确这四个方面

如果定义一个基本数据类型变量,我们可以了解到这三个方面的内容:1、变量所在的内存地址; 2、变量的值; 3、变量的类型;

例如:从语句 int i = 1; ,我们可以知道:

  • 1、编译器分配一块4个字节(sizeof(int))的内存,地址是&i
  • 2、这块内存里面存放了值为4的内容;
  • 3、这个变量是 int 类型的。

如果定义的是指针变量,则我们需要清楚四个方面的内容:

  • 1、指针变量所在的内存地址;
  • 2、指针变量的值;
  • 3、指针变量的类型;
  • 4、指针变量所指向的类型。

指针变量和基本数据类型变量的区别就是,编译器会将存放在指针变量里的任何数据都当作地址来处理。

✨2.1 指针变量所在的内存地址

定义了一个任何类型变量,编译器都会为其分配一块内存来存放该变量,起始地址就是 &变量名 ,大小就是该变量的类型的大小。

所以指针变量也是如此,定义指针变量后,编译器会为该变量分配一块内存。内存大小,在32位系统是4个字节,在64位系统是8个字节,可以使用 sizeof(void*) 来打印该系统指针类型的大小。


✨2.2 指针变量的值

指针变量的值是指针变量所在的内存存储的数值,这个值会被编译器当作一个地址,而不是一个一般的数值。

在32 位程序里,所有类型的指针的值都是一个32 位整数,因为32 位程序里内存地址全都是32 位长。指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度为 sizeof(指针所指向的类型) 的一片内存区。

以后,我们说一个指针的值是 XX,就相当于说该指针指向了以 XX 为首地址的一片内存区域;我们说一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。指针变量的值==某块内存的首地址

指针所指向的内存区域指针所指向的类型 是两个完全不同的概念。例如语句 int *p;,指针所指向的类型已经有了,但由于指针还未初始化,所以它所指向的内存区是不存在的,或者说是无意义的。


✨2.3 指针变量的类型

从语法的角度看,你只要把指针声明语句里的指针变量名字去掉,剩下的部分就是这个指针变量的类型。这是指针变量所具有的类型。

让我们看看下面语句中各个指针变量的类型:

(1)int* ptr;//指针的类型是int*
(2)char* ptr;//指针的类型是char*
(3)int** ptr;//指针的类型是int**
(4)int(*ptr)[3];//指针的类型是int(*)[3]
(5)int*(*ptr)[4];//指针的类型是int*(*)[4]

怎么样?找出指针变量的类型的方法是不是很简单?


✨2.4 指针变量所指向的类型

当你通过指针变量来访问指针所指向的内存区时,指针变量所指向的类型 决定了编译器将把那片内存区里的内容当做什么来看待。

从语法上看,你只须把指针声明语句中的指针变量名字和名字左边的指针声明符*去掉,剩下的就是指针变量所指向的类型。例如:

(1)int*ptr; //指针所指向的类型是int
(2)char*ptr; //指针所指向的的类型是char
(3)int**ptr; //指针所指向的的类型是int*
(4)int(*ptr)[3]; //指针所指向的的类型是int()[3]
(5)int*(*ptr)[4]; //指针所指向的的类型是int*()[4]

在指针的算术运算中,指针变量所指向的类型有很大的作用。

指针变量的类型指针变量所指向的类型 是两个概念。当你对C语言越来越熟悉时,你会发现,分清楚指针变量的类型指针变量所指向的类型 ,是精通指针的关键点之一。


清楚了上面关于指针的四个内容后,我们以后碰到一个指针都要问问自己:①指针是什么类型的?②指针指向的类型是什么?③指针指向的内存是哪里?

看看下面例子:

例:语句int *p = 0x0000FF00;

  • 1、分配了一块内存并命名为p,其起始地址是&p,长度为sizeof(int *);
  • 2、这块内存里面存放的值为0x0000FF000,是指针所指向内存的起始地址;
  • 3、指针类型是 int*
  • 4、指针所指向的类型是 int,说明0x0000FF000存放着 int 型的内容。

在这里插入图片描述

在这里插入图片描述

🎄三、运算符 &*

指针运算中,&是取地址运算符,用于获取变量所在内存区域的起始地址; *是间接访问运算符,用于访问指针变量所指向的内存区域。

&a 的运算结果是一个指针, 指针的类型是 a 的类型加个*, 指针所指向的类型是 a 的类型, 指针所指向的地址嘛, 那就是 a 的地址。

*p 的运算结果就五花八门了。 总之*p 的结果是 p 所指向的东西,这个东西有这些特点: 它的类型是 p 指向的类型, 它所占用的地址是 p 所指向的地址。

int a=12; int b; int *p; int **ptr;
p=&a;	//&a 的结果是一个指针,类型是int*,指向的类型是
		//int,指向的地址是a 的地址。
*p=24;	//*p 的结果,在这里它的类型是int,它所占用的地址是
		//p 所指向的地址,显然,*p 就是变量a。
ptr=&p;	//&p 的结果是个指针,该指针的类型是p 的类型加个*,
		//在这里是int **。该指针所指向的类型是p 的类型,这
		//里是int*。该指针所指向的地址就是指针p 自己的地址。
*ptr=&b;//*ptr 是个指针,&b 的结果也是个指针,且这两个指针
		//的类型和所指向的类型是一样的,所以用&b 来给*ptr 赋
		//值就是毫无问题的了。
**ptr=34; //*ptr 的结果是ptr 所指向的东西,在这里是一个指针,
		//对这个指针再做一次*运算,结果是一个int 类型的变量。

* 后面加一个合法的内存地址,就可以该段内存操作。所以我们甚至可以将数值存储到指定的内存地址,用下面这种方式:

*((int*)0x0000ff00) = 100;// 0x0000ff00 必须是合法地址,否则段错误。

在这里插入图片描述

🎄四、指针表达式

在这里插入图片描述

🎄五、指针算数运算

在这里插入图片描述

🎄六、

在这里插入图片描述
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁

参考资料:
《让你不再害怕指针.pdf》
《C语言深度剖析》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1205127.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

这样书写Python代码的方式,实在是太优雅了~

文章目录 前言一、在Python中配合pipe灵活使用链式写法二 、pipe中常用的管道操作函数1.使用traverse()展平嵌套数组2.使用dedup()进行顺序去重3.使用filter()进行值过滤4.使用groupby()进行分组运算5.使用select()对上一步结果进行自定义遍历运算6.使用sort()进行排序 总结关于…

thinkphp8 多级控制器调用

在使用这个目录的时候正常访问时 http://tp.com/index.php/user2.login/index, 这个多级目录时不允许使用的,想要使用就的使用路由 在route/app.php 里面配置:Route::get(user2/login,user2.Login/index); 第一个参数时外部访问参数,第二个是…

Android权限动态申请(包括悬浮窗)

目录 效果图 一、环境配置 二、新建工具类 三、开始使用 备注(一):用户手动设置权限 手动设置效果图 备注(二):在Fragment中如何调用动态权限申请 备注(三):悬浮窗…

SDL2 显示文字

1.简介 SDL本身没有显示文字功能,它需要用扩展库SDL_ttf来显示文字。ttf是True Type Font的缩写,ttf是Windows下的缺省字体,它有美观,放大缩小不变形的优点,因此广泛应用很多场合。 使用ttf库的第一件事要从Windows的…

【leetcode】8.字符串转换整数

题目 请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C 中的 atoi 函数)。 函数 myAtoi(string s) 的算法如下: 读入字符串并丢弃无用的前导空格 检查下一个字符(假设还未…

【文末送书】深入浅出嵌入式虚拟机原理

欢迎关注博主 Mindtechnist 或加入【智能科技社区】一起学习和分享Linux、C、C、Python、Matlab,机器人运动控制、多机器人协作,智能优化算法,滤波估计、多传感器信息融合,机器学习,人工智能等相关领域的知识和技术。关…

基于回溯搜索算法优化概率神经网络PNN的分类预测 - 附代码

基于回溯搜索算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于回溯搜索算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于回溯搜索优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要:针对PNN神…

二维码智慧门牌管理系统升级解决方案:数据可视化助力运营精准决策

文章目录 前言一、升级版二维码智慧门牌管理系统的特点二、数据可视化助力运营精准决策 前言 随着科技的不断进步,传统的门牌管理系统已经无法满足现代社会的需求。为了提高管理效率,减少人力成本,我们引入了升级版的二维码智慧门牌管理系统…

[PyTorch][chapter 62][强化学习-基本概念]

前言: 目录: 强化学习概念 马尔科夫决策 Bellman 方程 格子世界例子 一 强化学习 强化学习 必须在尝试之后,才能发现哪些行为会导致奖励的最大化。 当前的行为可能不仅仅会影响即时奖赏,还有影响下一步奖赏和所有奖赏 强…

如何应对招聘中的职业性格测评?

很多同学听说要做性格测试,第一反应是如何让自己的性格让HR看起来更好....没办法为了顺利入职,咱不能老实作答,因为性格测评搞不好是真刷人的,刷人的(无视你的专业能力和笔试成绩)..... 可是....很多性格测…

eNSP-打开华为USG6000V1防火墙web管理页面方法

一、本地打开防火墙web管理页面 1.先在ensp中启动USG6000V1防火墙,启动后,需要输入原始username和password(username:admin,password:Admin123),并修改原始密码后,才能配…

SQL学习(CTFhub)整数型注入,字符型注入,报错注入 -----手工注入+ sqlmap注入

目录 整数型注入 手工注入 为什么要将1设置为-1呢? sqlmap注入 sqlmap注入步骤: 字符型注入 手工注入 sqlmap注入 报错注入 手工注入 sqlmap注入 整数型注入 手工注入 先输入1 接着尝试2,3,2有回显,而3没有回显…

做一个springboot用户信息模块

目录 用户信息部分 1、获取用户详细信息 前言 代码分析 代码实现 测试 2、更新用户信息 前言 代码实现 测试 3、更新用户头像 前言 代码实现 测试 4、更新用户密码 前言 代码实现 测试 用户信息部分 1、获取用户详细信息 前言 承接上一篇博客登录注册功能…

快速批量去除文件夹名称中多余重复文字!一键轻松优化文件夹命名!

您是否曾经因为文件夹名称中多余重复文字而烦恼?是否因为文件夹重命名而浪费大量时间?现在,我们为您推荐一款全新的文件夹批量改名工具——快速批量去除文件夹名称中多余重复文字,轻松实现文件夹改名优化,让您的整理效…

Leetcode_2:两数相加

题目描述: 给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。 请你将两个数相加,并以相同形式返回一个表示和的链表。 你可以假设除了数字 0 之外&#xff…

106.am40刷机(linux)折腾记2-前期的准备工作2-软件使用

最终的目标是刷入firefly的3399的镜像,同时更新内核到linux5.10版本(4.4的内核应该是相同的方法,我目前没有去折腾,暂时不用了)。 1. 平台: rk3399 am40 4g32g 2. 内核:暂无 3. 交叉编译工…

数据结构----顺序栈的操作

1.顺序栈的存储结构 typedef int SElemType; typedef int Status; typedef struct{SElemType *top,*base;//定义栈顶和栈底指针int stacksize;//定义栈的容量 }SqStack; 2.初始化栈 Status InitStack(SqStack &S){//初始化一个空栈S.basenew SElemType[MAXSIZE];//为顺序…

macOS文本编辑器 BBEdit 最新 for mac

BBEdit是一款功能强大的文本编辑器,适用于Mac操作系统。它由Bare Bones Software开发,旨在为开发者和写作人员提供专业级的文本编辑工具。 以下是BBEdit的一些主要特点和功能: 多语言支持:BBEdit支持多种编程语言和标记语言&…

jstack java堆栈跟踪工具

jstack java堆栈跟踪工具 1、jstack介绍 jstack(stack trace for java)是java虚拟机自带的一种堆栈跟踪工具。 jstack主要用于生成java虚拟机当前时刻的线程快照,线程快照是当前java虚拟机内每一条线程正在执行的方法 堆栈的集合&#xf…

信息安全工程师软考知识点

文章目录 知识点总结2023软考总结选择题问答题 知识点总结 军用不对外公开的信息系统安全等级至少应该>三级 数据中心的耐火等级不应低于二级 政府网站的信息安全等级原则上不应低于二级第一代交换机以集线器为代表,工作在OSI物理层 第二代交换机以太网交换机&a…