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

指针的进阶

一、字符指针
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;
}
        这里str3str4指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域,当 几个指针。指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化 不同的数组的时候就会开辟出不同的内存块。所以str1str2不同,str3str4不同。

 

 

二、 指针数组
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;
}

通过指针数组里,每个元素(首元素地址 - 指针),循环打印出数组里完整的字符串

2.用一维数组模拟实现二维数组
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 [], 整型数组)则为数组指针
1.&数组名VS数组名

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 ) 再对整个地址解引用,得到元素

 

四、数组传参和指针传参
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] 传上去的是数组指针,指向第一个一维数组的首元素地址,同理遍历得到每个元素的地址

 

五、 函数指针
函数指针:指向函数的指针
&函数名 得到函数的地址
1.函数指针
void test()
{
printf("hehe\n");
}
//下面pfun1pfun2哪个有能力存放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_t

typedef void(*pf_t2)(int);//pf_t2是类型名
void(*pf)(int);//pf是函数指针变量的名字
 

3.两段有趣的代码(眼前一黑)
//代码1
( *void ()( ) )0 )( );
该代码是一次函数调用
1. 将0强制类型转换void (*)() 类型的函数指针
2. 这就意味着0地址处放着一个函数函数没参数,返回类型是void
3. 调用0地址处的这个函数
 
//代码2
void (*signal(int , void (*)(int) ) )( int );
gai代码是一个函数的声明
    函数的名字是signal
    signal函数的参数第一个是int类型,第二个是void(*)(int)类型的函数指针
    该函数指针指向的函数参数是int,返回类型是void
     
    signal函数的返回类型也是一个函数指针
    该函数指针指向的函数参数是int,返回类型是void
    
    void (* signal(int, void(*)(int)))(int)
6. 函数指针数组
7. 指向函数指针数组的指针
8. 回调函数
9. 指针和数组面试题的解析

相关文章:

指针的进阶

一、字符指针 int main() {char ch w;char* pc &ch;//pc就是字符指针//const char *p "abcdef";//这里其实是把字符串"abcdef"的首地址放入了指针p中//*p w;//这是错误的无法修改值&#xff08;可以看到这里绿色波浪线警告&#xff09;char arr[] …...

一元二次方程方程的类

1 问题设计一个一元二次方程的类&#xff0c;其中包括能够反映一元二次方程的属性与操作行为&#xff0c;然后再设计一个测试类&#xff0c;检测类的使用情况。2 方法使用package语句将方程的属性即计算跟的方法封装在一个有包名的类中&#xff0c;包名为tom.jiafei&#xff0c…...

Ask林曦|来回答,30个你关心的日常问题(二)

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

哪款电容笔适合开学季?电容笔和Apple Pencil的区别

其实&#xff0c;市场上一般的电容笔和Apple Pencil的最大差别&#xff0c;就在于Apple Pencil与普通电容笔两者的重量和压感。然而&#xff0c;由于苹果电容笔价格过高&#xff0c;目前电容笔的市场份额逐渐转向平替电容笔&#xff0c;平替电容笔其性能也逐渐得到改善。下面&a…...

Qt之Qprocess

QProcess 可用于完成启动外部程序&#xff0c;并与之交互通信。 一、启动外部程序的两种方式   1&#xff09;一体式&#xff1a;void QProcess::start(const QString & program,const QStringList &arguments,OpenMode mode ReadWrite)     外部程序启动后&…...

为什么不愿意专升本 学历有什么用

专升本包括两种形式普通专升本和成人专升本。普通专升本毕业是全日制学历&#xff0c;考试仅有一次&#xff0c;错过不能补考所以考生不愿意选择&#xff0c;成人专升本毕业是非全日制学历&#xff0c;学历被国家承认&#xff0c;和普通高校毕业证有相同的使用效力。为何考生不…...

构造函数的使用大全

概述 在C中创建一个对象时&#xff0c;通常需要做一些数据初始化的工作&#xff0c;因此便提供了一个特殊的成员函数 —— 构造函数。一般情况下&#xff0c;并不需要程序员主动调用构造函数&#xff0c;而是在创建对象时&#xff0c;由系统自动调用。构造函数可以由程序员定义…...

ASP.NET Core MVC 项目 IOC容器

目录 一&#xff1a;什么是IOC容器 二&#xff1a;简单理解内置Ioc容器 三&#xff1a;依赖注入内置Ioc容器 四&#xff1a;生命周期 五&#xff1a;多种注册方式 一&#xff1a;什么是IOC容器 IOC容器是Inversion Of Control的缩写&#xff0c;翻译的意思就是控制反转。 …...

ARM工控机/网关- 钡铼技术

一、NXP处理器ARM控制器的介绍 NXP半导体是汽车、穿戴、消费电子等领域中智能机器解决方案的领先供应商。其产品线庞大&#xff0c;包括处理器、微控制器、快速设计平台、ARM控制器等。在物联网控制、汽车电子、安全应用等领域&#xff0c;NXP处理器ARM控制器已成为半导体行业的…...

为什么都在喊数据可视化?它究竟怎么做?

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

nodejs+vue停车场停车位短租系统vscode

目 录前端技术&#xff1a;nodejsvueelementui 前端&#xff1a;HTML5,CSS3、JavaScript、VUE 1、 node_modules文件夹(有npn install产生) 这文件夹就是在创建完项目后&#xff0c;cd到项目目录执行npm install后生成的文件夹&#xff0c;下载了项目需要的依赖项。 2、…...

物理真机上LUKS结合TPM的测试 —— 使用随机数密钥

1. 创建磁盘空间 命令如下&#xff1a; dd if/dev/zero ofenc.disk bs1M count50 实际命令及结果如下&#xff1a; $ dd if/dev/zero ofenc.disk bs1M count50 输入了 500 块记录 输出了 500 块记录 52428800 字节 (52 MB, 50 MiB) 已复制&#xff0c;0.0587495 s&#xff…...

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):队列、信号量、互斥量

