c语言指针3

文章目录


前言

前面两章内容,小编已经讲述了指针和内存,指针之间的关系运算等有关内容,今天就给大家详细讲述一下指针与数组之间的联系了。


提示:以下是本篇文章正文内容,下面案例可供参考

一、数组名的理解

1.数组名正常情况是首元素的地址

数组名是首元素的地址

在前面内容我们就讲述了定义一个数组,数组名就是首元素地址,今天我们就用代码给大家证明一下。

代码部分

c 复制代码
#include<stdio.h>
int main()
{
	int arr[10] = { 0 };

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


分析

数组在内存中存储如下

此时我们分别打印首元素的地址和数组名的地址,发现两个打印结果相同,所以我们同时都指向数组的首元素地址。


因此我们可以得出,数组名正常情况下就是首元素的地址

2.数组名不是首元素地址的情况

2. 1 sizeof(arr)中的数组名

在sizeof(arr)中的数组名不是数组首元素的地址,此时的arr代表整个数组,接下来让我们验证一下

c 复制代码
int main()
{
	int arr[10] = { 0 };

	printf("%d\n", sizeof(arr));
	printf("%d\n", sizeof(&arr[0]));
	return 0;
}

代码分析

在上面代码中,定义了一个存储10个整型的数组,接下来,分别对arr和首元素的地址进行求字节数,并将字节数打印出来。如果此时arr代表首元素的地址进行求字节数,在32位环境下,指针变量的大小为四个字节,输出结果因为两个4,结果却是第一个结果为40,第二个结果为4。这是因为在sizeof(arr)中,此时arr代表整个数组,而不是首元素的地址,整个数组为10个整型的值,所以为40。

所以在sizeof中的arr代表整个数组,而不是首元素的地址。

2. 2 &arr中的arr代表整个数组

在c语言中,&arr中的arr也是代表整个数组 ,而不是单一的数组首元素的地址

代码引入

c 复制代码
int main()
{
	int arr[10] = { 0 };

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

代码分析

在这里我们打印arr还是 &arr 都是数组arr的地址,这样直接打印不好判断arr是否是代表首元素地址还是整个数组。看过小编前两节小内容,指针变量进行±可以判断跳过几个字节。arr代表首元素地址,变量类型是int*,整型指针+1跳过是一个整型的元素也就是一个字节。&arr代表整个元素,整个元素是10个整形数据,&arr+1代表跳过整个数组,也就是40个字节。如下图显示,因为是16进制数进行打印的地址,转换成10进制,可以得到,arr+1跳过四个字节,&arr+1则是跳过40个字节。

3. 结论

结论

综上:数组名就是数组首元素的地址,但是存在两种例外:

  1. sizeof(arr),sizeof中单独存放数组名,此时这里的数组名代表整个数组。计算的是整个数组的大小,单位是字节。

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

除了上述两种情况,任何地方使用数组名,数组名都代表首元素的地址。


二、使用指针访问数组

1.使用指针输入输出数组中的数

代码如下(示例):

c 复制代码
int main()
{
	int arr[5] = { 0 };
	int* p = arr; //数组名为数组首元素的地址

	//输入
	for (int i = 0; i < 5; i++)
	{
		scanf("%d", p+i);
	}

	//输出
	for (int i = 0; i < 5; i++)
	{
		printf("%d ", *(p+i));
	}
	return 0;
}

代码分析

数组在内存中的存储如下:


输入数据到数组中:

将第一个数组的地址存入数组中,此时p = &arr[0];通过指针访问数组后面的元素,只需要进行指针+偏移量的做法。

输出数据到数组中:

在数组学习的时候,我们打印数组为arr[ i ];在这里我们用指针进行访问则是:p是首元素的地址,通过p访问第一个元素则是 * p,访问后面的元素,则是通过指针+1进行访问---*(p+i)进行访问。

思考

打印部分原先用arr[ i ]进行打印,等价于*(arr+i),此时我们可以发现, 下标引用操作符[ ]作用等价于解引用,这时我们是否也可以 p[ i ]进行打印数组中的元素呢?下面来实践一下

经过代码运行,我们发现是可以的,因此打印的方式又多了一种:

  1. arr[ i ]
  2. p[ i ]
  3. *(p + i)
  4. *(arr + i)

以上四种方法皆可以用作访问数组内的元素,他们的本质其实都是通过指针的方式来访问数组,数组元素的访问在编译器处理的时候,也是转换成首元素的地址+偏移量求出元素的地址,然后解引用来访问的。


三、一维数组传参的本质

一维数组传参的本质就是传递首元素的地址

代码验证

c 复制代码
int test(int arr[10])   
{
	int sz2 = sizeof(arr) / sizeof(arr[0]);   
	return sz2;   
}
int main() intmain()  
{
	int arr[10] = { 0 };   
	int sz1 = sizeof(arr) / sizeof(arr[0]);   
	printf("sz1 = %d\n", sz1);   
	int sz2 = test(arr);   
	printf("sz2 = %d\n", sz2);   

	return 0; 返回0;  
}

假设数组传参传递的是首元素的地址,那么在测试函数中,sizeof(arr)中的arr就是数组首元素的地址,求出来的值在32位环境下因该为4。sz2的值应该为1,sz1的值应该为10。那么结果是不是这样的呢?

很明显,答案如我们预期一样,因此可以得出数组传参的本质就是传递首元素的地址 。所以函数形参的部分理论上应该使⽤指针变量来接收⾸元素的地址。那么在函数内部我们写sizeof(arr) 计算的是⼀个地址的大小(单位字节)而不是数组的大小(单位字节)。正是因为函数的参数部分是本质是指针,所以在函数内部是没办法求的数组元素个数的。


四、冒泡排序

冒泡排序的核心思想就是两个相邻的数之间进行比较

将十个数的排序进行排序成升序

假设输入数据:

9 8 7 6 5 4 3 2 1 0

输出数据应为:

0 1 2 3 4 5 6 7 8 9

思维导图

在这里一趟冒泡排序就让一个数回到了应该的位置,那么让10个数回到正确的位置只需要9趟,然后在一趟冒泡排序中,第一次我们要交换9次,然后9到了原有的位置,9保持不变,然后下一趟冒泡排序就只需要交换8次就可以,因为9已经到了因该去的位置,此次类推下去,每趟冒泡排序都会减少一个交换的数。

结论

n个数 需要n-1趟冒泡排序,使用循环变量i,从0开始到n-1

接下来定义每一趟冒泡排序交换的个数

每一趟冒泡排序需要交换n-1-i,每一趟冒泡排序就会减少一个需要交换的数

代码实现

c 复制代码
void bubble_sort(int arr[10], int sz)
{
	int i = 0;
	for (i = 0; i < sz - 1; i++)
	{
		int j = 0;
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
			}
		}
	}
}
int main()
{
	int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };     
	int sz = sizeof(arr) / sizeof(arr[0]);     
	bubble_sort(arr, sz);     
	for (int i = 0; i < sz; i++)     
	{
		printf("%d ",arr[i]);     
	}
	return 0;     
}

