掌握C语言指针,轻松解锁代码高效性与灵活性(中)

这篇具有很好参考价值的文章主要介绍了掌握C语言指针,轻松解锁代码高效性与灵活性(中)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

掌握C语言指针,轻松解锁代码高效性与灵活性(中)

✨✨ 欢迎大家来到贝蒂大讲堂✨✨

🎈🎈养成好习惯,先赞后看哦~🎈🎈

所属专栏:C语言学习
贝蒂的主页:Betty‘s blog

1. 引言

前面给大家介绍了一些指针的基本概念,今天就让我们继续深入指针的世界,和贝蒂一起打败指针大魔王吧

2. 二级指针

指针变量也是变量,是变量就有地址,那我们就把存放指针变量地址的指针称为二级指针。

可能理解起来有点绕,我们可以通过下面示意图演示一下

代码如下:

	int a = 10;
	int* pa = &a;//一级指针,存放a的地址
	int** ppa = &a;//二级指针,存放指针变量p的地址
  • 不能直接把&&a赋值给ppa哦,因为&&在C语言中是且的意思”

掌握C语言指针,轻松解锁代码高效性与灵活性(中)

(1)对ppa解引用,找到pa,也就是说*ppa==pa

(2)对pa解引用,找到a,也就是说**ppa==a

	int* b = *ppa;//找到a的地址
	int c = **ppa;//找到a
  • 依次内推我们可以衍生出三级指针,四级指针。

3. 数组与指针的关系

3.1 数组名的理解

我们在前面学习数组时就明白,数组名是首元素地址,但是讲解的不够深入,今天就让我们深入了解一下吧~

首先让我们观察一下如下代码

#include <stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	//&arr[0],arr,&arr的区别
	printf("&arr[0] = %p\n", &arr[0]);//首元素地址
	printf("arr = %p\n", arr);//一维数组数组名
	printf("&arr = %p\n", &arr);//对整个数组取地址
	return 0;
}

掌握C语言指针,轻松解锁代码高效性与灵活性(中)

从结果来说&arr[0],arr,&arr到底有什么区别呢?

让我们再看看下面这段代码

#include <stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	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);
	printf("&arr = %p\n", &arr);
	printf("&arr+1 = %p\n", &arr + 1);
	return 0;
}

输出结果:

掌握C语言指针,轻松解锁代码高效性与灵活性(中)

  1. &arr[0]与arr+1都是跳过4个字节,相当于跳过1个整型元素。

  2. &arr+1跳过40个字节,相当于10个整型,也就是整个数组。

总结:arr与&arr[0]都是首元素地址,指向数组第一个元素。&arr以首元素地址表示,但是指向的是整个数组。

3.2 sizeof与数组名

我们知道sizeof实际上是获取了数据在内存中所占用的存储空间,单位是字节

让我们看看下面这段代码吧

#include <stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	printf("%d\n", sizeof(arr));//计算大小
	return 0;
}

输出结果:40

不知道大家有没有疑惑?如果数组名是首元素地址的话,我们知道在32位机器上大小为4,在64位机器上大小为8。那为什么是40呢?

其实数组名就是数组⾸元素(第⼀个元素)的地址,但是有两个例外
• sizeof(数组名),sizeof中单独放数组名,这⾥的数组名表⽰整个数组,计算的是整个数组的⼤⼩,单位是字节
• &数组名,这⾥的数组名表⽰整个数组,取出的是整个数组的地址(整个数组的地址和数组⾸元素的地址是有区别的)

3.3 数组与指针等价关系

假设有一个一维数组和一个二维数组

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

我们要访问它的每个元素,有哪些方法呢~

  1. 数组访问
	int arr1[5] = { 1,2,3,4,5 };
	for (int i = 0; i < 5; i++)
	{
		printf("%d ", arr1[i]);
	}
int arr2[3][3] = { {1,2,3},{4,5,6},{7,8,9} };
for (int i = 0; i < 3; i++)
{
	for (int j = 0; j < 3; j++)
	{
		printf("%d ", arr2[i][j]);
	}
	printf("\n");
}
  1. 指针访问
