【C语言】指针的入门篇2,深入理解指针和数组的关系

欢迎来CILMY23的博客喔,本期系列为【C语言】指针的入门篇2,深入理解指针和数组的关系,图文讲解指针和数组关系的知识,带大家理解指针和数组的关系,以及指针+数组的用法,感谢观看,支持的可以给个赞哇。

前言

在上一篇博客中,我们了解了指针就是地址,并且把地址存放的变量叫做指针变量,以及指针用法,并且为了防止野指针,我们还学习了assert断言等等来防止野指针的产生,本期博客将用strlen函数的模拟实现来回顾先前知识,并学习指针和数组之间的关系。 

目录

 一、strlen的模拟实现

 二、数组名和数组首元素

二、指针访问数组 

三、一维数组的传参

 四、冒泡排序

五、二级指针

六、指针数组

七、指针数组模拟二维数组


 一、strlen的模拟实现

strlen函数,我们可以在cplusplus网站查询cplusplus.comicon-default.png?t=N7T8https://cplusplus.com/

它的功能是获取字符串的长度,返回字符串的长度。那字符串的特点是在“”的末尾有一个\0,所以我们可以采取计数的方式来统计字符串的长度。所以我们可以写出以下代码:

int my_strlen(char* str)
{
	int len = 0;
	while (*str != '\0')
	{
		len++;
		str++;
	}
	return len;
}
int main()
{
	char str[] = "hello SILMY23";
	printf("%d",my_strlen(str));
	return 0;
}

 我们可以看到结果:

 

但当我们在模拟实现的时候,往往可以发现一些问题,如果外界传入的是空指针呢?其次我们并不希望修改指针所指向的内容,因此可以加const修饰我们的形参str。 因为assert检测的时候,如果表达式为真,就不影响程序,如果为假,就停止程序并进行报错。最后,因为字符串长度是不可能有负数的,所以我们可以用size_t来代替int。

#include<stdio.h>
#include<assert.h>

size_t my_strlen(const char* str)
{
	assert(str);
	int len = 0;

	while (*str != '\0')
	{
		len++;
		str++;
	}
	return len;
}

int main()
{
	char str[] = "hello SILMY23";
	printf("%d",my_strlen(str));

	return 0;
}

 这样一个strlen字符函数的模拟实现就完成了,当然主体我们也可以不采取计数的方式,可以采取指针-指针的写法。

让一个指针找到尾巴,然后利用指针相减指针是元素的个数,返回指针减去指针就可以了。前提是仍然需要对str断言,如果str不为空我们再赋值给tail指针。

#include<stdio.h>
#include<assert.h>
size_t my_strlen(const char* str)
{
	assert(str);
	const char* tail = str;
	while (*tail != '\0')
	{
		tail++;
	}
	return tail - str;
}
int main()
{
	char str[] = "hello SILMY23";
	printf("%d",my_strlen(str));
	return 0;
}

 二、数组名和数组首元素

在上一篇文章中,我们用了以下代码

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

我们发现在用指针访问数组的时候,我把数组名赋值给了指针,那是否意味着所有情况下的数组名都是数组首元素的地址呢?

我们接下来看以下代码:

#include<stdio.h>

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);

	printf("%d ", sizeof(arr));
	printf("%d ", sizeof(arr[0]));

	return 0;
}

我们发现结果是40 4,那说明并不是所有情况下的数组名都是数组首元素的地址。

数组名就是数组首元素的地址,但是有两个例外:

1.sizeof(),在sizeof的关键字后面单独放数组名,这里的数组名表示整个数组,计算的是整个数组的大小, 单位是字节。

2.&arr,这里表示的也是整个数组,取出的是整个数组的地址(整个数组的地址和数组首元素的地址是有区别的)

那arr,&arr,&arr[0]的联系和区别又在哪里呢?我们看以下代码:

