C语言杂记(指针篇)
指针篇
指针就是地址,地址就是指针 指针变量就是存放地址的变量
*号只有定义的时候表示定义指针变量,其他表示从地址里面取内容
通过指针的方法使main函数中的data1和data2发生数据交换。
#include <stdio.h>
void chang_data(int *data1,int *data2)
{int tmp;tmp = *data1;*data1 = *data2;*data2 = tmp;}int main()
{int data1 = 10;int data2 = 20;chang_data(&data1,&data2);printf("data1:%d data2:%d\n",data1,data2);return 0;
}
============================================================================
指针指向固定的区域
volatile :防止编译器进行编译优化,导致原来的值发生变化
#include <stdio.h>int main()
{volatile unsigned int *p = (volatile unsigned int *)0x0000000001234567;printf("p:%p\n",p);return 0;
}
============================================================================
输入三个数,要求不管怎么输入,在输出的时候要由大到小输出,用函数封装的方法
#include <stdio.h>
int func(int *data1,int *data2,int *data3)
{int tmp;if(*data1 < *data2){tmp = *data1;*data1 = *data2;*data2 = tmp;}if(*data1 < *data3){tmp = *data1;*data1 = *data3;*data3 = tmp;}if(*data2 < *data3){tmp = *data2;*data2 = *data3;*data3 = tmp;}}
int main()
{int data1;int data2;int data3;scanf("%d %d %d",&data1,&data2,&data3);func(&data1,&data2,&data3);printf("大到小:%d %d %d \n",data1,data2,data3);return 0;
}
============================================================================
1.定义一个指针变量指向数组
在C语言当中,数组名(不包括形参数组名)代表数组中首元素的地址,所以p = &a[0];//或者p =a;这两个等价的
2. 指针的偏移,偏移多少根据指针的类型,int偏移四个字节,char就偏移一个字节,使用的指针偏移遍历数组
3.指针的访问效率是远远大于数组下标的访问效率的
int main()
{int i = 0;int a[] = {1,2,3};int *p;p = &a[0];//或者p = a;for(i=0;i<sizeof(a)/sizeof(a[0]);i++){printf("a[%d] = %d \n",i,*p++);}p = a;//让指针重新指向数组的首元素地址return 0;
}
============================================================================
64位操作系统,一个指针占8个字节,所以八个字节表示一个地址
struct demo{int a;char c;
};
int main()
{struct demo *test;printf("sizeof int *:%d\n",sizeof(int *));printf("sizeof char *:%d\n",sizeof(char *));printf("sizeof double *:%d\n",sizeof(double *));printf("sizeof struct *:%d\n",sizeof(test));return 0;
}
============================================================================
- 函数封装数组初始化和遍历
- 作为形参的指针,在调用函数时也会分配自己的一个地址,作为指针,指向传过来的地址(下面传过来的main函数arr的首地址),后续就是对传过来的地址进行操作
ps:下面输出遍历的时候为什么不用把parr重新指向arr的首地址,因为这是两个函数,每个传过来的就是parr就是arr的首地址
#include <stdio.h>void Init_arr(int *parr,int len)
{int i= 0;for(i=0;i<len;i++){*parr = i;parr++; }
}void Printf_arr(int *parr,int len)
{int *tmp2;for(int i = 0;i<len;i++){printf("arr:%d\n",*parr);parr++;}}int main()
{int arr[5];int len = sizeof(arr)/sizeof(arr[0]);Init_arr(arr,len);Printf_arr(arr,len);return 0;
}
============================================================================
练习:数组翻转
#include <stdio.h>void Init_arr(int *parr,int len)
{int i= 0;for(i=0;i<len;i++){*parr = i;parr++; }
}void Overturn_arr(int *arr,int len)
{int i,j;int tmp;for(i=0;i<(len/2);i++){j = len-1-i;tmp = *(arr+i);*(arr+i) = *(arr+j);*(arr+j) = tmp;
}
}
void Printf_arr(int *parr,int len)
{for(int i = 0;i<len;i++){printf("arr:%d\n",*parr);parr++;}}int main()
{int arr[5];int len = sizeof(arr)/sizeof(arr[0]);Init_arr(arr,len);Overturn_arr(arr,len);Printf_arr(arr,len);return 0;
}
============================================================================
二维数组的认知 现在有一个二维数组,我们可以把二维数组理解为父子数组,还有数组名就是地址
int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
那么父数组的名字就是a,而字数组的名字就是a[0],a[1],a[2],那么我们可以看出a的地址和a[0]的地址是一样的,但是要注意他们的偏移量是不一样的,a+1偏移的是一整个子数组的大小,a[0]+1是偏移一个数组元素的大小。
a[0] 等价于 &a[0][0] a[1]等价于 = &a[1][0] a[2]等价于 = &a[2][0]
那么a表示父数组的地址,a[0]表示的字数组的首地址
(a)等价与a[0]的首地址(因为a不可能取到整个子数组的值),*(a)+1就是(a[0])+1
#include <stdio.h>
int main()
{int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};printf("a父数组地址 IP:%p %d\n",a,*(a));printf("a[0]子数组地址 IP:%p %d\n",a[0],*(a[0]));printf("a+1父数组地址偏移 IP:%p %d\n",(a+1),*(a+1));printf("a[0]+1字数组地址偏移 IP:%p %d\n",(a[0]+1),*(a[0]+1));//*(a)等价与a[0]的首地址,*(a)+1就是(a[0])+1printf("*(a)字数组地址偏移 IP:%p *(a)+1的地址: %p\n",*(a),*(a)+1);return 0;
}

