深入理解c指针(五)

目录

八、指针与数组

1、数组名的理解

2、使用指针访问数组

3、一维数组传参的本质

4、冒泡排序

5、二级指针

6、指针数组

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


八、指针与数组

int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int *p = &arr[0];
1、数组名的理解

  某一数组名就是该数组的首元素地址,即 arr 等价于 &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;
}

但是有两个例外,除此之外,任何地方使用数组名,数组名都表示首元素的地址。

(1) sizeof(数组名),sizeof中单独放数组名,这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节。

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

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


#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[0]、arr、&arr 的地址相同,那 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]);  //int*printf("&arr[0]+1 = %p\n", &arr[0]+1);// +4printf("arr = %p\n", arr);  //int*printf("arr+1 = %p\n", arr+1); // +4printf("&arr = %p\n", &arr);printf("&arr+1 = %p\n", &arr+1);  //+40return 0;
}


          这⾥我们发现 &arr[0] 和 &arr[0] + 1 相差4个字节,arr 和 arr+1相差4个字节,是因为 &arr[0] 和 arr 都是首元素的地址,+1就是跳过⼀个元素。但是 &arr 和 &arr+1 相差40个字节,这就是因为&arr是数组的地址,+1操作是跳过整个数组的。

2、使用指针访问数组

数组的输入与输出,代码如下:

#include <stdio.h>
int main()
{int arr[10] = { 0 };int i = 0;int sz = sizeof(arr) / sizeof(arr[0]);//输入int* p = arr;for (i = 0; i < sz; i++){scanf("%d", p + i);}//输出for (i = 0; i < sz; i++){printf("%d ", *(p + i));}return 0;
}

       对上述代码进行分析,数组名 arr 是数组首元素的地址,可以赋值给 p,其实数组名 arr 和 p 在这里是等价的。那既然 arr[ i ] 可以访问数组的元素,那 p[ i ]是否也可以访问数组呢?

#include <stdio.h>
int main()
{int arr[10] = { 0 };//输⼊int i = 0;int sz = sizeof(arr) / sizeof(arr[0]);//输⼊int* p = arr;for (i = 0; i < sz; i++){scanf("%d", p + i);//scanf("%d", arr+i);   //也可以这样写//scanf("%d", &arr[i]); }//输出for (i = 0; i < sz; i++){printf("%d ", p[i]);//printf("%d ", arr[i]);//printf("%d ", *(arr+i));//printf("%d",*(p+i));}return 0;
}

        将*(p + i) 换成 p[ i ] 也是能够正常打印的,所以本质上 p[ i ] 是等价于*(p+i)。同理 arr[ i ] 应该等价于*(arr + i),数组元素的访问在编译器处理的时候,也是转换成首元素的地址+偏移量求出元素的地址,然后解引用来访问的。

3、一维数组传参的本质

        我们之前都是在函数外部计算数组的元素个数,那我们可以把函数传给⼀个函数后,函数内部求数组的元素个数吗?

#include <stdio.h>
void test(int arr1[])  //int* arr
{int sz2 = sizeof(arr1) / sizeof(arr1[0]);printf("sz2 = %d\n", sz2);
}
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int sz1 = sizeof(arr) / sizeof(arr[0]);printf("sz1 = %d\n", sz1);test(arr); //这里的数组名就是数组的首元素地址return 0;
}

        我们发现在函数内部是没有正确获得数组的元素个数。(数组名是数组⾸元素的地址);那么在数组传参的时候,传递的是数组名,也就是说本质上数组传参本质上传递的是数组首元素的地址。所以函数形参的部分理论上应该使用指针变量来接收首元素的地址。那么在函数内部我们写sizeof(arr) 计算的是⼀个地址的大小(单位字节)而不是数组的大小(单位字节)。正是因为函
数的参数部分是本质是指针,所以在函数内部是没办法求数组元素个数的。(64位系统上,sizeof(int*) 的结果是8)

         通过函数打印数组,代码如下:

#include <stdio.h>
void Print(int arr1[])
{int i = 0;for (i = 0; i < 10; i++){printf("%d ", arr1[i]);  //arr1[i] == *(arr+i)}
}
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };Print(arr);return 0;
}上面代码等价于
#include <stdio.h>
void Print(int* arr)
{int i = 0;for (i = 0; i < 10; i++){printf("%d ", *(arr+i));  //arr1[i] == *(arr+i)}
}
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };Print(arr);return 0;
}

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