#include<stdio.h>

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

	printf(" arr       = %p\n ", arr);
	printf("arr+1     = %p\n ", arr + 1);
	printf("&arr[0]   = %p\n ", &arr[0]);
	printf("&arr[0]+1 = %p\n ", &arr[0] + 1);
	printf("&arr      = %p\n ", &arr);
	printf("&arr + 1  = %p\n ", &arr + 1);
	return 0;
}

 结果如下:

 

  我们看指针走的步长是指针类型,前四个反应的是arr和&arr[0],几乎无差别,而最后一个&arr它所走的空间大小是整个数组的大小。

二、指针访问数组 

 在上一篇文章我们初步写了指针访问数组,并打印数组的值,那如果我们想要输入呢?


#include<stdio.h>

int main()
{
	int arr[10] = {0};
	int sz = sizeof(arr) / sizeof(arr[0]);
	int* p = arr;
	for (int i = 0; i < sz; i++)
	{
		scanf("%d", p + i);
	}
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));
	}

	return 0;
}

当我们输入1 -10的时候,打印的也是如此。

 

当编译器处理的时候,打印的时候,其实arr[i] == *(arr+i), 它们之间是等价的。同时我们也可以写成p[i] == *(p+i)

甚至可以写成以下形式

arr[i]  == *(arr+i) == *(i+arr) == i[arr],感兴趣的读者可以自己敲一下验证验证

三、一维数组的传参

 我们看以下代码:

#include<stdio.h>

void print(int arr[])
{
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}

}

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10};
	print(arr);
	return 0;
}

当我们运行之后发现,结果并没有像我们预期的那样打印出1到10,而是只打印了1.这就涉及一维数组传参的本质了,一维数组传参实际上传入的是数组首元素地址,所以在函数内部计算sz的时候,sizeof(arr)是计算了指针变量的大小,四个字节,而后面计算出的也是四个字节,最后sz只计算出了1。 

 所以要修改代码我们可以在没有传参之前把数组长度计算出来,然后去传参,arr[]它实际还是指针变量,所以我们可以写成int *p.

void print(int *p,int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", p[i] );
	}
}

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10};
	int sz = sizeof(arr) / sizeof(arr[0]);
	print(arr,sz);
	return 0;
}

 四、冒泡排序

冒泡排序作为练习我也写过这篇啦,可以跳转细看,这里我粗略讲解练习。

http://t.csdnimg.cn/VCpbBicon-default.png?t=N7T8http://t.csdnimg.cn/VCpbB冒泡排序,假设有一个数组,我们需要把一个有序数组排序为升序。其核心思想是两两相邻的元素进行交换。

五、二级指针

指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里呢?再存放在一个指针里,这就是二级指针

#include<stdio.h>

int main()
{
	int a = 10;
	int* pa = &a;
	int** ppa = &pa;
	
	printf("%p\n", &a);
	printf("%p\n", &pa);
	printf("%p\n", &ppa);
	return 0;
}

在以上代码中,a是整型变量,占据四个字节的空间,pa和ppa都是指针变量,占据4或者八个字节的空间,a,pa,ppa三个变量都有对应的地址。 我们把pa叫做一级指针,ppa叫做二级指针。

 

 

如果我们要通过ppa找到a,我们就需要两层的解引用, 

 

六、指针数组

 什么是指针数组呢?我们回想一下,整型数组是用来存放整型数据的,字符数组是用来存放字符的,所以指针数组就是用来存放指针的。

#include<stdio.h>

int main()
{
	int a = 10;
	int* arr[] = { &a };
	printf("%d ", *arr[0]);
	return 0;
}

我们开辟一个指针数组来存放a的地址,我们需要对数组元素解引用来获得a的值。 

 

七、指针数组模拟二维数组

假设我们现在有三个数组,arr1,arr2,arr3,我们将它放到指针数组parr中,我们在用parr[]的时候,得到的是三个数组的地址,再对其解引用,我们就得到三个数组中具体的数值。 

#include<stdio.h>

int main()
{
	int arr1[5] = { 1,2,3,4,5 };
	int arr2[5] = { 2,3,4,5,6 };
	int arr3[5] = { 3,4,5,6,7 };
	int* parr[3] = { arr1,arr2,arr3 };
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			printf("%d ", parr[i][j]);
		}
		printf("\n");
	}

	return 0;
}