文章目录目的队列&#xff08;queue&#xff09;信号量&#xff08;semaphore&#xff09;互斥量&#xff08;mutex&#xff09;互斥量递归互斥量总结目的 FreeRTOS提供给用户最核心的功能是任务&#xff08;Task&#xff09;&#xff0c;实际项目中通常会有多个任务&#xff…...

Biome-BGC在模拟过程中,如何使用Linux、Python等,完成前处理和后处理工作???

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

【unittest学习】unittest框架主要功能

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

京东测开岗3+1面经+经验分享,拿到offer,月薪34k....

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

后端接收格式为x-www-form-urlencoded的数据

1.x-www-form-urlencoded是什么&#xff1f; x-www-form-urlencoded纸面翻译即所谓url格式的编码&#xff0c;是post的默认Content-Type&#xff0c;其实就是一种编码格式&#xff0c;类似json也是一种编码传输格式。form表单中使用 form的enctype属性为编码方式&#xff0…...

LeetCode 707. 设计链表

LeetCode 707. 设计链表 难度&#xff1a;middle\color{orange}{middle}middle 题目描述 设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性&#xff1a;valvalval 和 nextnextnext。valvalval 是当前节点的值&#xff0c;nextnextnext 是指向下…...

HTTP的主要作用是什么

1、客户与服务器建立连接&#xff1b; 2、客户向服务器提出请求&#xff1b; 3、服务器接受请求&#xff0c;并根据请求返回相应的文件作为应答&#xff1b; 4、客户与服务器关闭连接。 HTTP的性质&#xff1a; 1、HTTP是一种无状态协议&#xff0c;即服务器不保留与客户交…...

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...

遍历 Map 类型集合的方法汇总

1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

学校招生小程序源码介绍

基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码&#xff0c;专为学校招生场景量身打造&#xff0c;功能实用且操作便捷。 从技术架构来看&#xff0c;ThinkPHP提供稳定可靠的后台服务&#xff0c;FastAdmin加速开发流程&#xff0c;UniApp则保障小程序在多端有良好的兼…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)

可以使用Sqliteviz这个网站免费编写sql语句&#xff0c;它能够让用户直接在浏览器内练习SQL的语法&#xff0c;不需要安装任何软件。 链接如下&#xff1a; sqliteviz 注意&#xff1a; 在转写SQL语法时&#xff0c;关键字之间有一个特定的顺序&#xff0c;这个顺序会影响到…...

Nuxt.js 中的路由配置详解

Nuxt.js 通过其内置的路由系统简化了应用的路由配置&#xff0c;使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...

【python异步多线程】异步多线程爬虫代码示例

claude生成的python多线程、异步代码示例&#xff0c;模拟20个网页的爬取&#xff0c;每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程&#xff1a;允许程序同时执行多个任务&#xff0c;提高IO密集型任务&#xff08;如网络请求&#xff09;的效率…...

Swagger和OpenApi的前世今生

Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章&#xff0c;二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑&#xff1a; &#x1f504; 一、起源与初创期&#xff1a;Swagger的诞生&#xff08;2010-2014&#xff09; 核心…...

是否存在路径(FIFOBB算法)

题目描述 一个具有 n 个顶点e条边的无向图&#xff0c;该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序&#xff0c;确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数&#xff0c;分别表示n 和 e 的值&#xff08;1…...

3-11单元格区域边界定位(End属性)学习笔记

返回一个Range 对象&#xff0c;只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意&#xff1a;它移动的位置必须是相连的有内容的单元格…...

优选算法第十二讲:队列 + 宽搜 优先级队列

优选算法第十二讲&#xff1a;队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...