当前位置: 首页 > news >正文

C语言指针(三):数组传参本质、冒泡排序与二级指针详解

1. 数组名的理解

在上⼀篇blog中我们在使⽤指针访问数组的内容时,有这样的代码:
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int *p = &arr[0];
这⾥我们使⽤ &arr[0] 的⽅式拿到了数组第⼀个元素的地址,但是其实数组名本来就是地址,⽽且
是数组⾸元素的地址:
#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 = %p\n", arr);
return 0;
}
输出结果:数组名和数组⾸元素的地址打印出的结果相同,数组名就是数组⾸元素(第⼀个元素)的地址
但是,数组名如果是数组⾸元素的地址,这句话不够严谨:
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("%zu\n", sizeof(arr));
return 0;
}
输出的结果是40,如果arr是数组⾸元素的地址,那输出应该的应该是8才对(x64)
数组名的理解有两个特殊情况:
sizeof(数组名),sizeof中单独放数组名,这⾥的数组名表⽰整个数组,计算的是整个数组的大小
&数组名,这⾥的数组名表⽰整个数组,取出的是整个数组的地址(整个数组的地址和数组⾸元素
的地址数值上相同,但意义不同)

#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 = %p\n", arr);
printf("&arr = %p\n", &arr);
return 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;
输出结果:
&arr[0] = 0077F820
&arr[0]+1 = 0077F824
相差4个字节
arr = 0077F820
arr+1 = 0077F824
相差4个字节
&arr = 0077F820
&arr+1 = 0077F848
两者相减得0x28,40byte,是整个数组的大小,&arr+1跳过的是整个数组。这个原理和上一篇blog中介绍的不同类型的指针的意义相似,类型不同,单位不同,所能操作的内存空间就不同

除此之外,任何地⽅使⽤数组名,数组名都表⽰⾸元素的地址

2. 使用指针访问数组

int main()
{int arr[10] = { 0 };//给数组中存值for (int i = 0; i < 10; i++){scanf("%d", *(arr+i));}//打印数组for (int i = 0; i < 10; i++){printf("%d", *(arr+i));}return 0;
}

 不用指针打印数组元素时,一般这样写arr[i],这里用指针的表达方式是这样,二者等价

arr[i] == *(arr + i) 
//加法交换律
arr[i] == *(arr + i) == *(i+arr) == i[arr] ???

发现了一个有趣的点

是否可以这样表示呢??? 

int main()
{int arr[10] = { 0 };//给数组中存值for (int i = 0; i < 10; i++){scanf("%d", (arr + i));}//打印数组for (int i = 0; i < 10; i++){printf("%d ", i[arr]);//没毛病6666}return 0;
}

通过运行发现,编译通过。布什戈门,有点逆天了哈哈哈哈哈

这说明:[]仅仅是一个操作符而已,这是这样子表示的,在计算机内部表示的就是*(i+arr)

3. 一维数组传参的本质

通过一段代码进行观察:
//一维数组传参的本质
size_t test(int a[])
{size_t sz = sizeof(a) / sizeof(a[0]);return sz;
}int main()
{int a[] = { 1,2,3,4,5,6,7 };printf("1. %zu\n", sizeof(a) / sizeof(a[0]));printf("2. %zu\n", test(a));
}

运行结果:两次打印的结果并不相同,第一种就是7,元素个数;第二种则为2(x64)

倒推一下,a[0]为int大小是4,结果是2说明sizeof(a)为8,这就说明这是一个指针。而我们实参就是数组名,也就是首元素的地址

//一维数组传参的本质
size_t test(int a[])//本质上是指针
{size_t sz = sizeof(a) / sizeof(a[0]);//传进来的是a,不是&a,不是sizeof(a)//因此并不能算出来整个数组的大小return sz;
}int main()
{int a[] = { 1,2,3,4,5,6,7 };printf("1. %zu\n", sizeof(a) / sizeof(a[0]));printf("2. %zu\n", test(a));
}
//本质上传的是首元素地址
函数的参数部分是本质是指针,在函数内部是没办法求的数组元素个数的
总结:⼀维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式

4. 冒泡排序

冒泡排序的核⼼思想就是:两两相邻的元素进⾏⽐较
//⽅法1
void bubble_sort(int arr[], int sz)//参数接收数组元素个数
{int i = 0;for(i = 0; i < sz-1; i++){int j = 0;for(j = 0; j < sz-i-1; j++){if(arr[j] > arr[j+1]){int tmp = arr[j];arr[j] = arr[j+1];arr[j+1] = tmp;}}}
}int main()
{int arr[] = {3,1,7,5,8,9,0,2,4,6};int sz = sizeof(arr)/sizeof(arr[0]);bubble_sort(arr, sz);int i = 0;for(i = 0; i < sz; i++){printf("%d ", arr[i]);}return 0;
}