结果如下: 

 

这就是指针数组的用法。 上述代码的分布图如下:

 

感谢各位同伴的支持,本期指针入门篇2就讲解到这啦,如果你觉得写的不错的话,可以给个赞,若有不足,欢迎各位在评论区讨论。 

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

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

相关文章

书生浦语大模型实战营-课程笔记(2)

介绍了一下InternLm的总体情况。 InternLm是训练框架&#xff0c;Lagent是智能体框架。 这个预训练需要这么多算力&#xff0c;大模型确实花钱。 Lagent是智能体框架&#xff0c;相当于LLM的应用。 pip设置 开发机的配置 pip install transformers4.33.1 timm0.4.12 sente…

数据结构——5.5 树与二叉树的应用

5.5 树与二叉树的应用 概念 结点的权&#xff1a;大小可以表示结点的重要性 结点的带权路径长度&#xff1a;从树的根到该结&#xff0c;的路径长度&#xff08;经过的边数&#xff09;与该结点权的乘积 树的带权路径长度&#xff1a;树中所有叶结点的带权路径长度之和(WPL) …

C语言函数(四):递归

目录 1.什么是递归2.递归的限制条件3.递归举例3.1 举例一&#xff1a;求n的阶乘 4.递归与迭代4.1 求第n个斐波那契数 5.递归与循环的选择 1.什么是递归 在学习函数这一章节&#xff0c;递归是每个计算机语言绕不开的知识点&#xff0c;那什么是递归呢&#xff1f; 递归就是一种…

Java入门高频考查基础知识9(银盛15问万字参考答案)

JAVA刷题专栏&#xff1a;http://t.csdnimg.cn/9qscL 目录 一、Springcloud的工作原理 三、注册中心心跳是几秒 四、消费者是如何发现服务提供者的 五、多个消费者调⽤用同⼀接口&#xff0c;eruka默认的分配⽅式是什么 六、springboot常用注解&#xff0c;及其实现 七、…

【C语言】指针的入门篇,深入理解指针和指针变量

欢迎来sobercq的博客喔&#xff0c;本期系列为【C语言】指针的入门篇&#xff0c;深入理解指针和指针变量 图文讲解指针的知识&#xff0c;带大家理解指针和内存的关系&#xff0c;以及指针的用法&#xff0c;感谢观看&#xff0c;支持的可以给个赞哇。 目录 一、内存和地址 二…

【使用IDEA总结】01——新增作者信息、方法参数返回值

[TOC](目录) 1.类新增作者信息 打开IDEA的Settings&#xff0c;Editor->Code Style->File and Code Templates->Includes->File Header&#xff0c;输入以下作者信息&#xff0c;作者名更换为自己的即可&#xff0c;操作如下图所示 /*** Author Linhaipeng* Date…

实现表达式语言

实现表达式语言 考虑使用大量Scriplet代码嵌入Java代码的JSP页面。过度使用Scriptlet代码使JSP页面变得混乱。因此。开发人员难以阅读和调试页面。另外,网页设计师在编辑表示代码时也会遇到问题。为了解决此类问题,开发无脚本的JSP页面受到推崇。 无脚本的代码使JSP页面易于…

Uipath 实现Excel 文件合并

场景描述 某文件夹下有多个相同结构(标题列相同)的Excel 文件&#xff0c;需实现汇总到一个Excel文件。 常见场景有销售明细汇总&#xff0c;订单汇总等。 解决方案 对于非IT 人员则可使用Uipath 新式Excel活动&#xff0c;通过拖拉实现。也可以通过内存表或使用VB脚本&…

【动态规划初识】不同路径问题

每日一道算法题之不同路径问题 一、题目描述二、思路三、C++代码一、题目描述 题目来源:LeetCode 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finis…

CTFshow web(php文件上传155-158)

