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

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.xdnf.cn/news/16880.html

相关文章:

  • 熵感知金字塔生成理论(Entropy-Aware Pyramid Generation, EAPG)
  • 机器学习03——数据与算法初步2
  • vue 开发总结:从安装到第一个交互页面-与数据库API
  • 【普中STM32精灵开发攻略】--第 2 章 开发板功能及使用介绍
  • 渗透RCE
  • IACheck助力办公环境装修检测报告的合规性
  • docker运行时目录/var/lib/docker 学习
  • 1 - 视频处理IP核之Video In to AXI4-Stream
  • 汽车线束行业AI智能化MES解决方案:推动智能制造与质量升级
  • 编程语言Java——核心技术篇(六)解剖反射:性能的代价还是灵活性的福音?
  • JVM面试通关指南:内存区域、类加载器、双亲委派与GC算法全解析
  • kafka使用kraft
  • Java设计模式之《命令模式》
  • LeetCode 刷题【23. 合并 K 个升序链表】
  • MongoDB用户认证authSource
  • 17-C语言:第18天笔记
  • AI 类型的 IDE
  • Cesium 快速入门(六)实体类型介绍
  • 【运维基础】Linux 文件系统基本管理
  • 【Leetcode】2683. 相邻值的按位异或
  • 前缀和-1314.矩阵区域和-力扣(LeetCode)
  • C# 枚举器和迭代器(常见迭代器模式)
  • VBA代码解决方案第二十七讲:禁用EXCEL工作簿右上角的关闭按钮
  • ubuntu22.04系统入门 linux入门 简单命令基础复习 实现以及实践
  • 经典屏保问题 - 华为OD机试真题(Java 题解)
  • pytorch程序语句固定开销分析
  • dubbo源码之消费端启动的高性能优化方案
  • 28. 找出字符串中第一个匹配项的下标
  • C++-2025.7.31
  • 1️⃣4️⃣ OOP:类、封装、继承、多态