总结:

============================================================================
数组指针:就是数组的指针,其实就是一个指向数组的指针。 数组指针才是等同于二维数组名
#include <stdio.h>
int main()
{int arr[2][3] = {{1,2,3},{4,5,6}};int (*p)[3];//指向有3个元素数组的指针p = arr;int i,j;printf("p=%p\n",p);printf("++p:%p\n",++p);//这里看出地址偏移是12字节,就是一个子数组的大小p = arr;//这里让p重新指向二维数组名,再使用p遍历数组for(i=0;i<2;i++){for(j=0;j<3;j++){//printf("arr:%d\n",arr[i][j]);printf("arr:%d\n",*(*(p+i)+j));}}return 0;
}

============================================================================
数组指针的和二维数组的配合使用
题目:用数组指针方法,输出二维数组任意行列的数
#include <stdio.h>void InputNum(int *hang,int *lie)
{printf("输入行列号:\n");scanf("%d %d",hang,lie);
}int SearchNum(int (*p)[4],int hang,int lie)
{return (*(*(p+hang)+lie));
}int main()
{int a[3][4] = {{0,1,2,3},{4,5,6,7},{8,9,10,11}};int hang,lie;int data;//提示输出InputNum(&hang,&lie);//找到对于的数字data = SearchNum(a,hang,lie);//打印出来printf("第%d行第%d列的数字为:%d\n",hang,lie,data);return 0;
}
============================================================================
函数指针,就是一个指向函数入口地址的指针。
1.函数指针:如果程序中定义了一个函数,在编译的时候,编译系统会为函数代码分配一段存储空间,这段存储空间的起始地址(又成为入口地址)称为这个函数的指针。
2.函数名就是地址
3.如何定义一个函数指针变量:int (*p)(int a,int b)
4.定义函数指针的时候要和指向的函数的类型保持一致
#include <stdio.h>void printfWeclme()
{printf("hello world\n");}int DataAdd(int data)
{return ++data;}int main()
{void (*p)(); //函数指针的定义int (*p2)(int data);p = printfWeclme;//指向printfWeclme这个函数p2 = DataAdd;(*p)();//利用函数指针调用这个函数printf("++data:%d\n",(*p2)(10));return 0;
}
练习:用函数指针实现两个整数ab,输入1 2 或者3,1:输出ab中的大者,2:输出ab中的小者,3:输出ab之和
#include <stdio.h>
#include <stdlib.h>
int GetMax(int a,int b)
{int Max;if(a>b){Max = a;}else{Max = b;}return Max;
}int GetMin(int a,int b)
{int Min;if(a<b){Min = a;}else{Min = b;}return Min;
}float GetAvg(int a,int b)
{return ((float)a+(float)b)/2;
}int dataHandler(int a,int b,int (*func1)(int,int))
{int ret;ret = (*func1)(a,b);return ret;}float dataHandler2(int a,int b,float (*func2)(int,int))
{float ret;ret = (*func2)(a,b);return ret;}
int main()
{int a = 10;int b = 20;int mark;int ret;float ret2;int (*func1)(int,int);float (*func2)(int,int);printf("Input:");scanf("%d",&mark);switch(mark){case 1:func1 = GetMax;break;case 2:func1 = GetMin;break;case 3:func2 = GetAvg;break;default:printf("Input error\n");exit(-1);}if(mark != 3){ret = dataHandler(a,b,func1);printf("ret:%d\n",ret);}else{ret2 = dataHandler2(a,b,func2);printf("ret2:%0.2f\n",ret2);}return 0;
}
============================================================================
数组指针:

理解::指针数组就是存放指针的数组,数组的每一项都是指针变量
int main()
{int a = 1;int b = 2;int c = 3;int* arr[3] = {&a,&b,&c};for(int i=0;i<3;i++){printf("%d ",*(arr[i]));} return 0;
}
定义一个函数指针数组,然后进行函数调用:
#include <stdio.h>
#include <stdlib.h>int GetMax(int a,int b)
{int Max;if(a>b){Max = a;}else{Max = b;}printf("Max:%d\n",Max);return Max;
}int GetMin(int a,int b)
{int Min;if(a>b){Min = b;}else{Min = a;}printf("Min:%d\n",Min);return Min;
}int main()
{int a = 10;int b = 20;int (*pfun[2])(int,int) = {GetMax,GetMin};//定义了一个指向函数入口的函数指针数组,每一项都指向一个函数入口地址for(int i=0;i<2;i++){(*pfun[i])(a,b);//进行调用}return 0;
}
============================================================================
指针函数:返回指针值的函数

有3个学生,每个学生对于四门成绩,要求用用户输入学生序号后,输出该学生的全部成绩。用函数指针实现
#include <stdio.h>int *getPosPenson(int pos,int (*pstu)[4])//函数指针
{int *p;p = (int *)(pstu+pos);return p;}int main()
{int arr[3][4] = {{1,2,3,4},{5,6,7,8},{45,52,63,54} };int *ppos;int pos;//学生0 1 2do{printf("输入学生012:");scanf("%d",&pos);}while(pos!=0 && pos!=1 && pos!=2 );ppos = getPosPenson(pos,arr);for(int i=0;i<4;i++){printf("%d ",*(ppos++));}return 0;
}

============================================================================
二级指针:保存指针地址的指针
#include <stdio.h>int main()
{int data = 10;int *p;p = &data;int **p2;printf("data的地址:%p\n",&data);printf("p存放的地址(data的地址):%p\n",p);printf("p本身的地址:%p\n",&p);p2 = &p;printf("p2保存的地址(p本身的地址):%p\n",p2);printf("*p2是:%p\n",*p2);//这里取到的应该是data的地址printf("**p2是:%d\n",**p2);//这里就取到data的值了return 0;
}

