C语言进阶知识(三)

指针的进阶

首先,我们来明确指针概念,指针是用来储存地址的变量,大小是固定的4/8个字节,具体看平台。这是因为地址是物理的电线上产生的,32位机器----32根地址线-----每根地址线产生1/0的电信号,那么以32位平台为例,32个0/1组成的二进制序列就需要4个字节来储存,同理,可推测64位平台就需要8个字节来储存地址了。这就是指针大小为什么是4/8个字节的原因。

1.字符指针

指针的类型中我们知道有一种指针类型为字符指针 char*。

我们常用的方法是,定义一个字符变量,用字符指针指向字符变量。如下代码:

int main()
{
	char ch = 'a';
	char* p = &ch;
	*p = 'b';
	printf("%c", ch);
	return 0;
}

但我们今天介绍新的一种使用方式:

int main()
{
	char arr[] = {"abcdef"};
	const char* p = "abcdef";
	printf("%s\n", p);
	printf("%c", *p);
	return 0;
}

大家想一想运行结果会是怎样的?运行结果显示如下:

 

于是,我们发现当字符指针指向常量字符串时,指向的其实是字符串的第一个地址,而当我们输出字符串时,又发现字符串在内存中是连续储存的。这里有一个问题:为什么const不是让p指向的地址不变,而是让const指向的字符串不能被改变?这个问题先留给大家去思考。

那我们来看一道面试题目:

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肯定不相同,而我们常量字符串则被开辟在代码区中,所以指向的是同一个地址。

2.指针数组

类比概念,整型数组是存放整型变量的数组,那指针数组是一个存放指针的数组。

int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 2,3,4,5,6 };
	int arr3[] = { 3,4,5,6,7 };
	//数组名为首元素地址,arr1相当于int*
	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;
}

 此代码模拟使用指针数组对二维数组进行遍历,结果如下:

3.数组指针

类比概念,整型指针是指向整型变量的指针,储存的是整型变量的地址;那么数组指针就是指向数组的指针,储存的是数组的地址。所以我们需要先来了解数组的地址。

我们知道通常情况下数组名代表数组首元素地址,除了两种特殊情况:一种是sizeof(arr),此时代表整个数组,另一种是&arr,代表整个数组的地址。以下代码详细进行解释:

int main()
{
	int arr1[5] = { 1,2,3,4,5 };
	printf("%p\n", arr1);
	printf("%p\n", arr1+1);
	printf("%p\n", &arr1[0]);
	printf("%p\n", &arr1[0] + 1);
	printf("%p\n", &arr1);
	printf("%p\n", &arr1+1);
	return 0;
}

 

从运行结果我们可以清楚地看到,数组名代表的就是首元素地址,所以当arr1+1时,相当于指针向后移动一个元素,整形数组,所以移动一个元素就是4个字节,所以从E8变为EC。而&arr1虽然和arr1指向位置一样,但当&arr1+1时,发现从E8变为FC,十进制则为232到252,增加了20个字节,也就是一个数组的大小。所以我们得出结论&arr1得到的是整个数组的地址。

那么我们知道&arr可以得到数组的地址后,我们再来看数组指针的使用。

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	//数组的地址,储存到数组指针变量里
	int(*p)[10] = &arr;
	return 0;
}

 我们来看一下对于一维数组打印,两种不同方式,从而加深对数组指针的了解。

第一种方法:使用首元素地址进行打印,也是常用方法。

int main()
{
	int arr[5] = { 1,2,3,4,5 };
	int* p = arr;
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%d ", *(p + i));
	}
	return 0;
}

 arr为首元素地址,我们把它存入整型指针里面,然后每次对p进行+i操作,相当于逐步遍历数组元素,再解引用得到数组值,后打印整个数组。方法思想:使用整型指针加法操作进行遍历。

第二种方法:使用数组指针进行打印。

int main()
{
	int arr[5] = { 1,2,3,4,5 };
	int(*p)[5] = &arr;
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%d ", *((*p) + i));
	}
	return 0;
}

 我们把整个数组的地址存在数组指针中,此时,数组指针就是指向数组的指针,储存的是数组的地址,所以要用&arr。但这里的不同是我们先对p进行*得到数组名也就是首元素地址,再进行遍历操作,后再解引用此时得到值。方法思想:使用数组指针进行对一维数组的遍历。

那么一维数组常见的使用方法就是这两种,那么我们思考一下二维数组的遍历?

二维数组的每一行可以理解为二维数组的一个元素,每一行又可以理解为一个一维数组。所以,二维数组其实是元素为一维数组的数组,那我们来思考一下如何利用数组指针对二维数组进行遍历?二维数组的数组名,为首元素地址,那么可以理解为为首行元素地址,也就是一维数组的地址,也就是数组的地址,所以需要使用数组指针。两种方式实现方法如下:

第一种方法,使用数组传参,直接进行打印

Print(int arr[][5], int m, int n)
{
	int i = 0;
	for (i = 0; i < m; i++)
	{
		int j = 0;
		for (j = 0; j < n; j++)
		{
			printf("%d ", arr[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 };
	Print(arr, 3, 5);
	//传参:数组,数组行,数组列
	return 0;
}

第二种方法,使用数组指针进行打印。

Print(int (*p)[5], int m, int n)
{
	int i = 0;
	for (i = 0; i < m; i++)
	{
		int j = 0;
		for (j = 0; j < n; j++)
		{
			printf("%d ",*( * (p + i) + j));
			//*p数组指针,指向数组的地址,对数组指针解引用得到首元素地址
			//再对首元素地址+j得到二维数组的位置,再进行解引用得到二维数组元素
			//printf("%p ", *(p + i));
			//*(p + i)得到的是第一,二,三行数组的地址
		}
		printf("\n");
	}
}
int main()
{
	int arr[3][5] = { 1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7 };
	//数组指针,指向数组的地址,首元素地址即为第一行元素地址,为一个数组,所以使用数组指针。
	Print(arr, 3, 5);
	//传参:数组,数组行,数组列
	return 0;
}

 

通过一维数组和二维数组的遍历,希望大家可以充分理解指针和数组的关系,包括数组指针和指针数组的具体应用,今天的内容补充就到这里,希望大家可以一起讨论!一起学好C语言。

 

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值