在改基础上,还可以进行改良:

如果排序到下一趟时,发现后面已经是升序了,就没必要在依次检索一遍了;若还想知道进行交换了多少次,也可以进行记录 

//⽅法2 - 优化
//冒泡排序
int bubble_sort(int arr[],int sz)
{int cnt = 0;for (int i = 0; i < sz - 1; i++){int flag = 1;for (int j = 0; j < sz - i -1; j++){int tmp = 0;if (arr[j] > arr[j + 1]){cnt++;//每交换一次,记录一下tmp = arr[j + 1];arr[j + 1] = arr[j];arr[j] = tmp;flag = 0;}}if (flag)//排完一趟发现并没有实现交换,则不用再排序了,解释://因为冒泡排序是从前到后依次比较的,没有实现交换,那么则都是整齐的{return cnt;}}return cnt;
}int main()
{int arr[] = { 3,2,1,0 };int sz = sizeof(arr) / sizeof(arr[0]);int cnt = bubble_sort(arr,sz);for (int i = 0; i < sz; i++){printf("%d ", arr[i]);}printf("\n");printf("%d\n", cnt);return 0;
} 

5. ⼆级指针

指针的指针
#include <stdio.h>int main(){int a = 100;int * pa = &a;int* * ppa = &pa;return 0;
}

pa是一个指针变量,势必也会有开辟内存空间,该空间也势必有自己的指针,即使这块空间中存放的就是一个指针

6. 指针数组

//指针数组
//类比 字符数组 整形数组
//存放指针的数组
是存放指针的数组,指针数组的每个元素都是⽤来存放地址(指针)的
指针数组的每个元素是地址,⼜可以指向⼀块区域

7. 指针数组模拟二维数组

//使用指针数组模拟一个二维数组
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 };//二维数组for (int i = 0; i < 3; i++){for (int j = 0; j < 5; j++){printf("%d ", arr[i][j]);}printf("\n");}return 0;
}
数组arr中放着各个数组的首元素地址,通过这些地址又可以找到他们数组中的其他元素,可以看成是一个二维数组,但:
上述的代码模拟出⼆维数组的效果,实际上并⾮完全是⼆维数组,因为每⼀⾏并⾮是连续的。
http://www.lryc.cn/news/605735.html

相关文章:

  • FISCO BCOS Gin调用WeBASE-Front接口发请求
  • [硬件电路-111]:滤波的分类:模拟滤波与数字滤波; 无源滤波与有源滤波;低通、带通、带阻、高通滤波;时域滤波与频域滤波;低价滤波与高阶滤波。
  • 操作系统数据格式相关(AI回答)
  • 无人船 | 图解基于LQR控制的路径跟踪算法(以欠驱动无人艇Otter为例)
  • 学习笔记《区块链技术与应用》第4天 比特币脚本语言
  • Docker部署Seata
  • Linux核心转储(Core Dump)原理、配置与调试实践
  • 前端-移动Web-day2
  • 野生动物巡查系统(H题)--2025 年全国大学生电子设计竞赛试题
  • ENSP防火墙部署
  • 快速理解RTOS中的pendsv中断和systick中断
  • Java Stream进阶:map是“一对一”,flatMap是“一对多”
  • H.266 vs H.265/AV1/H.264:从工程落地看下一代视频系统的技术演进
  • 前端核心技术Node.js(五)——Mongodb、Mongoose和接口
  • Web3:在 VSCode 中基于 Foundry 快速构建 Solidity 智能合约本地开发环境
  • 硬核技术协同:x86 生态、机密计算与云原生等技术如何为产业数字化转型筑底赋能
  • 云原生环境 DDoS 防护:容器化架构下的流量管控与弹性应对
  • 对git 熟悉时,常用操作
  • Java学习第九十一部分——OkHttp
  • MongoDB用户认证authSource
  • 微服务架构技巧篇——接口类设计技巧
  • 智能交通顶刊TITS论文分享|跨区域自适应车辆轨迹预测:TRACER框架攻克域偏移难题!
  • PageOffice实现文档并发控制
  • 2025年人形机器人动捕技术研讨会将在本周四召开
  • JVM面试通关指南:内存区域、类加载器、双亲委派与GC算法全解析
  • 暑期算法训练.11
  • wpf之ControlTemplate
  • RabbitMQ 消费者确认 (Ack/Nack) (With Spring Boot)
  • HUD抬头显示器-杂散光测试设备 太阳光模拟器
  • FastGPT + Kymo AI生态创新平台,搭建企业智能化知识管理