C指针进阶 (1)(字符指针(补充),数组指针的使用,一维二维传参等等)

本文深入探讨了字符指针的特性,包括其不可修改性、地址共享以及与数组的区别。通过实例解析了指针数组如何模拟二维数组,并介绍了数组指针在二维数组操作中的应用。此外,还讨论了一维和二维数组的传参方式,强调了二维数组并非二级指针。最后,提供了相关的小练习以巩固理解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本篇博客是对指针万字详解的补充,其中包含一些巧妙的理解,如果感觉这篇博客的内容看不太懂,可以参考上一篇指针博客😁😁


指针万字详解(空指针,野指针,泛型指针…)


在这里插入图片描述

🚩字符指针

字符数组本质上非常简单,但是有几个需要注意的点:

1.字符指针指向的是一个常量,常量存在于只读数据区,所以不能进行修改。

我们看以下代码,我们定义了一个字符指针,并让他指向一个常量,这时候你想通过解引用去修改它,最后的结果只能是程序崩溃。

	char*a = "abcdef";
	*a = "w";

在这里插入图片描述
可以理解为,在创建这个字符指针的时候,计算机先创建了一个常量字符串"abcdef",然后再让字符指针指向了这个字符串,这个字符串存储在计算机内无法修改的地方,所以程序会崩溃。

2.当你用两个字符指针指向相同的常量字符串时,存储的地址是一样的 我们进行以下操作:
	char *a = "abcdef";
	char *b = "abcdef";
	printf("%p %p",a,b);

在这里插入图片描述
这也说明了一件事,系统不会创建相同的常量,当再次让一个字符指针指向相同的字符串时,系统会找到之前创建的常量,而不是重新创建一个重复的常量。

3.字符指针与数组的区别

虽然之前我们说指针和数组有相同之处,但是在字符串的世界里,他们有着显著的区别。
请看以下代码:

	char a[] = "abcdef";
	a[0] = 'o';
	printf("%s", a);

在这里插入图片描述

	char a[] = "abcdef";
	char b[] = "abcdef";
	printf("%p %p",&a,&b);

在这里插入图片描述

我们之前说不能对常量字符串进行修改,相同常量字符串地址相同好像都不成立了,但事实真的如此吗?
其实你现在创建的"abcdef"根本不是常量,它并不储存在系统的只读区域。
仔细观察上面的代码,我们是先申请了一片空间给字符数组,然后再把字符串初始化给字符数组,是对数组里的元素赋值,并不是先创建了一个字符常量,那当然可以修改了。

🚩指针数组与数组指针的使用

在上一篇文章中我们简单的介绍了一下数组指针与指针数组,现在我们深入了解一下。

♨️♨️♨️1.指针数组模拟二维数组

在模拟二维数组之前,我们先来补充一些二维数组的知识。假设定义了一个三行五列的二维数组,并赋上若干值。
在这里插入图片描述
按照我们平时的理解,这个二维数组是按照行和列存放的,就像上图写的那样,但是实际上,数组存储是一条线存储的,并没有换行。
在这里插入图片描述

通过监视,我们可以看到这个数组每个元素的地址都是相邻的,并没有达到真正的二维,我们可以说,这个二维数组仍然是一个一维数组,只不过元素只有三个,分别代表每个行,一行中有五个整数而已。
现在我们来用指针数组模拟实现这样一个二维数组,在上一篇对指针的讲解中,我们说指针数组是一个数组,只不过存储的变量是指针变量。我们想要模拟二维数组的形态,就要让一个数组元素包含五个整数,这时就用到了指针数组,具体操作如下:
在这里插入图片描述
在这里插入图片描述

可以看到,我们让指针数组中的三个指针变量指向了三个一维数组,模拟实现了一个二维数组,至于为什么在监视中三个数组只显示了首元素1?这是因为指针数组存储的是p[0],p[1],p[2],计算机会翻译为*(p+0),*(p+1) ,这样的形式。

p[0]中的指针指向的是整个a数组,也就是数组名,数组名相当于首元素的地址,首元素的地址存储的值就是1。
既然p[0]中存储的值是a的首元素的地址,那我们对p[0]再进行解引用*(p[0]),那得到的就是a[0]的值,也就是1,我们知道p[0] == *(p+0),我们现在进行的操作就可以用p[0][0]来表示,这样看起来是不是更像一个二维数组了?代码表述如下:

	int a[5] = { 1,2,3,4,5, };
	int b[5] = { 1,2,3,4,5, };
	int c[5] = { 1,2,3,4,5, };
	int* p[3] = { a,b,c };
	for (int i = 0; i < 3; ++i)
	{
		for (int j = 0; j < 5; ++j)
		{
			printf("%d ", p[i][j]);
		}
		printf("\n");
	}