for (int i = 0; i < 5; i++)
{
	printf("%d ", *(arr1+i));
}
for (int i = 0; i < 3; i++)
{
	for (int j = 0; j < 3; j++)
	{
		printf("%d ", *(*(arr2 + i) + j));
	}
}

通过对上面代码的观察,我们可以总结如下规律

  1. arr[i]与*(arr+i)等价。

  2. arr[i][j]与((arr+i)+j)等价。

3.4 指针数组

(1) 指针数组的概念

指针数组顾名思义就是存放指针的数组 ,数组中每个元素都是指针,存放的都是地址

int*parr1[5];//存放五个整型指针变量
char*parr2[5];//存放五个字符指针变量
float*parr3[5];//存放五个浮点数指针变量

代码示例

	int arr1[] = { 1,2,3 };
	int arr2[] = { 4,5,6 };
	int arr3[] = { 7,8,9 };
	//将每个数组的首元素地址都存进去
	int* parr[3] = { arr1,arr2,arr3 };

示意图:

掌握C语言指针,轻松解锁代码高效性与灵活性(中)

(2) 指针数组的理解

int main()
{
	int arr1[] = { 1,2,3 };
	int arr2[] = { 4,5,6 };
	int arr3[] = { 7,8,9 };
	int* parr[3] = { arr1,arr2,arr3 };
	printf("%p\n", parr);//打印指针数组首元素地址,也就是打印存放arr1空间的地址
	printf("%p\n", parr[0]);//arr1数组首元素地址
	printf("%p\n", *parr);//arr1首元素地址
	printf("%d\n", **parr);//相当于对arr1首元素地址解引用,指的的是1
	printf("%d\n", *parr[0]);//也相当于对arr1首元素地址解引用,为1
	printf("%d\n", *parr[1]);//相当于对arr2首元素地址解引用,为4
	return 0;
}

输出结果:

012FFE30
012FFE6C
012FFE6C
1
1
4

(3) 模拟二维数组

通过上述我们对指针数组的理解,我们可以间接来模拟出二维数组。

代码如下:

int main()
{
	int arr1[] = { 1,2,3 };
	int arr2[] = { 4,5,6 };
	int arr3[] = { 7,8,9 };
	//将每个数组的首元素地址都存进去
	int* parr[3] = { arr1,arr2,arr3 };
	int i = 0;
	int j = 0;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 3; j++)
		{
			printf("%d ", parr[i][j]);
		}
		printf("\n");//换行)
	}
	return 0;
}
  • 模拟的二维数组并不是真正的二维数组,因为二维数组在内存中是连续存储的,而模拟出来的数组内存存储并不连续

3.5 数组指针

(1) 数组指针的概念

同理,指针数组的本质是一个数组;那么数组指针的本质就是个指针,指向一个数组的指针。

int(*parr1)[5];//指向一个有五个元素的整型数组
char(*parr2)[5];//指向一个有五个元素的字符数组
float(*parr3)[5];//指向一个有五个元素的浮点数数组

(2) 数组指针的理解

int main()
{
	int arr[5] = { 1,2,3,4,5 };
	int(*parr)[5] = &arr;
	//对数组名取地址代表整个数组的地址
	printf("%p\n", parr);//整个数组的地址一般用数组首元素地址表示
	printf("%p\n", parr[0]);//相当于*(parr+0)==arr,首元素地址
	printf("%p\n", *parr);//首元素地址
	printf("%d\n", **parr);//相当于对首元素地址解引用,指的的是1
	printf("%d\n", *parr[0]);//也相当于对首元素地址解引用,为1
	printf("%d\n", *parr[1]);//等价于*(*(parr+1)),parr+1跳过一个数组大小的地址,越界访问
	return 0;
}

输出结果:

012FF6F0
012FF6F0
012FF6F0
1
1
-858993460(越界访问,随机数)

示意图:

掌握C语言指针,轻松解锁代码高效性与灵活性(中)

