指针详解(3)

各位少年,大家好,我是博主那一脸阳光,今天介绍 二级指针 指针数组,还有个指针数组模拟二维数组。
前言:在浩瀚的C语言编程宇宙中,指针犹如一把打开内存世界大门的独特钥匙,它不仅是理解程序运行机制的关键要素,也是提升代码执行效率的重要工具。如同寻宝图上的经纬坐标,指针精准地指向了内存中的特定位置,使得我们能够直接操作数据的核心。

想象一下,计算机内存就像一座巨大的储物仓库,每个存储单元都承载着独一无二的信息宝藏。而指针,则如同仓库管理员手中的定位器,通过它可以迅速找到并访问任何一个角落的数据。当我们改变指针所指向的位置时,就好比调整了探索目标,实现对不同信息资源的快速定位和灵活调动。

因此,在深入学习C语言的过程中,掌握指针这一概念及其使用方法,就如同掌握了驾驭数据流动的秘密通道。它不仅有助于我们更深入地洞察程序运行的本质,还能使代码更为简洁高效,更具表现力。接下来,让我们一同踏上这段揭示指针奥秘的旅程,揭开其背后深藏的编程智慧与艺术。

二级指针的定义

int a10;
int*p=&a;
&p;
//p是指针变量,是一级指针
int **pp=&p;//int *是在说明,pp对象指向的对象的int*类型
//*说明pp是指针变量

这里的pp是二级指针,指针类型进行+1 -1的操作,执行解引用的权限。
注意这里的pp是另外开辟了一块空间。

int a = 10;
	int* p = &a;
	int**pp = &p;
	int*** ppp = &pp;
	return 0;
}

指针数组

我们类比一下
指针数组是指针还是数组呢?(数组中每个元素都是整形类型)
整形数组-存放整形数据的数组(数组中每个元素都是字符类型)
指针数组-存放指针的数组(数组中每个元素都是指针类型)

int arr[10];//整形数组 
char ch[5];//字符数组
double data[4];//多浮点型数组

希望有一个数组,数组有四个元素,每个元素是整形指针

int arr[4];

每个元素是整形指针,所以指针数组。

指针模拟二维数组

模拟二维数组的效果,但不是二维数组!
二维数组其实每一行都是一维数组。

#include<stdio.h>
int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 2,3,4,5,6 };
	int arr3[] = { 3,4,5,6,7 };
	int *arr[3] = { arr1,arr2,arr3 };
	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");
	}
	return 0;
}

上面指针的方式存储了三个数组的地址,然后进行遍历,最后进行每一位,最后执行了二维数组的打印。

字符串指针类型

char ch='w';
char*pc=&ch;   //pc是字符指针
char *p"abcdef"://叫做常量字符串
printf("%c\n","abcdef"[3]);

字符指针的类型是可以赋值的,但非传统赋值,
不是把字符串abcdef\0存放在p中,
而是把第一个字符的地址存放在p中
意思就是说p存储a的地址,你可以把abcdef这个看做出一个数组。

1你可以把字符串想象为一个字符数组,但是这个数组是不能修改的
2当常量字符串出现在表达式中的时候,它的值是第一个字符的地址。

#include<stdio.h>
int main()
{
	char* p = "abcdef";
	printf("%c\n", p[3]);
	p[3] = 'q';
	return 0;
}

此时注意p没办法间接修改它,因为p的字符串是常量,所以建议在char前面加const以避免误导。

剑指offer面试题

今天来介绍《剑指offer》一书中的题目

#include<stdio.h>
int main()
{
	char str1[] = "hello bit.";
	char str2[] = "hello bit.";
	const char* str3 = "hello bit.";
	const char* str4 = "hello bit.";
	if (str1 == str2)
		printf("str1 and str2 are same\n");
	else
		printf("str1 and str2 are not same\n");
	if(str3==str4)
		printf("str3 and str4 are same\n");
	else
		printf("str3 and str4 are not same\n");
	return 0;
}

