指针的进阶
int main()
{char ch = 'w';char* pc = &ch;//pc就是字符指针//const char *p = "abcdef";//这里其实是把字符串"abcdef"的首地址放入了指针p中//*p = 'w';//这是错误的无法修改值(可以看到这里绿色波浪线警告)char arr[] = "abcdef";//创建了一个arr数组,里面存了字符串char* p = arr;*p = 'w';//此时这里的首元素可以被修改printf("%s\n", arr);return 0;
}
可以发现,通过字符指针 p ,将数组arr首元素a改为了w
而创建指针,直接指向字符串,是没法修改的
因为p里面存储的是,字符串abcdef的首地址
仔细比较二者之间的区别
int main()
{char str1[] = "hello bit.";char str2[] = "hello bit.";//这里字符型数组 str1[]和str2[]是分别创建了两块不同的空间,来存放相同的hello bit.内容,//这两块空间的起始地址不同,所以str1和str2代表的首元素地址也不同。const char* str3 = "hello bit.";const char* str4 = "hello bit.";//这里是创建了两个符号型指针str3和str4指向了同一块空间hello bit.的首地址//所以指针str3和str4的地址相同if (str1 == str2)printf("str1 and str2 are same\n");elseprintf("str1 and str2 are not same\n");//str1 and str2 are not sameif (str3 == str4)printf("str3 and str4 are same\n");elseprintf("str3 and str4 are not same\n");//str3and str4 are samereturn 0;
}
这里str3和str4指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域,当 几个指针。指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化 不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4不同。
int arr1[10]; //整形数组 - 存放整型的数组char arr2[4]; //字符数组 -存放字符的数组那么指针数组 - 存放指针的数组char* arr3[5];//
1. 指针数组举例
int main()
{//指针数组:是一个存放指针的数组。char* arr[] = { "abcdef", "hehe", "qwer" };//数组里每一个元素,都是字符串的首地址,指针int i = 0;for (i = 0; i < 3; i++){printf("%s\n", arr[i]);//arr[i] <=> *(arr+i)}return 0;
}
通过指针数组里,每个元素(首元素地址 - 指针),循环打印出数组里完整的字符串
int main()
{//创建3个一维数组int arr1[] = { 1,2,3,4,5 };int arr2[] = { 2,3,4,5,6 };int arr3[] = { 3,4,5,6,7 };//arr[i] == *(arr+i)//创建arr指针数组 ,是一个存放整型指针的数组int* arr[] = { arr1, arr2, arr3 };//将3个一维数组的首元素地址(指针)存储int i = 0;for (i = 0; i < 3; i++){int j = 0;for (j = 0; j < 5; j++){//printf("%d ", arr[i][j]);printf("%d ", *(arr[i]+j));//arr[i]遍历一维数组,arr[i][j]遍历得到一维数组里的每个元素,}printf("\n");}return 0;
}
整形指针: int * pint; 能够指向整形数据的指针。浮点型指针: float * pf; 能够指向浮点型数据的指针。那数组指针应该是:能够指向数组的指针。
//p1, p2分别是什么?
int *p1[10]; //指针数组
int (*p2)[10];//数组指针如果容易混淆,可以试着这样分辨:1)int *p1[10]; 去掉命名为 int * [10] ,可以发现是一个拥有10个元素的数组,该数组里每个元素的类型是 int * 也就是整型指针类型 ,最后结合起来就是,指针数组了2)int (*p2)[10];//解释:p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个 指针,指向一个数组,叫数组指针。//这里要注意:[]的优先级要高于*号的,所以必须加上()来保证p先和*结合简单来说,指针p先和谁结合,他就是什么东西:int *p1[10] 先和 [] 结合,则为数组(然后是int * ,类型为整型指针)则为指针数组;int (*p2)[10] 先和*结合 ,则为指针(然后是int [], 整型数组)则为数组指针
arr 和 &arr 有什么区别?
数组名绝大部分情况下是数组首元素的地址
但是有2个例外:
1. sizeof(数组名) - sizeof内部单独放一个数组名的时候。sizeof(arr) - 此时数组名表示的整个数组,计算得到的是数组的总大小
2. &arr - 这里的数组名表示整个数组,取出的是整个数组的地址,从地址值的角度来讲和数组首元素的地址是一样的,但是意义不一样
int main()
{int arr[10] = { 0 };printf("%d\n", sizeof(arr)); // 此时arr表示整个数组,计算得到整个数组的内存大小 - 40字节printf("%p\n", arr);//int * //arr数组名通常为首元素地址printf("%p\n", arr+1);//首元素地址 +1 ,指针往后移动一个int长度(4字节)printf("%p\n", &arr[0]);//int* //取首元素地址printf("%p\n", &arr[0]+1);//首元素地址 +1 ,指针往后移动一个int长度(4字节)printf("%p\n", &arr);//int(*)[10] //取数组地址 - 此时地址代表整个数组printf("%p\n", &arr+1);//数组地址+1,跳过整个数组(移动了40个字节)int (*p)[10] = &arr;//p是一个数组指针//int(*)[10]return 0;
}
之所以说 arr 和arr[0]是首元素地址,是因为+1之后它们都只跳过一个整型,4个字节
而&arr +1后跳过了整个数组,40个字节(看16进制末尾两个数字,68-40=28H,换算成十进制,2*16^1 + 8*16^0 = 32+8 =40 )
从而推断它们指针的类型为 :
arr 和 arr[0] - int* 整型指针(+1跳过一个整型)
&arr - int (*)[] 数组指针 (+1跳过整个数组)
2.数组指针的运用
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int sz = sizeof(arr) / sizeof(arr[0]);int (* p)[10] = &arr;//数组指针 - 指向arr整个数组int i = 0;//p --- &arr//*p --- *&arr//*p --- arr//虽然对,但是不推荐//for (i = 0; i < sz; i++)//{// printf("%d ", (*p)[i]);//*p解引用数组指针,得到arr数组,再通过下标i得到arr数组的每个元素//}//虽然对,但是不推荐//for (i = 0; i < sz; i++)//{// printf("%d ", *((*p) + i));//因为p - &arr , *p - arr,arr即首元素地址,arr[i] <=> *(arr+1)// //所以,得到*((*p)+1)//}//使用指针来访问//int* p = arr;//for (i = 0; i < sz; i++)//{// printf("%d ", *(p + i));//}//下标的形式访问数组//for (i = 0; i < sz; i++)//{// printf("%d ", arr[i]);//}return 0;
}
打印出数组的内容:
1.最简单的办法就是通过下标 和 指针
2.通过数组指针访问数组元素(不太推荐)
有点杀鸡用牛刀的感觉,没有必要。
上面说过,数组指针是一个指向数组的指针,+1就跳过整个数组。而我们仅仅是想要打印数组的每个元素而已,就用下标或者普通的整型指针就可以了。
但是也可以试试用数组指针
1)第一种写法:解引用 数组指针 得到数组,再通过下标访问元素
int(*p)[10] = &arr , p的值为数组地址&arr,*p解引用一次,*(&arr)得到数组arr,
数组arr[]用下标再去访问每个元素,实现代码
2)第二种写法:解引用 数组指针 得到数组,再通过普通整型指针访问元素
同样的
p --- &arr
*p --- *&arr
*p --- arr*p得到首元素地址,(*p)+1 地址+1,移动一个整型,再用 *((*p)+1 ) 再对整个地址解引用,得到元素
//一维数组传参,形参是数组
void print1(int arr[10], int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}printf("\n");
}
//一维数组传参,形参是指针
void print2(int *arr, int sz)
{int i = 0;for (i = 0; i < sz; i++){//printf("%d ", arr[i]);printf("%d ", *(arr+i));}printf("\n");
}
int main()
{int arr[10] = {1,2,3,4,5,6,7,8,9,10};int sz = sizeof(arr) / sizeof(arr[0]);print1(arr, sz);printf("\n");print2(arr, sz);return 0;
}

2.二维数组传参(形参是 二维数组 或者 数组指针)
//二维数组传参,形参是 二维数组
void print1(int arr[3][5], int r, int c)
{int i = 0;for (i = 0; i < 3; i++){int j = 0;for (j = 0; j < 5; j++){printf("%d ", arr[i][j]);}printf("\n");}
}
//二维数组传参,形参是 数组指针 - 指向第一个一维数组的指针
void print2(int(*arr)[5], int r, int c)
{int i = 0;for (i = 0; i < 3; i++){int j = 0;for (j = 0; j < 5; j++){//printf("%d ", *(*(arr + i) + j));//arr[i]printf("%d ", arr[i][j]);//arr[i]}printf("\n");}
}
int main()
{int arr[3][5] = {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7};//二维数组的数组名,也表示首元素的地址//二维数组的首元素是第一行//首元素的地址就是第一行的地址,是一个一维数组的地址print1(arr, 3, 5);printf("\n");print2(arr, 3, 5);return 0;
}
1)二维数组传参,形参是 二维数组
int arr[3][5]传上去的是首元素的地址,也就是第一个一维数组的地址。
arr[i]得到每一个一维数组, arr [i][j]进而得到一维数组里的每个元素,这样,二维数组里每个元素便得到了
2)二维数组传参,形参是 数组指针 - 指向第一个一维数组的指针
int(*arr)[5] 传上去的是数组指针,指向第一个一维数组的首元素地址,同理遍历得到每个元素的地址

函数指针:指向函数的指针&函数名 得到函数的地址
void test(){printf("hehe\n");}//下面pfun1和pfun2哪个有能力存放test函数的地址?void (*pfun1)();void *pfun2();pfun1可以存放。pfun1先和*结合,说明pfun1是指针,指针指向的是一个函数,指向的函数无参 数,返回值类型为void。
int Add(int x, int y)
{return x + y;
}
int main()
{//printf("%p\n", &Add);//printf("%p\n", Add);//pf就是函数指针int (* pf)(int, int) = Add;//函数的地址要存起来,就得放在【函数指针变量】中int ret = (*pf)(3, 5);//int ret = Add(3, 5);//int ret = pf(3, 5);printf("%d\n", ret);//&函数名得到就是函数的地址printf("%p\n", Add);printf("%p\n", &Add);return 0;
}
2.可以给函数指针重命名
typedef int* ptr_t;
typedef void(*pf_t)(int);//将void(*)(int)类型重新起个别名叫pf_ttypedef void(*pf_t2)(int);//pf_t2是类型名
void(*pf)(int);//pf是函数指针变量的名字
//代码1( *( void (* )( ) )0 )( );该代码是一次函数调用1. 将0强制类型转换为void (*)() 类型的函数指针
2. 这就意味着0地址处放着一个函数,函数没参数,返回类型是void
3. 调用0地址处的这个函数
//代码2void (*signal(int , void (*)(int) ) )( int );gai代码是一个函数的声明
函数的名字是signal
signal函数的参数第一个是int类型,第二个是void(*)(int)类型的函数指针
该函数指针指向的函数参数是int,返回类型是void
signal函数的返回类型也是一个函数指针
该函数指针指向的函数参数是int,返回类型是void
void (* signal(int, void(*)(int)))(int)
相关文章:

指针的进阶
一、字符指针 int main() {char ch w;char* pc &ch;//pc就是字符指针//const char *p "abcdef";//这里其实是把字符串"abcdef"的首地址放入了指针p中//*p w;//这是错误的无法修改值(可以看到这里绿色波浪线警告)char arr[] …...
一元二次方程方程的类
1 问题设计一个一元二次方程的类,其中包括能够反映一元二次方程的属性与操作行为,然后再设计一个测试类,检测类的使用情况。2 方法使用package语句将方程的属性即计算跟的方法封装在一个有包名的类中,包名为tom.jiafei,…...

Ask林曦|来回答,30个你关心的日常问题(二)
在林曦老师的线上书法直播课上,上课前后的聊天时间里,时常有同学向林曦老师提问,这些问题涵盖了日常生活的诸多方面,从身体的保养,到快乐的法门,皆是大家感兴趣的,也都共同关切的。 暄桐教室…...

哪款电容笔适合开学季?电容笔和Apple Pencil的区别
其实,市场上一般的电容笔和Apple Pencil的最大差别,就在于Apple Pencil与普通电容笔两者的重量和压感。然而,由于苹果电容笔价格过高,目前电容笔的市场份额逐渐转向平替电容笔,平替电容笔其性能也逐渐得到改善。下面&a…...
Qt之Qprocess
QProcess 可用于完成启动外部程序,并与之交互通信。 一、启动外部程序的两种方式 1)一体式:void QProcess::start(const QString & program,const QStringList &arguments,OpenMode mode ReadWrite) 外部程序启动后&…...
为什么不愿意专升本 学历有什么用
专升本包括两种形式普通专升本和成人专升本。普通专升本毕业是全日制学历,考试仅有一次,错过不能补考所以考生不愿意选择,成人专升本毕业是非全日制学历,学历被国家承认,和普通高校毕业证有相同的使用效力。为何考生不…...
构造函数的使用大全
概述 在C中创建一个对象时,通常需要做一些数据初始化的工作,因此便提供了一个特殊的成员函数 —— 构造函数。一般情况下,并不需要程序员主动调用构造函数,而是在创建对象时,由系统自动调用。构造函数可以由程序员定义…...

ASP.NET Core MVC 项目 IOC容器
目录 一:什么是IOC容器 二:简单理解内置Ioc容器 三:依赖注入内置Ioc容器 四:生命周期 五:多种注册方式 一:什么是IOC容器 IOC容器是Inversion Of Control的缩写,翻译的意思就是控制反转。 …...
ARM工控机/网关- 钡铼技术
一、NXP处理器ARM控制器的介绍 NXP半导体是汽车、穿戴、消费电子等领域中智能机器解决方案的领先供应商。其产品线庞大,包括处理器、微控制器、快速设计平台、ARM控制器等。在物联网控制、汽车电子、安全应用等领域,NXP处理器ARM控制器已成为半导体行业的…...

为什么都在喊数据可视化?它究竟怎么做?
在数字化转型的浪潮中,不论是传统行业,还是新兴行业总会提到“数据可视化”这个词。那数据可视化到底是什么?为什么会受到那么多人追捧?又该怎么才能做到数据可视化呢? 一、数据可视化是什么? 首先“可视…...

nodejs+vue停车场停车位短租系统vscode
目 录前端技术:nodejsvueelementui 前端:HTML5,CSS3、JavaScript、VUE 1、 node_modules文件夹(有npn install产生) 这文件夹就是在创建完项目后,cd到项目目录执行npm install后生成的文件夹,下载了项目需要的依赖项。 2、…...
物理真机上LUKS结合TPM的测试 —— 使用随机数密钥
1. 创建磁盘空间 命令如下: dd if/dev/zero ofenc.disk bs1M count50 实际命令及结果如下: $ dd if/dev/zero ofenc.disk bs1M count50 输入了 500 块记录 输出了 500 块记录 52428800 字节 (52 MB, 50 MiB) 已复制,0.0587495 sÿ…...

Linux USB 开发指南
文章目录Linux USB 开发指南1 前言1.1 文档简介1.2 目标读者1.3 适用范围2 模块介绍2.1 模块功能介绍2.2 相关术语介绍2.3 模块配置介绍2.3.1 Device Tree 配置说明2.3.2 board.dts 配置说明2.3.3 kernel menuconfig 配置说明2.4 源码结构介绍2.5 驱动框架介绍2.6 Gadget 配置2…...

FreeRTOS入门(03):队列、信号量、互斥量
文章目录目的队列(queue)信号量(semaphore)互斥量(mutex)互斥量递归互斥量总结目的 FreeRTOS提供给用户最核心的功能是任务(Task),实际项目中通常会有多个任务ÿ…...

Biome-BGC在模拟过程中,如何使用Linux、Python等,完成前处理和后处理工作???
在Biome-BGC模型中,对于碳的生物量积累,采用光合酶促反应机理模型计算出每天的初级生产力(GPP),将生长呼吸和维持呼吸减去后的产物分配给叶、枝条、干和根。生物体的碳每天都按一定比例以凋落方式进入凋落物碳库;对于水份输运过程…...

【unittest学习】unittest框架主要功能
1.认识unittest在 Python 中有诸多单元测试框架,如 doctest、unittest、pytest、nose 等,Python 2.1 及其以后的版本已经将 unittest 作为一个标准模块放入 Python 开发包中。2.认识单元测试不用单元测试框架能写单元测试吗?答案是肯定的。单…...

京东测开岗3+1面经+经验分享,拿到offer,月薪34k....
现在,招聘黄金时间已经来临,在网上看了很多大佬的面经,也加了很多交流群,受到了很多朋友的提点,今天终于轮到我来分享面经啦,之前面试了几家公司,最后拿到了京东测试岗的 offer,这里…...

后端接收格式为x-www-form-urlencoded的数据
1.x-www-form-urlencoded是什么? x-www-form-urlencoded纸面翻译即所谓url格式的编码,是post的默认Content-Type,其实就是一种编码格式,类似json也是一种编码传输格式。form表单中使用 form的enctype属性为编码方式࿰…...

LeetCode 707. 设计链表
LeetCode 707. 设计链表 难度:middle\color{orange}{middle}middle 题目描述 设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性:valvalval 和 nextnextnext。valvalval 是当前节点的值,nextnextnext 是指向下…...
HTTP的主要作用是什么
1、客户与服务器建立连接; 2、客户向服务器提出请求; 3、服务器接受请求,并根据请求返回相应的文件作为应答; 4、客户与服务器关闭连接。 HTTP的性质: 1、HTTP是一种无状态协议,即服务器不保留与客户交…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
AspectJ 在 Android 中的完整使用指南
一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...
力扣-35.搜索插入位置
题目描述 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...

Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...

AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别
【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而,传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案,能够实现大范围覆盖并远程采集数据。尽管具备这些优势…...
多模态图像修复系统:基于深度学习的图片修复实现
多模态图像修复系统:基于深度学习的图片修复实现 1. 系统概述 本系统使用多模态大模型(Stable Diffusion Inpainting)实现图像修复功能,结合文本描述和图片输入,对指定区域进行内容修复。系统包含完整的数据处理、模型训练、推理部署流程。 import torch import numpy …...

(一)单例模式
一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...
Spring Security 认证流程——补充
一、认证流程概述 Spring Security 的认证流程基于 过滤器链(Filter Chain),核心组件包括 UsernamePasswordAuthenticationFilter、AuthenticationManager、UserDetailsService 等。整个流程可分为以下步骤: 用户提交登录请求拦…...