3.6 指针数组与数组指针的区别

可能有许多小伙伴区别不清楚指针数组与数组指针,但是如果写成指针的数组,数组的指针,可能更好理解。接下来让我们具体分析一下吧?

首先我们要清楚一个优先级顺序:()>[]>*

  1. 在int*parr[]中,parr先与[]结合(数组),而parr前面声明的变量类型是int*。所以这是一个数组,数组中每个元素的类型是int*的指针,这一类我们统称为指针数组

  2. 在int(*parr)[]中,parr先与结合(指针),而后除开(parr)是一个int []的数组类型。所以这是一个指针,这个指针指向的是一个数组,这一类我们称为数组指针。

3.7 字符串

我们先看一下下面这段代码

char arr1[]="im betty";
char arr2[]={'a','b','c','\0'};

这是一种常见的字符串的表示形式,以'\0'作为其结尾标志。

但是还有另外一种表示形式,代码如下

	//const可以省略
    const char* p1 = "im betty";
    const char* p2 = "abc";

我们知道const修饰在*前,不能改变指针变量所指向的值,所以这个字符串是不能改变的,这种字符串我们称为常量字符串

  • 本质上是将字符串中的首元素地址存放进指针变量。

知道这些之后,让我们来看一道题吧

输出什么?

#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;
}

输出结果:

掌握C语言指针,轻松解锁代码高效性与灵活性(中)

为什么会出现这种结果呢,那是因为这⾥str3和str4指向的是⼀个同⼀个常量字符串。C/C++会把常量字符串存储到单独的⼀个内存区域(常量区),当⼏个指针指向同⼀个字符串的时候,他们实际会指向同⼀块内存。但是⽤相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块,每个数组地址就会不同。所以str1和str2不同,str3和str4相同。

3.8 数组传参

(1) 一维数组传参

我们在之前学习函数时候就讲过一维数组传参,让我们来复习一下吧。

代码如下

void print(int arr[])//写成数组的形式
{
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
}
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	print(arr);//将数组传递给print函数
	return 0;
}

我们传参是传的数组名,我们知道数组名是首元素的地址,既然是地址,自然就能用指针来接受,所以就有了另外一种写法。

void print(int*p)//用指针来接收
{
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ",*(p+i));
	}
}

(2) 二维数组的传参

先让我们看看一般二维数组是如何传参的吧

void print(int arr[][3])//行可以省略,列不可以
{
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 3; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
}
int main()
{
	int arr[3][3] = {{1, 2, 3}, { 4,5,6 }, { 7,8,9 }};
	print(arr);//将数组传递给print函数
	return 0;
}

那么指针接收如何写呢,还是int*p吗,我们知道二维数组可以看成把每一行当做一个元素的一维数组,数组名首元素地址自然是第一行元素的地址,所以要用数组指针来接收哦~

代码如下:

void print(int(*p)[3])//明确元素个数
{
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 3; j++)
		{
			printf("%d ",*(*(p+i)+j));
		}
		printf("\n");
	}
}

本文由博客一文多发平台 OpenWrite 发布!文章来源地址https://www.toymoban.com/news/detail-837855.html