4、冒泡排序

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

基本思路:如果要对n个数进行冒泡排序,那么要进行n-1次趟比较,在第 1 趟比较中要进行n-1次两两比较,在第 j 趟比较中要进行 n - j 次两两比较。从这个基本思路中就会发现,趟数决定了两两比较的次数,这样就很容易将两个for循环联系起来了。

利用指针进行冒泡排序代码如下:(以升序为例) 

#include<stdio.h>void bubble_sort(int* p, int n)//参数接收数组元素个数
{int i = 0;for (i = 0; i < n - 1; i++)  //确定趟数{int flag = 1;//用来判断是否已经有序,若已经有序则不进行后续比较int j = 0;for (j = 0; j < n - i - 1; j++)  //每一趟进行比较{if (*(p + j) > *(p + j + 1)){flag = 0;//发⽣交换就说明,⽆序int tmp = *(p + j);*(p + j) = *(p + j + 1);*(p + j + 1) = tmp;}}if (flag == 1)//这⼀趟没交换就说明已经有序,后续⽆序排序了break;}
}
int main()
{int arr[20] = { 0 };int i=0,n;printf("请输入数组长度:");scanf("%d", &n);printf("请输入 %d 个数字:\n", n);for (i = 0; i < n; i++){scanf("%d", &arr[i]);}bubble_sort(arr, n); //冒泡排序函数for (i = 0; i < n;i++){printf("%d ", arr[i]);}return 0;
}

5、练习题-调整奇数偶数顺序 

说明:输入一个整数数组,实现一个函数,来调整该数组中数字的顺序使得数组中所有的奇数位于数组的前半部分,所有偶数位于数组的后半部分。