在这里插入图片描述
上面代码str1和str2不同这是为什么呢?
这就好比两个一模一样的背包,其中有一个可能不是你的,所以地址是不相同的。
str3和str4常量字符串,不可能修改了,就好比你和你女朋友的包,你都得背一样。
在这里插入图片描述
因为str3和srt4都是常量,无法更改,所以计算机偷懒了,只开辟了一块空间。

数组指针变量

指针数组:是数组,是存放指针的数组!
哪数组指针是什么呢?
我们类比一下:
整形指针:指向整形的指针。
字符指针:指向字符的指针。
浮点型指针:指向浮点型的指针。

数组指针 指向数组的指针!

数组指针变量应该是:存放的应该是数组的地址,能够指向数组的指针的变量。

整形指针变量存放的就是整形的地址
字符指针变量存放的就是字符的地址
数组指针变量存放的应该是数组的地址

 int*pa)[10];

数组指针变量存放数组的地址,里面每个类型都是int类型。

 int arr[10]={1,2,3,4,5,6,7,8,9,10};
 int(*parr)[10]=&arr;

在这里插入图片描述

解释:先和结合,说明p是⼀个指针变量变量,然后指着指向的是⼀个⼤⼩为10个整型的数组。所以
p是⼀个指针,指向⼀个数组,叫 数组指针。
这⾥要注意:[]的优先级要⾼于
号的,所以必须加上()来保证p先和*结合。

我们调试也能看到 &arr 和 p 的类型是完全⼀致的。
数组指针类型解析:

int (*p) [10] = &arr;
 | | |
 | | |
 | | p指向数组的元素个数
 | p是数组指针变量名
 p指向的数组的元素类型