============================================================================
下面对于ppos的理解
#include <stdio.h>void getPosPenson(int pos,int (*pstu)[4],int **ppos)//函数指针
{*ppos = (int *)(pstu+pos);//*ppos取到的就是main函数一级指针ppos的地址,这个指针指向了pstu这个指针的偏移地址,所以main函数中的ppos就再*(取一次内容)就访问到里面的值了}int main()
{int arr[3][4] = {{1,2,3,4},{5,6,7,8},{45,52,63,54} };int *ppos;int pos;//学生0 1 2do{printf("输入学生012:");scanf("%d",&pos);}while(pos!=0 && pos!=1 && pos!=2 );getPosPenson(pos,arr,&ppos);//这里传过去的时ppos的地址(指针的地址需要二级指针来承接)for(int i=0;i<4;i++){printf("%d ",*(ppos++));}return 0;
}
============================================================================
二级指针不能简单粗暴指向二维数组
============================================================================
搞懂下面这张图

================================================================================================================================================================
相关文章:
C语言杂记(指针篇)
指针篇 指针就是地址,地址就是指针 指针变量就是存放地址的变量 *号只有定义的时候表示定义指针变量,其他表示从地址里面取内容 通过指针的方法使main函数中的data1和data2发生数据交换。 #include <stdio.h> void chang_data(int *data1,int *da…...
ES window 系统环境下连接问题
环境问题:(我采用的版本是 elasticsearch-7.9.3)注意 开始修正之前的配置:前提:elasticsearch.yml增加或者修正一下配置:xpack.security.enabled: truexpack.license.self_generated.type: basicxpack.secu…...
hexo部署github搭建个人博客 完整详细带图版(更新中)
文章目录0. 前置内容1. hexo创建个人博客2. GitHub创建仓库3. hexo部署到GitHub4. 常用命令newcleangenerateserverdeploy5. 添加插件5.1 主题5.2 博客基本信息5.3 创建新的菜单5.4 添加搜索功能5.5 添加阅读时间字数提示5.6 打赏功能5.7 切换主题5.8 添加不蒜子统计5.9 添加百…...
SpringBoot集成DruidDataSource实现监控 SQL 性能
一、快速入门 1.1 基本概念 我们都使用过连接池,比如C3P0、DBCP、hikari、Druid,虽然 HikariCP 的速度稍快,但 Druid 能够提供强大的监控和扩展功能。Druid DataSource 是阿里巴巴开发的号称为监控而生的数据库连接池,它不仅可以…...
maven镜像源及代理配置
在公司使用网络一般需要设置代理, 我在idea中创建springboot工程时,发现依赖下载不了,原以为只要浏览器设置代理,其他的网络访问都会走代理,经过查资料设置了以下几个地方后工程创建正常,在此记录给大家参考…...
【Java面试篇】Spring中@Transactional注解事务失效的常见场景
文章目录Transactional注解的失效场景☁️前言🍀前置知识🍁场景一:Transactional应用在非 public 修饰的方法上🍁场景二: propagation 属性设置错误🍁场景三:rollbackFor属性设置错误dz…...
【C】分配内存的函数
#include <stdlib.h>//分配所需的内存空间,并返回一个指向它的指针。 void *malloc(size_t size);//分配所需的内存空间,并返回一个指向它的指针。并且calloc负责把这块内存空间用字节0填//充,而malloc并不负责把分配的内存空间清零 vo…...
IDEA 断点总是进入class文件没有进入源文件解决
前言 idea 断点总是进入class文件没有进入源文件解决 问题 在源文件里打了断点,断点模式启动时却进入了class文件里的断点,而没有进入到java源文件里的断点。 比如:我在 A.java 里打了断点,调试时却进入到了 jar 包里的 A.clas…...
【flink】 flink入门教程demo 初识flink
文章目录通俗解释什么是flink及其应用场景flink处理流程及核心APIflink代码快速入门flink重要概念什么是flink? 刚接触这个词的同学 可能会觉得比较难懂,网上搜教程 也是一套一套的官话, 如果大家熟悉stream流,那或许会比较好理解…...
LeetCode 1487. 保证文件名唯一
【LetMeFly】1487.保证文件名唯一 力扣题目链接:https://leetcode.cn/problems/making-file-names-unique/ 给你一个长度为 n 的字符串数组 names 。你将会在文件系统中创建 n 个文件夹:在第 i 分钟,新建名为 names[i] 的文件夹。 由于两个…...
详细剖析|袋鼠云数栈前端框架Antd 3.x 升级 4.x 的踩坑之路
袋鼠云数栈从2016年发布第⼀个版本开始,就始终坚持着以技术为核⼼、安全为底线、提效为⽬标、中台为战略的思想,坚定不移地⾛国产化信创路线,不断推进产品功能迭代、技术创新、服务细化和性能升级。 在数栈过去的产品迭代中受限于当前组件的…...
【C++PrimerPlus】第三章 处理数据
文章目录前言内容目录3.1 简单变量3.1.2 变量名3.1.2 整形3.1.3 整形short,int,long,long long3.1.4 无符号类型3.1.5 选择整形类型3.1.6 整形字面值3.1.7 C如何确定常量的类型3.1.8 char类型:字符和小整数3.1.9 bool类型3.2 const修饰符3.3浮点数3.3.1 书写浮点数3…...
【基础算法】单链表的OJ练习(1) # 反转链表 # 合并两个有序链表 #
文章目录前言反转链表合并两个有序链表写在最后前言 上一章讲解了单链表 -> 传送门 <- ,后面几章就对单链表进行一些简单的题目练习,目的是为了更好的理解单链表的实现以及加深对某些函数接口的熟练度。 本章带来了两个题目。一是反转链表&#x…...
离散数学笔记(1)命题逻辑
文章目录1.命题符号化及联结词基本概念本节题型2.命题公式及分类基本概念本节题型1.命题符号化及联结词 基本概念 命题的定义:能够判断真假的陈述句称为命题。 备注:感叹句、疑问句、祈使句和类似于xy>5之类真值不唯一的句子都不是命题。 真值的真假…...
IDEA Android 网格布局(GridLayout)示例(计算器界面布局)
网格布局(GridLayout) 示例程序效果(实现类似vivo手机自带计算器UI) 真机和模拟器运行效果: 简述: GridLayout(网格布局)和TableLayout(表格布局)有类似的地方,通俗来讲可以理解为…...
【蓝桥杯嵌入式】拓展板之数码管显示
文章目录硬件电路连接方式函数实现文章福利硬件电路 通过上述原理图,可知拓展板上的数码管是一个共阴数码管,也就是说某段数码管接上高电平时,就会点亮。 上述原理图还给出一个提示,即:三个数码管分别与三个74HC59…...
Web Spider案例 网洛克 第三题 AAEncode加密 练习(七)
声明 此次案例只为学习交流使用,抓包内容、敏感网址、数据接口均已做脱敏处理,切勿用于其他非法用途; 文章目录声明一、资源推荐二、逆向目标三、抓包分析 & 下断分析逆向3.1 抓包分析3.2 下断分析逆向拿到混淆JS代码3.3 AAEncode解决方…...
【javaScript面试题】2023前端最新版javaScript模块,高频24问
🥳博 主:初映CY的前说(前端领域) 🌞个人信条:想要变成得到,中间还有做到! 🤘本文核心:博主收集的关于javaScript的面试题 目录 一、2023javaScript面试题精选 1.js的数据类型…...
Hadoop集群启动从节点没有DataNode
一、问题背景 之前启动hadoop集群的时候都没有问题,今天启动hadoop集群的时候,从节点的DataNode没有启动起来。 二、解决思路 遇见节点起不来的情况,可以去看看当前节点的日志文件 我进入当前从节点的hadoop安装目录的Logs文件下去查看日…...
FIFO IP Core
FIFO IP Core 先进先出的缓存器常常被用于数据的缓存,或者高速异步数据交互(跨时钟信号传递)和RAM和ROM的区别是没有地址线,无法指定地址 写时钟(Write Clock Domain),读时钟写复位(wr_rst),读…...
测试微信模版消息推送
进入“开发接口管理”--“公众平台测试账号”,无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息: 关注测试号:扫二维码关注测试号。 发送模版消息: import requests da…...
【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器
——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的一体化测试平台,覆盖应用全生命周期测试需求,主要提供五大核心能力: 测试类型检测目标关键指标功能体验基…...
Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...
Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...
AI病理诊断七剑下天山,医疗未来触手可及
一、病理诊断困局:刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断",医生需通过显微镜观察组织切片,在细胞迷宫中捕捉癌变信号。某省病理质控报告显示,基层医院误诊率达12%-15%,专家会诊…...
Redis:现代应用开发的高效内存数据存储利器
一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发,其初衷是为了满足他自己的一个项目需求,即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源,Redis凭借其简单易用、…...
STM32---外部32.768K晶振(LSE)无法起振问题
晶振是否起振主要就检查两个1、晶振与MCU是否兼容;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容(CL)与匹配电容(CL1、CL2)的关系 2. 如何选择 CL1 和 CL…...
小木的算法日记-多叉树的递归/层序遍历
🌲 从二叉树到森林:一文彻底搞懂多叉树遍历的艺术 🚀 引言 你好,未来的算法大神! 在数据结构的世界里,“树”无疑是最核心、最迷人的概念之一。我们中的大多数人都是从 二叉树 开始入门的,它…...
ZYNQ学习记录FPGA(一)ZYNQ简介
一、知识准备 1.一些术语,缩写和概念: 1)ZYNQ全称:ZYNQ7000 All Pgrammable SoC 2)SoC:system on chips(片上系统),对比集成电路的SoB(system on board) 3)ARM:处理器…...
macOS 终端智能代理检测
🧠 终端智能代理检测:自动判断是否需要设置代理访问 GitHub 在开发中,使用 GitHub 是非常常见的需求。但有时候我们会发现某些命令失败、插件无法更新,例如: fatal: unable to access https://github.com/ohmyzsh/oh…...