方法一:#include<stdio.h>void exchange(int arr[], int sz)
{int i = 0;int right = sz-1;for (i = 0; i < sz; )   //对数组内各个数字进行判断{int num = 0;if (arr[i] % 2 != 0 )     //如果是奇数,则无需调换{i++;}else               //如果是偶数,则与最后数字调换,再重新判断当前位置调换后的数字{num = arr[i];arr[i] = arr[right];arr[right] = num;right--;}}
}int main()
{int arr[20] = { 0 };int i = 0, n;printf("请输入数组长度:");scanf("%d", &n);printf("请输入 %d 个数字:\n", n);for (i = 0; i < n; i++){scanf("%d", &arr[i]);}exchange(arr, n); //调换函数 for (i = 0; i < n; i++) {printf("%d ", arr[i]);}return 0;
}方法二:1. 给定两个下标left和right,left放在数组的起始位置,right放在数组中最后一个元素的位置
2. 循环进行一下操作a. 如果left和right表示的区间[left, right]有效,进行b,否则结束循环b. left从前往后找,找到一个偶数后停止c. right从后往前找,找到一个奇数后停止d. 如果left和right都找到了对应的数据,则交换,继续a,void swap_arr(int arr[], int sz)
{int left = 0;int right = sz-1;int tmp = 0;while(left<right){// 从前往后,找到一个偶数,找到后停止while((left<right)&&(arr[left]%2==1)){left++;}// 从后往前找,找一个奇数,找到后停止while((left<right)&& (arr[right]%2==0)){right--;}// 如果偶数和奇数都找到,交换这两个数据的位置// 然后继续找,直到两个指针相遇if(left<right){tmp = arr[left];arr[left] = arr[right];arr[right] = tmp;}}
}
6、二级指针

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

         在这个例子中,swap() 函数接收两个二级指针 pq,并且通过交换它们所指向的地址来实现交换它们所指向的值。在 main() 函数中,我们定义了两个指针 ptr1ptr2 分别指向变量 ab 的地址,并且通过调用 swap() 函数来交换它们的指向。三级、四级、...指针同理。

#include <stdio.h>void swap(int** p, int** q) 
{int* temp = *p;*p = *q;*q = temp;
}int main() 
{int a = 10, b = 20;int* ptr1 = &a, * ptr2 = &b;printf("Before Swapping:\n");printf("*ptr1 = %d\n", *ptr1);printf("*ptr2 = %d\n", *ptr2);swap(&ptr1, &ptr2);printf("After Swapping:\n");printf("*ptr1 = %d\n", *ptr1);printf("*ptr2 = %d\n", *ptr2);return 0;
}

7、指针数组

指针数组是指针还是数组?
我们类⽐⼀下,整型数组,是存放整型的数组,字符数组是存放字符的数组。
那指针数组呢?是存放指针的数组。

#include <stdio.h>int main() 
{int a = 10, b = 20, c = 30;int* ptrArr[3];ptrArr[0] = &a;ptrArr[1] = &b;ptrArr[2] = &c;for (int i = 0; i < 3; i++) {printf("Value of element %d: %d\n", i, *(ptrArr[i]));}return 0;
}

8、指针数组模拟二维数组

首先理解第1、2点指针与数组名的关系:

利用指针数组来模拟二维数组的代码 如下:

其原理如下,指针数组中的每个指针变量分别指向对应数组的首元素地址:

那么根据其原理对代码进行如下改写,输出结果相同。

#include <stdio.h>
int main()
{int arr1[] = { 1,2,3,4,5 };int arr2[] = { 2,3,4,5,6 };int arr3[] = { 3,4,5,6,7 };//数组名是数组⾸元素的地址,类型是int*的,就可以存放在parr数组中//数组名就是数组首元素地址int* parr[3];parr[0] = arr1;parr[1] = arr2;parr[2] = arr3;int i = 0;int j = 0;for (i = 0; i < 3; i++){for (j = 0; j < 5; j++){/*printf("%d ", parr[i][j]);*/printf("%d ", *(parr[i] + j));}printf("\n");}return 0;
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2815766.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

深度学习PyTorch 之 RNN-中文多分类

关于RNN的理论部分我们已经在前面介绍过&#xff0c;所以这里直接上代码 1、 数据部分 1.1 读取数据 # 加载数据 data_path ./data/news.csv data pd.read_csv(data_path)# 预览数据的前几行 data.head()数据是csv格式&#xff0c;只有两列&#xff0c;第一列是标签&#…

Django数据库配置+迁移

目录 配置settings.py 在项目下新建bookstore应用 将新建应用添加到项目中 创建模型 执行数据库信息迁移 新增或修改数据库的信息 配置settings.py 找到项目同名文件夹下的settings.py文件&#xff0c;将原有的django默认配置修改为下图 引擎只需要将最后一部分改为对应…

网络:IPv6

1、由于IPv4地址资源枯竭&#xff0c;所以产生了IPV6。 版本长度地址数量IPv432 bit4 294 967 296IPv6128 bit340 282 366 920 938 463 374 607 431 768 211 456 2、IPv6的基本报头在IPv4报头基础上&#xff0c;增加了流标签域&#xff0c;去除了一些冗余字段&#xff0c;使报…

Mint_21.3 drawing-area和goocanvas的FB笔记(一)

一、关于freebasic和goocanvas Linux下的FreeBasic是C的一种实现&#xff0c;有指针、类、线程&#xff0c;正则表达式&#xff0c;可内嵌asm和其它语言c等&#xff0c;c的h库几乎都能改写后使用(不能直接用&#xff0c;它的.bi可从h近乎自动转换)&#xff0c;老的Quick Basic…

搭建服务器及跨域处理

使用内置的模块搭建服务器 自己电脑: 域名:localhost ip:127.0.0.1 http模块搭建服务器 const http = require(http)// 创建一个http对应的服务器,每次改完服务器的代码后都需要重新启动下服务器 /*方式一: const server = http.createServer((request,response)=>{…

第三章-Mybatis源码解析-以xml方式走流程-mapper解析(四)

3.2.2.7 selectKey解析 回到 XMLStatementBuilder.processSelectKeyNodes 的方法 private void processSelectKeyNodes(String id, Class<?> parameterTypeClass, LanguageDriver langDriver) {// 拿到所有 selectKey 节点List<XNode> selectKeyNodes context.…

jmeter 压测数据库

当前版本&#xff1a; jmeter 5.6.3mysql 5.7.39 简介 JMeter 是一个开源的 Java 应用程序&#xff0c;主要用于进行性能测试和负载测试。它支持多种协议&#xff0c;包括但不限于 HTTP、HTTPS、FTP、JDBC 以及各种 Web Services。对于数据库的压力测试可以使用 JDBC 协议与数…

【Docker】狂神说

图片后补 官网&#xff1a; https://www.docker.com/ Docker概述 Docker为什么出现 原因&#xff1a;环境配置不能跨平台 方案 传统方式&#xff1a;jar&#xff08;开发人员&#xff09; 部署&#xff08;运维人员&#xff09; 解决方式&#xff1a;开发打包上线一套流程 …

spring boot学习第十三篇:使用spring security控制权限

该文章同时也讲到了如何使用swagger。 1、pom.xml文件内容如下&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instanc…

LeetCode238题:除自身以外数组的乘积(python3)

代码思路&#xff1a; 当前位置的结果就是它左部分的乘积再乘以它右部分的乘积&#xff0c;因此需要进行两次遍历&#xff0c;第一次遍历求左部分乘积&#xff0c;第二次遍历求右部分的乘积&#xff0c;再将最后的计算结果一起求出来。 class Solution:def productExceptSelf(…

redis中的分布式锁(setIfAbsent)(expire)

目录 应用场景 代码实例1&#xff1a; 代码实例2&#xff1a; setIfAbsent&#xff1a; expire&#xff1a; 举例说明&#xff1a; 代码实例3&#xff1a; 代码实例4&#xff1a; 还是一个同事问的一个问题&#xff0c;然后闲着没事就记录下来了。多人操作同一个保单&a…

K8S存储卷与PV,PVC

一、前言 Kubernetes&#xff08;K8s&#xff09;中的存储卷是用于在容器之间共享数据的一种机制。存储卷可以在多个Pod之间共享数据&#xff0c;并且可以保持数据的持久性&#xff0c;即使Pod被重新调度或者删除&#xff0c;数据也不会丢失。 Kubernetes支持多种类型的存储卷…

【大数据架构(2)】kappa架构介绍

文章目录 一. Kappa架构1. Speed Layer (Stream Layer) - The Foundation of Kappa Architecture2. Stream Processing: The Heart of Kappa Architecture 二. Benefits of Kappa and Streaming Architecture1. Simplicity and Streamlined Pipeline2. High-Throughput Process…

数据服务安全的重要性

数据服务安全在当今信息化社会显得尤为重要。随着大数据、云计算、人工智能等技术的飞速发展&#xff0c;数据已经成为企业和组织的核心资产&#xff0c;数据服务安全也面临着前所未有的挑战。本文将从数据服务安全的重要性、常见威胁、防护策略以及未来发展趋势等方面进行探讨…

ROS 2基础概念#1:计算图(Compute Graph)| ROS 2学习笔记

在ROS中&#xff0c;计算图&#xff08;ROS Compute Graph&#xff09;是一个核心概念&#xff0c;它描述了ROS节点之间的数据流动和通信方式。它不仅仅是一个通信网络&#xff0c;它也反映了ROS设计哲学的核心——灵活性、模块化和可重用性。通过细致探讨计算图的高级特性和实…

2024年小程序云开发CMS内容管理无法使用,无法同步内容模型到云开发数据库的解决方案,回退老版本CMS内容管理的最新方法

一&#xff0c;问题描述 最近越来越多的同学找石头哥&#xff0c;说cms用不了&#xff0c;其实是小程序官方最近又搞大动作了&#xff0c;偷偷的升级的云开发cms&#xff08;内容管理&#xff09;以下都称cms&#xff0c;不升级不要紧&#xff0c;这一升级&#xff0c;就导致我…

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之FlowItem容器组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之FlowItem容器组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、FlowItem组件 子组件 可以包含子组件。 接口 FlowItem() 使用该接口来…

Android14之解决编译报错:bazel: no such file or directory(一百八十九)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

Stable Diffusion WebUI 图库浏览器插件:浏览器以前生成的图片

本文收录于《AI绘画从入门到精通》专栏&#xff0c;专栏总目录&#xff1a;点这里。 大家好&#xff0c;我是水滴~~ 本文介绍的插件叫图库浏览器&#xff0c;是一个用于浏览器以前生成的图片信息的插件。本文将介绍该插件的安装和使用&#xff0c;希望能够对你有所帮助。 文章…

GitHub宣布GitHub Copilot Enterprise的全面发布;使用Python与Gemma和MongoDB构建RAG系统的全过程

&#x1f989; AI新闻 &#x1f680; GitHub宣布GitHub Copilot Enterprise的全面发布 摘要&#xff1a;GitHub Copilot Enterprise是一款基于OpenAI的GPT-4模型的代码助手&#xff0c;它结合了十多年的真实、安全可靠的代码数据进行开发。该工具可以通过文本提示来获取、审核…