web155 老样子&#xff0c;还是那个后端检测。 知识点&#xff1a; auto_append_file 是 PHP 配置选项之一&#xff0c;在 PHP 脚本执行结束后自动追加执行指定的文件。 当 auto_append_file 配置被设置为一个文件路径时&#xff0c;PHP 将在执行完脚本文件的所有代码后&…

opencv通道分离与合并

void QuickDemo::channels_demo(Mat & image) {std::vector<Mat>mv;//通道分离合并split(image,mv);//原图 指针(Mat)imshow("蓝色", mv[0]);imshow("绿色", mv[1]);imshow("红色", mv[2]); } split(image,mv);//原图 指针(Mat) 这里…

华为OD机试 - 分配土地( Python C C++ JavaGo JS PHP)

题目描述 从前有个村庄&#xff0c;村民们在各种田地上插上小旗子&#xff0c;每个旗子上都标识了一个数字。现在&#xff0c;村民们想要找出一个包含相同数字的最小矩形区域&#xff0c;并将这块土地分配给对村庄做出巨大贡献的村民。我们需要找出这个矩形区域的最大面积。 …

网络原理(3)--以太网协议,DNS

&#x1f495;"Echo"&#x1f495; 作者&#xff1a;Mylvzi 文章主要内容&#xff1a;网络原理(3)–以太网协议,DNS 在网络原理(2)中介绍了网络层中的一个重要的协议–ip协议,网络层关注的通信时的起点和终点,而数据链路层更加"底层"一些,关注的是传输过程…

嵌入式软件设计入门:从零开始学习嵌入式软件设计

&#xff08;本文为简单介绍&#xff0c;个人观点仅供参考&#xff09; 首先,让我们了解一下嵌入式软件的定义。嵌入式软件是指运行在嵌入式系统中的特定用途软件,它通常被用来控制硬件设备、处理实时数据和实现特定功能。与桌面应用程序相比,嵌入式软件需要具备更高的实时性、…

第13讲创建图文投票

创建图文投票实现 图文投票和文字投票基本一样&#xff0c;就是在投票选项里面&#xff0c;多了一个选项图片&#xff1b;、 <view class"option_item" v-for"(item,index) in options" :key"item.id"><view class"option_input&…

MATLAB知识点:randsample函数(★★★☆☆)生成随机样本的函数,可指定有放回和无放回随机抽样

讲解视频&#xff1a;可以在bilibili搜索《MATLAB教程新手入门篇——数学建模清风主讲》。​ MATLAB教程新手入门篇&#xff08;数学建模清风主讲&#xff0c;适合零基础同学观看&#xff09;_哔哩哔哩_bilibili 节选自第3章&#xff1a;课后习题讲解中拓展的函数 在讲解第三…

机器学习:Pooling层作用及反向传播

CNN网络在反向传播中需要逐层向前求梯度&#xff0c;然而pooling层没有可学习的参数&#xff0c;那它是如何进行反向传播的呢&#xff1f;此外&#xff0c;CNN中为什么要加pooling层&#xff0c;它的作用是什么&#xff1f; Pooling层 CNN一般采用average pooling或max pooli…

【STM32 CubeMX】GPIO_HAL库源码分析

文章目录 前言一、GPIO_HAL库源码分析1.1 初始化GPIO1.2 HAL_GPIO_Init源码分析GPIO_InitTypeDef初始化结构体HAL_GPIO_Init函数 总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 例如&#xff1a;随着人工智能的不断发展&#xff0c;机器学习这门技…

判断一个时间序列中的元素是否属于一个月的第一天或最后一天

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 判断一个时间序列中的元素 是否属于一个月的第一天或最后一天 Series.dt.is_month_start Series.dt.is_month_end [太阳]选择题 以下代码的输出结果中正确的是? import pandas as pd ts pd.S…

【JavaEE】传输层网络协议

传输层网络协议 1. UDP协议 1.1 特点 面向数据报&#xff08;DatagramSocket&#xff09;数据报大小限制为64k全双工不可靠传输有接收缓冲区&#xff0c;无发送缓冲区 UDP的特点&#xff0c;我理解起来就是工人组成的**“人工传送带”**&#xff1a; 面向数据报&#xff08;…