五、二级指针

5.1 二级指针的定义

在这之前我们讲述了一级指针,一级指针就是存储变量的地址,那么指针变量也有地址,这我们我们定义二级指针进行存储指针变量地址。

c 复制代码
int main()
{
	int a = 10;
	int* p = &a;
	int** pp = &p;
	return 0;

}


分析

在这里创建变量a此时系统空间会开辟一段空间进行存储a,此时a所在空间地址假设为0x12ff40,此时我们将a的地址取出放入指针变量p中,然后指针变量p也开辟一段空间进行存储a的地址,通过指针变量p我们可以找到a所在空间,指针变量p也有地址,它的地址为0x45ff87,我们取出它的地址放入二级指针变量pp中。

也就是我们可以将二级指针变量类型int **进行拆分成int*和*,前面的int*代表二级指针指向的那块空间的地址的变量类型是int*,*代表pp是一个二级指针变量。

5.1 二级指针的运用

二级指针的运用主要有如下两种:

  • *pp 通过对pp中的地址进⾏解引⽤,这样找到的是 p , *pp 其实访问的就是 p.
c 复制代码
int a = 10;   
*pp = &a;//等价于 p = &a;       
  • **pp 先通过 *pp 找到 p ,然后对 p 进⾏解引⽤操作: *p ,那找到的是 a
c 复制代码
**ppa = 30;       
//等价于*pa = 30;       
//等价于a = 30;       

六、指针数组

6.1指针数组的定义

在c语言中,我们常见到的数组有整型数组,字符数组

整型数组,存放的都是整型变量的数组

字符数组,存放的都是字符变量的数组

指针数组,存放的都是指针变量的数组

指针数组的每个元素都是⽤来存放地址(指针)的。

指针数组的每个元素是地址,⼜可以指向⼀块区域

6.2指针数组模拟二维数组

c 复制代码
int main()
{ 
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 2,3,4,5,6 };
	int arr3[] = { 3,4,5,6,7 };

	int* arr[] = { 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;
}

分析

arr[ i ] 是访问arr数组中的元素,arr[ i ]找到的数组元素指向了整型一维数组,arr[ i ][ j ]就是整型一维数组中的元素。

注意

在这里只是利用指针数组进行模拟二维数组,数组在内存中存储都是连续的,而arr1,arr2,arr3三个数组所处的空间是不同的,所以在利用指针数组模拟二维数组,这个不是真的二维数组

相关推荐
USER_A00121 分钟前
【C语言】第五期——函数
c语言
StickToForever3 小时前
第4章 信息系统架构(五)
经验分享·笔记·学习·职场和发展
李白同学6 小时前
【C语言】结构体内存对齐问题
c语言·开发语言
leegong231117 小时前
学习PostgreSQL专家认证
数据库·学习·postgresql
楼台的春风7 小时前
【MCU驱动开发概述】
c语言·驱动开发·单片机·嵌入式硬件·mcu·自动驾驶·嵌入式
Moonnnn.8 小时前
51单片机学习——动态数码管显示
笔记·嵌入式硬件·学习·51单片机
南宫生8 小时前
力扣每日一题【算法学习day.132】
java·学习·算法·leetcode
技术小齐8 小时前
网络运维学习笔记 016网工初级(HCIA-Datacom与CCNA-EI)PPP点对点协议和PPPoE以太网上的点对点协议(此处只讲华为)
运维·网络·学习
竹言笙熙9 小时前
代码审计初探
学习·web安全
日记成书9 小时前
物联网智能项目
物联网·学习