到了这里,关于掌握C语言指针,轻松解锁代码高效性与灵活性(中)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • RunnerGo:轻量级、全栈式、易用性和高效性的测试工具

    随着软件测试的重要性日益凸显,市场上的测试工具也日益丰富。RunnerGo作为一款基于Go语言研发的开源测试平台,以其轻量级、全栈式、易用性和高效性的特点,在测试工具市场中逐渐脱颖而出。 RunnerGo是一款轻量级的测试工具,使用Go语言研发,具有轻便灵活的特点。与

    2024年02月10日
    浏览(43)
  • JDK、Tomcat、Redis、MySQL集齐,教你如何搭建高效性能测试项目

    作为一个软件工程师,怎能不知道如何搭建一个性能测试项目呢?性能测试是一个软件工程师不可或缺的一环,因为一款软件性能好坏直接关系到用户体验,而用户体验又是一个软件能否成功的最重要的因素。本文将带你一步步搭建一个性能测试项目,包括如何搭建 JDK、To

    2024年02月04日
    浏览(42)
  • 掌握Go语言:Go语言类型转换,解锁高级用法,轻松驾驭复杂数据结构(30)

    在Go语言中,类型转换不仅仅局限于简单的基本类型之间的转换,还可以涉及到自定义类型、接口类型、指针类型等的转换。以下是Go语言类型转换的高级用法详解: Go语言类型转换的高级用法 1. 自定义类型之间的转换 在Go语言中,可以使用类型别名或自定义类型来创建新的

    2024年04月09日
    浏览(68)
  • 【C语言初阶】带你轻松掌握指针基础知识(1)——指针的定义,类型,大小

    君兮_的个人主页 勤时当勉励 岁月不待人 C/C++ 游戏开发 Hello,这里是君兮_,最近刚回家有点懒,从今天开始恢复更新并开始更新新的刷题系列,我们先继续更新0基础入门C语言的内容,今天给大家带来的是指针方面的内容,但由于是初阶,一些高级的用法我们放在进阶篇再讲

    2024年02月12日
    浏览(43)
  • 【C语言初阶】带你轻松掌握指针基础知识完结篇——野指针,指针运算,指针和数组,二级指针

    君兮_的个人主页 勤时当勉励 岁月不待人 C/C++ 游戏开发 Hello,这里是君兮_,今天继续给大家更新0基础入门C语言的内容,我们这次主要更新的依然是初阶指针的基础知识 废话不多说咱们直接开始吧!! 概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有

    2024年02月16日
    浏览(46)
  • “智能查单轻松实现批量快递查询,高效掌握快递物流信息!“

    亲爱的用户,你是否常常为了查询大量快递单号而感到烦恼?不用担心,我们已经为你提供了一种高效、智能的解决方案!现在,只需一键操作,即可实现批量快递查询,迅速了解每个单号的详细物流信息。 首先,我们要进入快递批量查询高手主页面,并点击高级设置, 在

    2024年02月12日
    浏览(44)
  • 视频批量高效剪辑,支持将视频文件转换为音频文件,轻松掌握视频格式

    在数字化时代,视频内容日益丰富,管理和编辑这些视频变得愈发重要。然而,传统的视频剪辑软件往往操作复杂,难以满足高效批量处理的需求。现在,一款全新的视频批量剪辑神器应运而生,它支持将视频文件一键转换为音频文件,同时轻松掌握各种视频格式,让你的视

    2024年04月14日
    浏览(66)
  • 晨曦记账本筛选支付宝收支明细,轻松掌握账户总花销,高效管理财务!

    晨曦记账本是一款专业的财务管理软件,可以帮助用户轻松记录和管理自己的收支明细。通过使用晨曦记账本,用户可以轻松筛选出支付宝的收支明细,更好地掌握自己的财务状况。 首先,第一步,我们要进入晨曦记账本主页面,在左上方的根据旁下拉列表选择“收支账户”

    2024年02月07日
    浏览(49)
  • 码上加速,低代码解锁高效交付案例

    站长工作台,致力于为京东物流所有站长、运营管理人员提供高效工作平台,拥有多元化的业务形态。我们力求 提升团队研发效率 、 实现敏捷业务交付 ,以打造一支具备灵活性、高度协作和强适应能力的敏捷团队。 站长工作台的报表页面和任务卡片页面,大多数的UI风格和

    2024年02月22日
    浏览(44)
  • 解锁更高效的AIGC工具:现代大语言模型工具推荐

    AI技术的普及已经在近年来不断增长。这种技术已经改变了我们与电脑的互动方式,让我们能够更高效、更自然地完成任务。本文将展示10个基于ChatGPT和GPT-3 AI模型构建的最强大的资源,使您更容易充分利用它们的潜力。因此,如果您想利用AI技术改进生活或工作,这篇文章是

    2024年02月09日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包