int arr[10]={1,2,3,4,5,6,7,8,9,10};
int(*p)[10]=&arr;
printf("%p\n",arr);
printf("%p\n,&arr+1);

printf("%p\n",p);
printf("%p\n,p+1);

还记得我们之前说过这个代码,如果打印整个数组的地址+1跳过整个数组,
如果取地址数组名打印跳过整个数组。那好我们看打印结果
在这里插入图片描述
接下来介绍数组指针是怎么打印的呢?

#include<stdio.h>
int main()
{
int arr[10]={1,2,3,4,5,6,7,8,9,10};
int(*p)[10]=&arr;
int i=0;
int sz=sizeof(arr)/sizeof(arr[0]);
for(i=0;i<sz;i++)
{
printf("%d ",*(p+i));
}
return 0;
}

在这里插入图片描述
因为数组指针,是数组的地址+1跳过了整个数组,但是解决办法还是有的。
p==&arr
因为数组指针,数组本来就是地址所以p等于取地址arr。

*p== *&arr==arr
#include<stdio.h>
int main()
{
int arr[10]={1,2,3,4,5,6,7,8,9,10};
int(*p)[10]=&arr;
int i=0;
int sz=sizeof(arr)/sizeof(arr[0]);
for(i=0;i<sz;i++)
{
printf("%d ",(*p)[i]);
return 0;
}

这样就可以是数组指针每个元素了。

二维数组传参的本质

有了数组指针的理解,我们就能够讲⼀下⼆维数组传参的本质了。
过去我们有⼀个⼆维数组的需要传参给⼀个函数的时候,我们是这样写的:

void test(int a[3][5], int r, int c)
{
 int i = 0;
 int j = 0;
  for(i=0; i<r; i++)
 {
 for(j=0; j<c; j++)
 {
 printf("%d ", a[i][j]);
 }
 printf("\n");
 }
}
int main()
{
 int arr[3][5] = {{1,2,3,4,5}, {2,3,4,5,6},{3,4,5,6,7}};
 test(arr, 3, 5);
 return 0;
}

这⾥实参是⼆维数组,形参也写成⼆维数组的形式,那还有什么其他的写法吗?
⾸先我们再次理解⼀下⼆维数组,⼆维数组起始可以看做是每个元素是⼀维数组的数组,也就是⼆维
数组的每个元素是⼀个⼀维数组。那么⼆维数组的⾸元素就是第⼀⾏,是个⼀维数组。
如下图:
在这里插入图片描述
所以,根据数组名是数组⾸元素的地址这个规则,⼆维数组的数组名表⽰的就是第⼀⾏的地址,是⼀
维数组的地址。根据上⾯的例⼦,第⼀⾏的⼀维数组的类型就是 int [5] ,所以第⼀⾏的地址的类
型就是数组指针类型 int(*)[5] 。那就意味着⼆维数组传参本质上也是传递了地址,传递的是第⼀
⾏这个⼀维数组的地址,那么形参也是可以写成指针形式的。如下:

#include <stdio.h>
void test(int (*p)[5], int r, int c)
{
 int i = 0;
int j = 0;
 for(i=0; i<r; i++)
 {
 for(j=0; j<c; j++)
 {
 printf("%d ", *(*(p+i)+j));
 }
 printf("\n");
 }
}
int main()
{
 int arr[3][5] = {{1,2,3,4,5}, {2,3,4,5,6},{3,4,5,6,7}};
 test(arr, 3, 5);
 return 0;
}

总结:⼆维数组传参,形参的部分可以写成数组,也可以写成指针形式。

函数指针变量

函数指针顾名思义函数的指针的,函数的地址,哪问题来了,它有什么用呢?

数组名- -数组首元素的地址
&数组名–整个数组的地址。
函数名:函数的地址
&函数名:函数的地址

函数指针的写的方法和数组指针的创建方式非常的类似的

data type(*Pointer name)(Function Parameter type)

数据类型 指针名字 和指针类型组成的函数指针,
这里大家可能看不懂,下面我来分享给大家例子。

int(*pf)(int,int)=&Add;

pf就是函数指针变量。下面例子是函数指针基本的格式。这里必须加(),否则int先和*结合就是函数传参了。

int *pf=(int,int);.//这里不加()就变成函数传参 如果再加等于Add的话直接报错

函数的使用的例子

#include<stdio.h>
int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int(*pf)(int,int) = &Add;//pf就是函数指针变量
	//原来我们是不是这样写
	int ret=Add(35);
	printf("%d\n",ret);//8
	//新的写法
	int ret2=(*pf)(4,9);//pf解引用,然后传入两个值
	printf("%d\n",ret2);//13
	return 0;
}

接下来再引导出一个概念,函数名和&数组名是一样的,函数的地址都是一样的。

int*pf2)(int,int)=Add;
int ret=(*pf2)(5,6);
printf("%d\n",ret3);

新问题来了 add把哪个地址放到pf2里头,pf2也是地址呀,大家可能不理解,大家只要记住可以这样写就好。

int ret4=pf2(5,6);
printf("%d\n",ret4);
#include<stdio.h>
char* test(int a, char c)
{
	return NULL;
}
int main()
{
	pt = test;
	return 0;
}

接下来分享一道题,大家不要自定义函数,以及如何使用的NULL
大家想想它该怎么写成函数指针

char*(*pt)(int,char) = test;

既然上面是char了,那我们这块也要写成char才能对称。

C陷阱与缺陷

接下来分享两段有趣的代码均出自C陷阱和缺陷这本书中。

(*(void(*)())0)();

在这里插入图片描述
上面代码中红色代表括号,蓝色代表函数指针类型,
(int)0这叫什么意思呢?(void(*))0叫做强制转换了
把0强制转换成地址 然后解引用了,然后后面括号是 参数。

void(* signal(int, void(*)(int)))(int);

我们发现*signed没有阔括号再一起,因为优先级所以signal是函数名
还记得我们之前写函数指针,**都是和函数阔在一起的,所以叫做函数名
它的第一个参数是int 第二次参数是函数指针类型,函数返回的是函数指针类型

typedef关键字

typedef叫做类型的重定义 把一个复杂名字简单化,把int改成uint

typedef unsigned int uint
int main()
{
unsigne int num;
uint num2;
return 0}
typedef int*PArr_t)[10];

数组指针也可以重新命名,但必须在星号的右边```

pArr_t pa;
typedef int(*pf_t)(int,int);
typedef int(*pf2)(int,int);

好,我们把之前的代码也简化一下

typedef void*pf_t)(int);
pf_t signal( int,pf_t);

今天就分享到这里,剩下今天给大家分享给大家指针的使用

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

牛客寒假算法基础集训营1 E本题主要考察了贪心

示例1 输入 3 4 3 2 4 5 8 1 2 1 4 2 4 3 1 3 1 1 2 3 6 6 1 2 3 4 5 6 2 3 2 3 3 4 4 5 5 6 6 1 输出 1 1 4 #include<bits/stdc.h> using namespace std; int u0,m,n; int num100,tempnum1;//tempnum表示当前这一组合鸡的排名&#xff0c;num是最终排名 pair<i…

JAVA学习之面向对象

JAVA学习之面向对象 PART ONE&#xff1a;面向对象基础 1.类与对象&#xff1a; 类是一种抽象的概念&#xff0c;它描述了一类具有相似属性和行为的对象的集合。类定义了对象的属性和行为&#xff0c;并且可以作为创建对象的模板。 对象是类的实例&#xff0c;它是类的具体…

[RAM] DRAM 导论:DDR4 | DDR5 | LPDDR5 | GDRR6 | HBM 应运而生

主页&#xff1a;元存储 全文 7700 字&#xff0c;原创请勿转载。 DRAM: where memory meets potential." - Bill Gates 前言 有数据的地方就有存储&#xff0c; 内存是谁都离不开的产品。DRAM主要负责硬盘、主板、显卡等硬件与处理器之间的数据交换。 本博客将介绍D…

STM32--HAL库定时器学习记录(易懂)--持续学习

一、什么是定时器 定时器就是计数器&#xff0c;通过计数完成一系列功能。 二、定时器的分类 定时器分为基本定时器、通用定时器、高级定时器。级别不同&#xff0c;功能不同。级别越高&#xff0c;功能越强。 三、定时器&#xff08;计数器&#xff09;三个重要寄存器 预分…

快速上手AI代码生成:CodeGeeX

1. VSCode等IDE插件直接搜到 CodeGeeX 就可以安装&#xff0c;装上之后&#xff0c;会看到左侧出现了&#xff1a; 2. 登陆以后&#xff0c;在代码区域就可以直接用了。 3. 官网功能说明文档&#xff08;非常简洁清晰&#xff09;&#xff1a; https://zhipu-ai.feishu.cn/wik…

机器学习系列——(九)决策树

简介 决策树作为机器学习的一种经典算法&#xff0c;在数据挖掘、分类和回归等任务中广泛应用。本文将详细介绍机器学习中的决策树算法&#xff0c;包括其原理、构建过程和应用场景。 原理 决策树是一种基于树状结构的监督学习算法&#xff0c;它通过构建一棵树来对数据进行分…

以太网帧格式及ARP协议简介

在以太网中&#xff0c;一个主机和另一个主机进行通信&#xff0c;必须要知道目的主机的MAC地址&#xff08;物理地址&#xff09;&#xff0c;只要知道目的主机的IP地址&#xff0c;就可以通过ARP协议获取目的主机的MAC地址。 1、ARP协议简介 ARP&#xff08;Address Resolut…

2024年【熔化焊接与热切割】考试题及熔化焊接与热切割操作证考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 熔化焊接与热切割考试题根据新熔化焊接与热切割考试大纲要求&#xff0c;安全生产模拟考试一点通将熔化焊接与热切割模拟考试试题进行汇编&#xff0c;组成一套熔化焊接与热切割全真模拟考试试题&#xff0c;学员可通…

京东广告算法架构体系建设--高性能计算方案最佳实践 | 京东零售广告技术团队

1、前言 推荐领域算法模型的在线推理是一个对高并发、高实时有较强要求的场景。算法最初是基于Wide & Deep相对简单的网络结构进行建模&#xff0c;容易满足高实时、高并发的推理性能要求。但随着广告模型效果优化进入深水区&#xff0c;基于Transformer用户行为序列和Att…

网络原理TCP/IP(3)

文章目录 滑动窗口流量控制拥塞控制延迟应答捎带应答 滑动窗口 确认应答策略,对每⼀个发送的数据段,都要给⼀个ACK确认应答.收到ACK后再发送下⼀个数据段.这样做有⼀个比较大的缺点,就是性能较差.尤其是数据往返的时间较长的时候. 为了解决低效率传输&#xff0c;引入了滑动窗…

镜舟科技客户成功团队负责人孟庆欢:湖仓一体将成为数据架构的新范式

大数据产业创新服务媒体 ——聚焦数据 改变商业 随着数字化的概念逐步深入不同领域企业的运营中&#xff0c;业务形态和数字化路径也越来越丰富。这也为企业数据处理、储存的方式提出了更多要求。对于企业&#xff0c;尤其是数据驱动型企业来说&#xff0c;需要强大的解决方案…

STM32F407 CAN 静默回环模式 配置

CAN 静默回环模式 【 Silent_LoopBack】 自发自收&#xff0c;这种方式作“热自检”使用&#xff0c;即自我检查&#xff0c;不干扰总线&#xff0c;也不受总线干扰. 发送&#xff1a;发出的数据&#xff0c;只会传输到自己的输入端&#xff0c;不会影响总线.接收&#x…

隐写术:隐藏信息的秘密艺术

一、引言 隐写术&#xff0c;这个充满神秘色彩的词汇&#xff0c;似乎让我们回到了间谍和秘密特工的时代。但实际上&#xff0c;隐写术在现代社会仍然有着广泛的应用&#xff0c;例如在军事、情报、商业等领域。本文将带你走进隐写术的世界&#xff0c;探索它的原理、应用和防…

Vue3+TS+Vite+Pinia学习总结

VUE3介绍 vue2和vue3之间的区别 因为需要遍历data对象上所有属性&#xff0c;所以如果data对象属性结构嵌套很深&#xff0c;就会存在性能问题。因为需要遍历属性&#xff0c;所有需要提前知道对象上有哪些属性&#xff0c;才能将其转化为getter和setter,所以vue2中无法将data新…

物流平台架构设计与实践

随着电商行业的迅猛发展&#xff0c;物流行业也得到了极大的发展。从最初的传统物流到现在的智慧物流&#xff0c;物流技术和模式也在不断的更新与升级。物流平台作为连接电商和物流的重要媒介&#xff0c;其架构设计和实践显得尤为重要。 一、物流平台架构设计 1. 前端架构设…

Selenium处理Alert弹窗

页面弹窗有 3 种类型&#xff1a; alert&#xff08;警告信息&#xff09; confirm&#xff08;确认信息&#xff09; prompt&#xff08;提示输入&#xff09; 对于页面出现的 alert 弹窗&#xff0c;Selenium 提供如下方法&#xff1a; 序号 方法/属性 描述 1 ac…

leetcode正则表达式匹配问题(困难)

1.题目描述 2.解题思路&#xff0c;这道题自己没做出来&#xff0c;看了官方的题解&#xff0c;感觉对自己来说确实是比较难想的。使用了动态规划的解决方案&#xff0c;这种方案看题解都不一定能看明白&#xff0c;不过有个评论画图讲解的非常明白。其实仔细看题解的话&#…

关于网络面试题汇总

什么是TCP/IP五层模型&#xff1f;它们的作用是啥&#xff1f;基于TCP/IP实现的应用&#xff08;层协议&#xff09;有哪些&#xff1f; TCP/IP五层模型&#xff0c;从上向下分别是&#xff1a; 应用层&#xff1a;应用程序本身&#xff0c;应用层的作用是负责应用程序之间的…

Python实现PDF到HTML的转换

PDF文件是共享和分发文档的常用选择&#xff0c;但提取和再利用PDF文件中的内容可能会非常麻烦。而利用Python将PDF文件转换为HTML是解决此问题的理想方案之一&#xff0c;这样做可以增强文档可访问性&#xff0c;使文档可搜索&#xff0c;同时增强文档在不同场景中的实用性。此…

HTML+CSS:WIFI开关按钮

效果演示 实现了一个按钮的切换效果&#xff0c;当用户点击按钮时&#xff0c;按钮会从一个颜色渐变到另一个颜色&#xff0c;同时按钮的边框和阴影效果也会发生变化。同时&#xff0c;按钮的图标也会从一个颜色渐变到另一个颜色。这个效果可以用来提醒用户进行操作&#xff0c…