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

指针(2)

1.数组名的理解

使用指针访问数组的内容时,有这样的代码:

int arr[10]={1,2,3,4,5,6,7,8,9,10}int * p=&arr[0];

&arr[0] 的方式拿到了数组的第一个元素的地址,但是其实数组名本来就是地址,而且还是首元素的地址:

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

 

 可以从上面的运行结果中就可以看见:确实数组名表示的就是数组首元素的地址。

如果是这样的话,嗯~那所有的情况都是数组名表示的是首元素的地址吗?

int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };printf("%d\n", sizeof(arr));return 0;
}

运行结果为什么是 40 呢?如果是数组首元素地址的话,结果应该是 4 or 8

 

其实数组名是数组首元素地址这个说法是正确的,但是有两种特殊情况:

  1. sizeof(数组名),数组名表示的是整个数组的大小
  2. &数组名,数组名表示的是整个数组,取出的是整个数组的地址

除此之外,遇到所用的数组名表示的都是数组首元素的地址 

int main()
{int arr[10] = { 0 };printf("%d\n", sizeof(arr));printf("\n&arr=%p\n", &arr); //代码1     //取出的是整个数组的地址printf("arr=%p\n", arr);  //代码2     //数组名表示的是数组首元素,所以取出的是数组首元素地址printf("&arr[0]=%p\n", &arr[0]);//代码3   //取出的是数组首元素地址//运行上面的代码会发现代码123的结果都是一样的,why???

为什么三个输出的结果都是一样的呢? 

代码23相同是因为,本身表示的就是数组首元素地址,代码1和代码23相同是因为 &arr--取出整个数组的地址时一般都是取首个数组元素的地址,这样也可以顺藤摸瓜找到整个数组的所有地址

上面的解释是否正确?还需要进行进一步的检测:

int main()
{int arr[10] = { 0 };printf("%d\n", sizeof(arr));printf("\n&arr      = %p\n", &arr); //代码1     //取出的是整个数组的地址printf("arr       = %p\n", arr);  //代码2     //数组名表示的是数组首元素,所以取出的是数组首元素地址printf("&arr[0]   = %p\n", &arr[0]);//代码3   //取出的是数组首元素地址//运行上面的代码会发现代码123的结果都是一样的,why???//代码23相同是因为,本身表示的就是数组首元素地址,代码1和代码23相同是因为 &arr--取出整个//数组的地址时一般都是取首个数组元素的地址,这样也可以顺藤摸瓜找到整个数组的所有地址//让我们进行“+1操作”再次检验一下,上面的解释是否正确?printf("\n&arr+1    = %p\n", &arr+1);    //   ?   +40printf("arr+1     = %p\n", arr+1);     // int * +4printf("&arr[0]+1 = %p\n", &arr[0]+1); // int * +4return 0;
}

2.使用指针访问数组

int main()
{int arr[10] = { 0 };int sz = sizeof(arr) / sizeof(arr[0]);int* p = arr;//p<==>arrint i = 0;for (i = 0; i < sz; i++){scanf("%d", &arr[i]);//scanf("%d", p + i);//scanf("%d", arr + i);}for (i = 0; i < sz; i++){printf("%d ", arr[i]);//arr[i]=*(arr+i)// arr[i]=*(p+i)// p[i]=*(p+i)//加法交换律:arr[i]=*(arr+i)=*(i+arr),arr[i]=*(p+i)=*(i+p)//*(i+arr)=i[arr],*(i+p)=i[p]//printf("%d ", i[p]);//printf("%d ", i[arr]);//*(i+arr)-->*(arr+i)-->arr[i]//printf("%d ", *(p + i));//printf("%d ", *(arr + i));//上面打印的语句多种多样,但是常常使用的就是第一个,其余作为拓展}

数组名 arr 是数组首元素的地址,可以赋值给 p ,其实数组名 arr 和 p 在这里是等价的,我们可以通过 arr [ i ] 来访问数组,那么 p [ i ] 也同样可以访问数组。

将 p [ i ] 换成 *( p + i )也是可以打印的,所以本质上 p [ i ] 是的等价于 * ( p + i )。

同理 arr[ i ] 也等价于 * ( arr + i ),其实数组的访问在编译器处理的时候,也是转换成首元素的地址+偏移量求出元素的地址,然后进行解引用的操作来访问的。

3.一维数组传参的本质

之前我们都是在函数外部计算数组的大小,那么可以在函数内部求得数组的元素个数吗?

void test(int * arr) //数组的形参可以有两种形式,虽然形式不同但是本质是一样的
{int sz = sizeof(arr) / sizeof(arr[0]);printf("%d\n", sz);//得到的并不是元素个数:1(x86) or 2(x64)
}int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };test(arr);//这里的数组名就是数组的首元素的地址return 0;
}

运行结果为什么是 1 呢?

数组传过去时是不需要再次创建新的数组,因为传的是数组的首元素地址
void test(int arr[])//int arr[]中的arr是一个指针变量,等价于 int * arr ,是一个接收变量的指针变量

那么指针变量的大小就只与环境有关,要么是(4/4=1),要么是(8/4=2),数组的形参可以用数组的形式来接收,因为数组的本质就是指针:*(arr+i)

总结:

  • ⼀维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式。
  • 要计算数组的元素个数,只能在主函数传参之前进行,将元素个数(sz)作为参数传入,这样才可以遍历数组

4.冒泡排序

核心思想:两两相邻的元素进行比较。

//冒泡排序
int count = 0;
BubbleSort(int arr[], int sz)
{int i = 0;//趟数for (i = 0; i < sz - 1; i++){//一趟冒泡排序的过程for (int j = 0; j < sz-1-i; j++){count++;//一对元素的比较if (arr[j] > arr[j + 1]){//进行元素的交换int tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tmp;}}}}int main()
{int arr[10] = { 9,0,1,2,3,4,5,6,7,8};//降序//想把数组调整成升序int sz = sizeof(arr) / sizeof(arr[0]);BubbleSort(arr, sz);int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}printf("\ncount=%d\n", count);return 0;
}//优化代码:
int count = 0;
BubbleSort(int arr[], int sz)
{int i = 0;//趟数for (i = 0; i < sz - 1; i++){ int flag = 1;//假设已经有序了//一趟冒泡排序的过程for (int j = 0; j < sz - 1 - i; j++){count++;//一对元素的比较if (arr[j] > arr[j + 1]){//进行元素的交换int tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tmp;flag = 0;//只要发生交换说明该数组还是无序,就还需要进行交换}}if (flag == 1)break;}}int main()
{int arr[10] = { 9,0,1,2,3,4,5,6,7,8 };//降序//想把数组调整成升序int sz = sizeof(arr) / sizeof(arr[0]);BubbleSort(arr, sz);int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}printf("\ncount=%d\n", count);return 0;
}

5.二级指针

指针变量也是变量,是变量就有地址,那么指针变量的地址存放在哪里?

存放指针变量地址的指针就是二级指针。以此类推,也有三级指针,存放的就是二级指针变量的地址,四级、五级指针变量……也是有的,但是用的最多就是二级指针变量了,三级指针用的很少。

int main()
{int a=10;int * pa=&a;int * * ppa=&pa; // * ppa中的* 代表的是 ppa 是一个指针变量, int * 是 pa 变量//的类型return 0;
}

对于二级指针的运算:

* * ppa 先通过 * ppa 找到 pa 的地址 ,然后对 pa 进行解引用操作:* pa ,那找到的就是 a 

* * ppa =19;
//等价于 *pa = 19;
//等价于   a = 19;

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

 

parr[ i ] 是访问的是 parr 数组的元素,parr[ i ]找到的数组元素的指的是整型一维数组的地址,parr[i][j]访问的就是整型一维数组中的元素。

指针数组模拟出二维数组:实际上是指针数组模拟出来的二维数组的效果,而不是真正的二维数组,这是因为指针数组里面的元素是不连续的,而二维数组里面的每个数组是连续的

相关文章:

  • gorm基础:自定义数据类型
  • 【Vulkan 入门系列】创建帧缓冲、命令池、命令缓存,和获取图片(六)
  • 【leetcode100】一和零
  • Linux | I.MX6ULL 文件系统
  • github新建一个远程仓库并添加了README.md,本地git仓库无法push
  • 小迪抓包技术算法加密(6-9天)
  • 如何高效利用呼叫中心系统和AI语音机器人
  • c++基础·左值右值
  • 京东百亿补贴杀入外卖市场:一场关乎即时零售未来的攻防战
  • 【Rust 精进之路之第9篇-所有权·核心】规则与移动 (Move):Rust 内存安全基石详解
  • SQL注入 02
  • [SpringBoot-1] 概述和快速入门(使用vscode)
  • 【C语言函数部分的重要知识点】--自定义函数,static和extern
  • 【2025软考高级架构师】——计算机系统基础(7)
  • Matlab PID参数整定和设计
  • B+树节点与插入操作
  • MySQL 视图
  • shell 正则表达式与文本处理器
  • 基于Python智能体API的Word自动化排版系统:从零构建全流程模块化工作流与版本控制研究
  • Flink介绍——实时计算核心论文之MillWheel论文详解
  • 消费维权周报丨上周合同纠纷类投诉多,合同未到期关闭门店等
  • 王东杰:重审康有为的“大同世界”
  • 朱雨玲:从前世界第一到兼职运动员,30岁后开始“玩”乒乓
  • 淄博张店区国资公司挂牌转让所持“假国企”股权,转让底价为1元
  • 科普|军团菌肺炎:春末夏初的隐形健康威胁
  • 一图看懂|特朗普政府VS美国顶尖高校:这场风暴如何刮起?