在这里插入图片描述

♨️♨️♨️2.数组指针的应用

无论是指针数组,或是数组指针,他们都与二维数组有密切的联系,数组指针是指向有固定数目元素数组的指针,而二维数组每一行的元素个数都是固定的,所以我们也可以用数组指针来打印一个二维数组。具体操作如下:

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

在这里插入图片描述
这里有一个有点复杂的表达式。*(*(p+i)+j),我们来单独解释一下他,我们先简单的分析一下:
p是我们函数的参数,是一个有五个元素的数组指针,而他指向就是二维数组的第一行,他就相当于是&a,(我们之前说过,只有两种特殊情况数组名是指整个数组的,一种就是&数组名,另一种是sizeof(数组名)),那么我们对p+1,字长是一整行,自然就跳跃到了第二行,加i就跳跃到跳跃到第i行,那对p进行解引用呢? p是&a,那解引用就是*&a也就是a,a是数组名,也就是&a[0],此时数组名的意义是数组首元素的地址,那么再次解引用,得到的就是数组首元素的值了。

有点绕,可以画图理解一下:
在这里插入图片描述
(图有点小了,见谅)

♨️小练习

我们来看以下三行程序,并说出他的含义:

int *a[10];
int (*a)[10];
int (*a[10])[5];

请思考完毕后查看答案:

  1. 指针数组,因为*号的结合力没有[ ]强,所以a先于[ ]结合,成为一个数组,int * 就是数组里每个元素的类型。
  2. 数组指针·,因为加了括号,所以a先与*结合,变成一个指针,这个指针指向的类型应该是 int [10],一个含有十个元素的数组。
  3. 这个看起来有点复杂,我们可以采用删减的思想来看他,首先看a与谁结合了,a先和[ ],结合了,说明他是一个数组,数组由的定义由数组名和数组元素的类型构成,我们把数组名删掉,剩下了一个int (*)[5],这就是数组元素的类型,所以这是存储数组指针的数组。

注意:无论是我们分析复杂的程序,还是类型,我们都可以用这种删减的思想,看原本应该有什么,我现在有了什么,把有的删去,剩下的就是我们想要的结果了。

🚩3.数组传参与指针传参

一维数组传参

首先我们来看一维数组传参

void ArrPrint(int a[5],int size)
{
	for (int i=0; i < size; ++i)
	{
		printf("%d ", a[i]);
	}
}
int main()
{
	int a[5] = { 1,2,3,4,5, };
	ArrPrint(a, 5);
	return 0;
}

我们可以使用直来直去的传参方式,要传进去一个有五个元素的数组,那我们就在参数上写一个五个元素的数组,当然int a[5]中的5是没有用处的,他只是提醒你你传进去的是有五个元素的数组而已,你在里面填9,10,什么数都可以,因为他是没有实际意义的。
我们常说数组和指针是密切联系的,可以用数组就可以用指针,因为我们传进去的a,是数组首元素的地址,指针当然是可以接收地址的,指针形式如下:

void ArrPrint(int *a,int size)
{
	int* q = a;
	for (q = a; q < a+size; ++q)
	{
		printf("%d ", *q);
	}
}

这里使用纯指针的形式打印一维数组,也是可以的。

♨️♨️二维数组传参

在之前我们说过,二维数组其实就是一维数组,而二维数组传参打印,我们在描述数组指针和指针数组时也已经写过了,这里不再赘述。
但是值得注意的是,二维数组与二级指针没有什么关系,二级指针是储存一级指针地址的指针,而二维数组,实质上是加长版一维数组,他的类型仍然是一级指针,只不过对于二维数组,它是由三个行元素组成的,每一个行元素可以用数组指针来表示:
在这里插入图片描述

可以看到,使用数组指针来接收二维数组是允许的,而二级指针则会出现问题。

🚩总结

这篇博客实质上是对指针基础内容的补充,谈不上指针的进阶,而在下一篇博客中,就要真正的增加难度了,有问题欢迎指正,分享不易,希望大家多多支持😎😎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值