【C语言】13.指针(3)

🎉个人主页: 缘三水的博客
❄专栏传送门:C语言专栏(新手向)
🎀人生格言:行动是迷茫的最好解药


🚀个人介绍:


往篇回顾

【C语言】指针(1)

【C语言】指针(2)


文章目录


正文开始

(一)数组名的理解

数组名是首元素的地址

示例

c 复制代码
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	printf("&arr[0] : %p\n", &arr[0]);
	printf("arr     : %p\n", arr);
	return 0;
}

结果

拓展: %p 是专门用来打印地址的占位符,以16进制形式打印

但是有两个特殊情况

特殊情况1
sizeof(数组名)

这里的数组名表示整个数组

计算数组名的数据类型长度 时不是4或者8

而是 (元素个数 X 每一个元素的数据类型长度)

示例

特殊情况2
&数组名

&数组名,在值上与首元素的地址相同,但实际含义不同

&数组名,实际上是取出整个数组的地址

两者的指针类型是不同的

数组名(首元素地址)±整数,跨度是一个元素的大小

&数组名±整数,跨度是一个数组的大小

示例

&arr[0]和&arr[0]+1相差4个字节

arr和arr+1相差4个字节

+1就是跳过⼀个元素

然而&arr和&arr+1相差40个字节

+1操作是跳过整个数组

(二)指针访问数组

问题 向数组输入值,并打印

示例1

c 复制代码
int main()
{
	int arr[10] = { 0 };
 int sz = sizeof(arr) / sizeof(arr[0]);
	for (int i = 0; i < sz; i++)//输入
	{
		scanf("%d",&arr[i]);
	}
	for (int i = 0; i < sz; i++)
	{
		printf("%d ",arr[i]);
	}
	return 0;
}

示例2 利用指针向数组里输入值,改变数组内容

c 复制代码
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);//p可以改为arr
	}
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", *(p+i));//p可以改为arr
	}
	return 0;
}

也可以将输入和打印中的p改为arr

说明数组名就是首元素的地址,arr和p在这里是等价的

我们再看示例1和示例2中的printf函数,得出

c 复制代码
arr[ i ] ==  * (arr + i)

数组元素的访问在编译器处理的时候,也是转换成首元素的地址+偏移
求出元素的地址,然后解引用来访问的

又因为

c 复制代码
 *(arr + i) ==  *(i + arr)

而且

c 复制代码
 *(i+arr)== i[arr]//这种形式不常写,影响理解代码的效率

因此1,我们得出一种类似加法交换律的结论

arr[ i ] == * (arr + i) = = *(i +arr) == i[arr]

(三)一维数组传参本质

由结果看见

两个计算数组元素个数的结果不相同

实际上是因为一维数组传参,传的数组名是首元素的地址

只有在上面提到的两种特殊情况中,数组名才代表整个数组

这里函数的参数部分是本质是指针

因此,传一维数组应该用指针变量接收

而我们这里将首元素地址用int类型接收

指针变量计算大小则由环境决定

在x64环境下,指针变量大小是8,因此得出sz2 = 2

但是如果我们写int*arr接收的话,则程序会出现问题
总结:
一维数组传参本质上传的是首元素的地址
在函数内部求一维数组的元素个数是错误的

⼀维数组传参,形参的部分可以写成数组的形式 ,也可以写成指针的形式

示例
用int*接收,打印数组内容

(四)冒泡排序

冒泡排序两两相邻的元素进行比较

问题
一组数字,有n个元素,要排成升序形式

1.确定趟数

n个元素--进行n-1趟排序
2.一趟内部两两比较

一趟排序,数字内部两两相邻进行比较

前面数字大,就进行两个值的交换

直到成为正确的排序

根据上面两个步骤,写出代码

c 复制代码
//冒泡排序
//排成升序形式,两两相邻元素进行比较
void bubble_sort(int arr[], int sz)
{
	//1.确定趟数
	int i = 0;
	for (i = 0; i < sz - 1; i++)
	{
		//2.一趟内部排序
		int j = 0;
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}
void print_num(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}

int main()
{
	int arr[10] = { 10,9,8,7,6,5,4,3,2,1 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz);
	print_num(arr, sz);
	return 0;
}

但是如果碰见在n-1趟之内就已经排序好的,仍然需要进行比较,这就影响了效率

于是我们可以这样优化

在进行一次无需交换的趟数时,说明已经排序好了,就直接break 跳出循环,排序结束

而我们可以用一个状态量来判断是否进行了一次无需交换的趟

部分代码优化

c 复制代码
//优化
void bubble_sort(int arr[], int sz)
{
	//1.确定趟数
	int i = 0;
	for (i = 0; i < sz - 1; i++)
	{
		//2.一趟内部排序
		int j = 0;
		//假定排序好
		int flag = 1;
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])//进行一次无需交换的趟,说明不进入if语句内部,flag值仍为1
			{
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
				flag = 0;//说明是经过一趟排序,不是排序好的,flag赋为0
			}
		}
		if (flag == 1)
			break;//经过一次无需交换趟数,说明已经排序完,无需再判断,跳出循环
	}
}

(五)二级指针

我们知道指针变量也是变量,那指针变量是不是也有自己对应的地址(指针)呢?

是的,一级指针变量对应的地址被称作二级指针

c 复制代码
int a = 10;
int* pa = &a;//一级指针
int** ppa = &pa;//二级指针

我们来拆解这个二级指针

可以看到二级指针有两个*

后面的*说明这个ppa是指针变量

前面的 int *则说明这个指针指向的类型是 int *类型的

使用

c 复制代码
**ppa  等价于 *pa  等价于  变量a

(六)指针数组

定义
指针数组是存放指针的数组

类型

因为数组的类型是由元素的类型 决定

所以,如果存放的是整型类型的元素的地址 ,那么数组类型就是int *

如果存放的是字符类型的元素的地址,那么数组类型就是 char *

示例

c 复制代码
int main()
{
	int a = 10;
	int b = 20;
	int c = 30;
	int* arr[3] = { &a,&b,&c };
	return 0;
}

使用示例
利用指针数组,模拟一个二维数组

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));//*(arr[i] + j) == arr[i][j]
		}
		printf("\n");
	}
	return 0;
}

打印每一个元素时,也可以这样写

*(arr[ i ] + j) == arr[ i ][ j ]


总结

这篇文章我们详细介绍了指针部分内容,希望能对你有帮助

有错误的地方希望可以得到大佬的指正
最后不要吝啬你的点赞,收藏和评论!!!
感谢你的观看

相关推荐
wjs20241 小时前
SQL NOW() 函数详解
开发语言
Doris8931 小时前
【JS】JS进阶--作用域、函数、解构赋值、数组方法
开发语言·前端·javascript
!停1 小时前
深入理解指针3
c语言
山峰哥1 小时前
现代 C++ 的炼金术:铸就高性能与高可维护性的工程实践
java·开发语言·前端·数据结构·c++
小尧嵌入式2 小时前
QT软件开发知识流程及秒表计时器开发
开发语言·c++·qt·算法
ULTRA??2 小时前
强化学习算法分类,工具箱AI总结
开发语言·c++·人工智能
GISer_Jing2 小时前
Next.js 15 全栈开发实战指南
开发语言·javascript·ecmascript
JIngJaneIL2 小时前
基于Java在线考试管理系统(源码+数据库+文档)
java·开发语言·数据库·vue.js·spring boot
JIngJaneIL2 小时前
基于Java音乐管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot