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

news2024/11/26 19:38:20

欢迎来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.coloradmin.cn/o/1448957.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

投资理财的正确方式,实现个人的财富积累

一、教程描述 本套理财教程&#xff0c;大小18.98G&#xff0c;共有16个文件。 二、教程目录 第01课&#xff1a;记账为什么能帮月薪3000的你&#xff0c;省20%&#xff1f;.mp4 第02课&#xff1a;玩转信用卡&#xff0c;银行帮你越刷越有钱.mp4 第03课&#xff1a;巧用公…

代码+视频基于R语言进行K折交叉验证

我们在建立数据模型后通常希望在外部数据验证模型的检验能力。然而当没有外部数据可以验证的时候&#xff0c;交叉验证也不失为一种方法。交叉验验证&#xff08;交叉验证&#xff0c;&#xff23;&#xff36;&#xff09;则是一种评估模型泛化能力的方法&#xff0c;广泛应用…

30、二维数组/字符串操作相关练习20240214

一、编程实现二维数组的杨辉三角。 代码&#xff1a; #include<stdlib.h> #include<string.h> #include<stdio.h>int main(int argc, const char *argv[]) {int n;printf("please enter n:");scanf("%d",&n);int arr[n][n];for(in…

Django学习全纪录:Django开发环境的搭建

导言 对于Django&#xff0c;它是Python的一个开发框架&#xff0c;之前系统地学习过。遗憾的是&#xff0c;对于一些遇到的问题&#xff0c;没有及时地记录下来。因此&#xff0c;我将它重新捡起&#xff0c;进行学习和实践。从搭建环境开始&#xff0c;重新去学习它&#xff…

【从Python基础到深度学习】4. Linux常用命令(进阶)

接上篇 【从Python基础到深度学习】4. Linux 常用命令-CSDN博客 1.文件查找 - find 命令 find [搜索路径] [搜索条件] [操作]1.1 常用选项和参数 -name&#xff1a;按文件名搜索。 find 命令的 -name 选项可以接受通配符来匹配文件名。通配符可以帮助你更灵活地搜索文件名&a…

第13章 网络 Page724 asio定时器

程序代码&#xff1a; 11行&#xff0c;声明一个ios对象 13行&#xff0c;使用ios对象作为参数声明一个定时器&#xff0c;此时&#xff0c;定时器和ios完成了关联&#xff0c;后面定时器如果有任务的话&#xff0c;就可以将任务交给ios 16行&#xff0c;为定时器设置一个定…

MySQL数据库基础(一):数据库概述

文章目录 数据库概述 一、数据库介绍 二、数据库分类 1、关系型数据库 2、非关系型数据库NoSQL 三、常见数据库介绍 1、关系型数据库 2、非关系型数据库 数据库概述 一、数据库介绍 数据库就是存储数据的仓库&#xff0c;其本质是一个文件系统&#xff0c;按照特定的…

【Pygame手册01/20】最简应用:窗口

目录 一、说明 二、pygame是什么&#xff1f; 2.1 为游戏开发设计 2.2 版本发展史 2.3 特点 三、pygame安装要点 四、入门知识 4.1 初始使用 4.2 要更改 pygame 窗口的外观 4.3 完整窗口程序 4.4 窗口对象接口示例 五、隐形窗口和显性窗口 六、结论 一、说明 为什…

EasyRecovery专业版2024最新版本数据恢复软件

EasyRecovery专业版2024是一款功能强大的数据恢复软件&#xff0c;专为满足专业用户的需求而设计。它支持恢复各种类型的数据&#xff0c;包括文档、图片、视频、音频等&#xff0c;且可以恢复的数据存储介质也非常多样化&#xff0c;如硬盘、U盘、移动硬盘、数码相机、手机等。…

如何流畅进入Github

前言 以下软件是免费的&#xff0c;放心用 一、进入右边的下载链接https://steampp.net/ 二、点击下载 三、点击接受并下载 四、随便选一个下载链接进行下载 五、软件安装好打开后&#xff0c;找到Github 六、点击全部启用 七、再点击左上角的一键加速 八、这个时候你再进Git…

java的面向对象编程(oop)——认识泛型

前言&#xff1a; 打好基础&#xff0c;daydayup! 泛型 1&#xff0c;认识泛型&#xff1a; 定义类&#xff0c;接口&#xff0c;方法时&#xff0c;同时声明了一个或多个类型变量&#xff08;例&#xff1a;<E>&#xff09;,称为泛型&#xff0c;泛型接口&#xff0c;泛…

error MSB8008: 指定的平台工具集(v143)未安装或无效。请确保选择受支持的 PlatformToolset 值解决办法

右击解决方案&#xff0c;选择属性 将工具集为143的修改为其他&#xff0c;如图 重新编译即可运行

应用进程跨越网络的通信

目录 1 系统调用和应用编程接口 应用编程接口 API 几种应用编程接口 API 套接字的作用 几种常用的系统调用 1. 连接建立阶段 2. 传送阶段 3. 连接释放阶段 1 系统调用和应用编程接口 大多数操作系统使用系统调用 (system call ) 的机制在应用程序和操作系统之间传递控制…

AI:127-基于卷积神经网络的交通拥堵预测

🚀点击这里跳转到本专栏,可查阅专栏顶置最新的指南宝典~ 🎉🎊🎉 你的技术旅程将在这里启航! 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带有在本地跑过的关键代码,详细讲解供…

Java并发基础:PriorityBlockingQueue全面解析!

内容概要 PriorityBlockingQueue类能高效处理优先级任务&#xff0c;确保高优先级任务优先执行&#xff0c;它内部基于优先级堆实现&#xff0c;保证了元素的有序性&#xff0c;同时&#xff0c;作为BlockingQueue接口的实现&#xff0c;它提供了线程安全的队列操作&#xff0…

本地部署Stable Diffusion WebUI

官网 Stable Diffusion在线 Github上的Stable Diffusion WebUI 提醒一下&#xff1a;下面实例讲解是在Mac系统演示的&#xff1b; 一、 环境所需资源 PythonPycharmAnacondastable-diffusion-webui项目代码 注意事项 python版本一定要3.10&#xff0c;最好是3.10.6版本的。…

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

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

【Spring 校验】

校验 &#x1f347; 概述&#x1f349; 使用场景&#x1f349; 依赖引入 &#x1f34d; 校验示例&#x1f348;&#xff08;1&#xff09;在实体上标记校验注解&#x1f352;&#xff08;2&#xff09;在方法参数上声明校验注解&#x1f34f;&#xff08;3&#xff09;抛异常 &…

Unity(单元测试)在STM32上的移植与应用

概述 Unity Test是一个为C构建的单元测试框架。本文基于STM32F407为基础&#xff0c;完全使用STM32CubeIDE进行开发&#xff0c;移植和简单使用Unity。 单片机型号&#xff1a;STM32F407VET6 软件&#xff1a;STM32CubeIDE Version: 1.14.1 Unity Version&#xff1a;2.…

【报错解决】-bash: export: `-8‘: not a valid identifier 不是有效的标识符

现象 一登陆就提示-bash: export: -8’: not a valid identifier 不是有效的标识符 问题出现的原因 设置字符集时多写了空格 [rootdb1 ~]# cat >>/etc/profile<<EOF export LANGen_US.UTF -8(-8前不应有空格) EOF 解决方法 cd /etc vi profile 把export带有-8的…