指针的进阶
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是一种无状态协议,即服务器不保留与客户交…...
SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...
超短脉冲激光自聚焦效应
前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
C++:std::is_convertible
C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...
聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...







