深入理解指针(2)

在上一篇深入理解指针(1)中我们已经初步了解指针地址;指针的解引用;指针变量类型作用,指针运算等知识,接下来我们将继续学习指针的相关内容,一起加油吧!!!

1. 数组名的理解

在之前的学习中我们知道可以将一串数字存放在整形指针当中,而且指针在内存当中存放是连续的,就可以通过取地址的方式找到数组当中想要的元素

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

在之前还提到过在函数实参中数组名表示首元素的地址,那在指针中这样规律是否还适用呢?

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

通过以上代码发现&arr[0]与arr的地址是相同的,因此可以得出数组就是首元素的地址

数组就是首元素的地址是在所有条件下都成立吗? 

1.在sizeof(数组名)中

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

如果在sizeof() 内arr表示首元素的地址,那么在x86环境下,应该sizeof(arr)的大小与sizeof(arr[0])一样为4字节,但在以上运行结果可以看出sizeof(arr)的大小为36字节,说明arr在sizeof内表示的不是首元素而是整个数组,计算的是整个数组的大小

1.在&数组名中 

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

 在x86环境下,由于地址是以16进制表示的,以上代码运行结果可以看出arr+1和&arr[0]+1都让地址先后移动4个字节,而&arr+1是让地址向后移动了36个字节,由于在整形数组arr中有9个元素,说明&arr+1是向后移动了一个数组大小的步长,因此可见在&arr不是取出数组首元素地址而是取出整个数组的地址

通过以上的例子现在就知道其实数组名就是数组首元素(第⼀个元素的地址),但是有两个例外:
• sizeof(数组名),sizeof中单独放数组名,这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节。
• &数组名,这里的数组名表示整个数组,取出的是整个数组的地址(整个数组的地址和数组首素
的地址是有区别的)

除此之外,任何地方使用数组名,数组名都表示首元素的地址

2. 使用指针访问数组

在之前学习完数组后我们知道可以用数组下标的方式来访问数组,例如以下代码

#include<stdio.h>
int main()
{int arr[9] = { 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;
}

其实还可以用指针的方式访问数组 

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

因为数组名表示首元素的地址,对以上代码的arr+i就是arr跳过i个元素,所以在以上代码中将scanf("%d",p+i);替换成为scanf("%d",arr+i);也是可行的

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

同理arr[i] 应该等价于 *(arr+i)

因为加法是支持交换律的所以*(arr+i)等价*(i+arr) 

结论:*(arr+i)=arr[i]=*(i+arr)=i[arr]

由此看见[ ]其实是一个操作符

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

3. 一维数组传参的本质

#include <stdio.h>
void test(int arr[10])
{int sz2 = sizeof(arr)/sizeof(arr[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;
}

在以上代码中为什么在函数test内的sz2输出的值为1呢?

首先通过以上学习知道数组名表示首元素的地址,所以在test()实参中的arr传给形参的是首元素的地址,所以在test函数内的sizeof(arr)计算出的是地址大小,又因为是在x86环境下,计算结果是4字节

在数组传参时,形参接收的其实是地址 ,所以应该用指针去接收,写成为int* arr的形式,因此我们上面代码形参写的int arr[10]其实不是数组而是指针,之前写成数组的形式只是为了让我们更好理解

之前在指针传参时候说过形参的数组元素个数可以省略不写,现在我们知道了因为形参接收的是地址,不需要接收数组大小,所以可以省略不写

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

通过以上的了解在计算数组元素个数时,要在数组所在的函数内就求出,不要通过传参的方式在另一个函数内计算

4. 冒泡排序

当有一是乱序的数字时候,要编写一个程序使得输出的数字大小由大到小,我们应该怎么实现呢

在这里使用到的是冒泡排序,冒泡排序的核心思想就是:两两相邻的元素进行比较,不满足顺序就交换,满足就找下一对

1.冒泡排序趟数分析

当输入为9 8 7 6 5 4 3 2 1 0  在冒泡排序中是有多套的,例如以下就是一套冒泡排序

 一套进行完就会将最大的数排到最后,此时就要进行下一套排序
 

最终进行完9趟后就使数由小到大排序了

因此从以上例子发现当输入值个数为n个时候,只需n-1趟排序就可以实现升序效果
 

2.冒泡排序每一趟内部比较分析 

例如在的数字串中,第一趟排序中需要进行9对数字的比较,在第二趟排序中需要进行8对数字的比较,所以当趟数为i时候;当在第n趟排序中需要进行n-1-i对数字的比较

3.代码实现 

void  bubble_sort(int* arr, int sz)
{for (int i = 0; i < sz - 1; i++)//每排序都会确定一个数的位置{for (int 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;}}}
}int main()
{int arr[10] = { 0 };int sz = sizeof(arr) / sizeof(arr[0]);for (int i = 0; i < sz; i++){scanf("%d", &arr[i]);}bubble_sort(arr, sz);for (int i = 0; i < sz; i++){printf("%d ", arr[i]);}return 0;
}

 测试程序运行

4.代码优化

在以上冒泡排序程序确实可以实现升序排列,但如果当输入数字串一开始就是生序的时,当走完一趟一对数都没有进行交换但还是会进行n-1趟排序,这就会使程序在运行时浪费很多时间,因此我们有什么优化的方法呢?

void  bubble_sort(int* arr, int sz)
{for (int i = 0; i < sz - 1; i++)//每排序都会确定一个数的位置{int flag=1;//假设这一趟已经有序for (int j = 0; j < sz - 1 - i; j++)//{if (arr[j] > arr[j + 1]){int flag=0;//这一趟无序int tmp = arr[j];arr[j ] = arr[j+1];arr[j + 1] = tmp;}}if(flag==1)//这⼀趟没交换就说明已经有序,后续⽆序排序了break;}
}int main()
{int arr[10] = { 0 };int sz = sizeof(arr) / sizeof(arr[0]);for (int i = 0; i < sz; i++){scanf("%d", &arr[i]);}bubble_sort(arr, sz);for (int i = 0; i < sz; i++){printf("%d ", arr[i]);}return 0;
}

5. 二级指针

之前我们学习的指针都是一级指针,现在将继续学习二级指针相关知识点
那么二级指针是什么呢?

二级指针就是存放指针变量地址的指针

int main()
{int a = 10;int* p = &a; //p是一级指针变量int** pp = &p;//pp就是二级指针变量return 0;
}

在int ** pp中第二个*表示pp是指针变量,第一个*表示pp指向的p类型是int * 

通过调试可以了解这几个变量之间的关系 

int main()
{int a = 10;int* p = &a; //p是一级指针变量int** pp = &p;//pp就是二级指针变量printf("a=%d", **pp);return 0;
}

 

 因为**pp=*p=a,当打印**pp时,就是a的地址找出变量a

6. 指针数组 

1.指针数组概念

在之前我们学习了整形数组就是存放整形的数组,数组的每个元素是整形类型;字符数组就是存放字符的数组,数组的每个元素是字符类型
因此就可以类比出指针数组就是
存放指针的数组,数组的每个元素是指针类型

例如在整形数组中,数组元素个数为5,若数组名为arr 则可表示为int arr[5]
因此但这个是指针数组时,就可以类别出该数组可表示为int* arr[5],这里的int*表示数组的元素类型为int*

int main()
{int a = 10;int b = 20;int c = 30;int* pa = &a;int* pb = &b;int* pc = &c;return 0;
}

但我们要创建多个相同类型的指针变量时,使用以上方法就会让代码臃肿,还有说明更好的方法呢?
因为a b c三个变量都是整形类型,这时就可以用到指针数组

int main()
{int a = 10;int b = 20;int c = 30;int* parr[3] = { &a,&b,&c };return 0;
}

 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[3] = {arr1,arr2,arr3};for (int i = 0; i < 3; i++){for (int j = 0; j < 5; j++){printf("%d ", *(*(parr + i) + j));}printf("\n");}return 0;
}

在以上代码中*(parr + i)是先找到数组名,也可以写成parr[i];后*(*(parr + i) + j) 就能找到数组内的元素,也可以写成parr[i][j]

上述的代码模拟出二维数组的效果,实际上并非完全是二维数组,因为每一行并分非是连续的

以上就深入理解指针(2)的全部内容,希望看完以上内容你能有所收获,接下来还会继续更新指针的其他内容,未完待续.... 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【计算机网络】HTTP协议详解实战抓包分析教程

文章目录 1.HTTP简介2.HTTP报文的结构3.HTTP协议中空行的作用4.uri和url的区别5.HTTP请求5.1 HTTP请求方法5.2 HTTP请求报头 6.HTTP响应6.1 状态码 7.HTTP位于应用层(基于TCP)8.非持久和持久连接8.1 非持久连接8.2 持久连接 1.HTTP简介 HTTP&#xff08;Hypertext Transfer Pr…

pandas style添加表格边框,或是只添加下边框等自定义边框样式设置

添加表格边框 可以使用如下程序添加表格&#xff1a; import dataframe_image as dfi import pandas as pd import numpy as npdf pd.DataFrame(np.random.random(size(10, 5))) df_style df.style.set_properties(**{text-align: center,border-color: black,border-width…

cubemx配置stm32f407VET6实现USB虚拟串口

背景&#xff1a; 最近做项目需要使用USB&#xff0c;一根数据线连接“mcu的板子”和“电脑”&#xff1b; 电脑上的串口助手通过USB线和mcu的USB通信&#xff1b; 原理图&#xff1a; 1&#xff09;外围电路2&#xff09;mcu引脚 软件实现&#xff1a; 1.cubemx配置USB_devic…

流程图用什么软件做?选择这4款,让工作事半功倍

流程图用什么软件做&#xff1f;流程图是一种直观、清晰的图形表示法&#xff0c;它通过节点、箭头和符号等元素&#xff0c;精确地展示出一系列步骤、决策点以及操作过程。这种图形化的呈现方式不仅简化了复杂信息的处理&#xff0c;还使得读者能够迅速把握整体流程&#xff0…

构建第一个ArkTS应用之@LazyForEach:数据懒加载

LazyForEach从提供的数据源中按需迭代数据&#xff0c;并在每次迭代过程中创建相应的组件。当在滚动容器中使用了LazyForEach&#xff0c;框架会根据滚动容器可视区域按需创建组件&#xff0c;当组件滑出可视区域外时&#xff0c;框架会进行组件销毁回收以降低内存占用。 接口…

上海交大携手阿里巴巴成立人工智能与系统联合实验室

5 月 8 日&#xff0c;上海交通大学电子信息与电气工程学院&#xff08;简称电院&#xff09;与阿里巴巴集团宣布共同成立人工智能与系统联合实验室&#xff08;后称“联合实验室”&#xff09;&#xff0c;并在上海交大闵行校区举行了揭牌仪式。 上海交大电院副院长王贺升教授…

python实现图片模式转换成素描模式

欢迎关注我👆,收藏下次不迷路┗|`O′|┛ 嗷~~ 目录 一.前言 二.代码 三.使用 四.分析 一.前言 素描,是一种用线条来描绘物体、场景或人物的绘画方式,它可以是简单的线条勾勒,也可以是复杂的光影渲染。这种艺术形式主要依赖线条和明暗关系来表现形态、空

PyTorch进行CIFAR-10图像分类

本节将通过一个实战案例来详细介绍如何使用PyTorch进行深度学习模型的开发。我们将使用CIFAR-10图像数据集来训练一个卷积神经网络。 神经网络训练的一般步骤如图5-3所示。 &#xff08;1&#xff09;加载数据集&#xff0c;并做预处理。 &#xff08;2&#xff09;预处理后的…

sin^2(x) 的图像

[TOC](sin^2(x) 的图像) 正文 这里记录一下 s i n 2 x sin^2{x} sin2x 的图像。 函数值以正弦的形式在 [0, 1] 区间内波动。 如果大家觉得有用&#xff0c;就点个赞让更多的人看到吧~

嵌入式文件系统

嵌入式文件系统 文件系统简介 在计算机系统中&#xff0c; 需要用到大量的程序和数据&#xff0c; 它们大部分以文件的形式存放在外部存储当中&#xff0c; 根据需要可随时调入内存使用 如果用户直接管理外存文件所面临的问题&#xff1a; 必须熟悉外存的物理特性了解各种存…

英语新概念2-回译法-lesson14

第一次回译 I had a amusing experience. I kept driving to the next town when I left a small village in the south of Franch.A teenager waved to me in the path.I stopped my car, he asked me to give him a lift.As soon as he get my car,I say good morning in Fr…

【Kubernetes】污点、容忍度、亲和性、调度和重启策略

标签、污点、容忍度、亲和性 一、标签1、定义2、给资源打标签【1】给Pod打标签【2】给Service打标签【3】给Node打标签 3、查看资源标签 二、节点选择器1、nodeName2、nodeSelector 三、污点、容忍度、亲和性1、node节点亲和性【1】硬亲和性【2】软亲和性 2、pod节点亲和性【1】…

安泰电子电压放大器应用及示例是什么样的

电压放大器是电子电路中常用的一种器件&#xff0c;用于将输入信号的电压放大至所需的输出电压。它在许多领域中有着重要的应用&#xff0c;包括通信、音频放大、仪器测量等。以下是电压放大器的一些应用及示例&#xff1a; 信号处理&#xff1a;在许多电子系统中&#xff0c;需…

Kasawaki川崎机器人故障维修

在当今的自动化工业领域&#xff0c;川崎工业机器人以其卓越的性能和可靠的工作效率赢得了广泛的赞誉。作为机器人的核心组成部分&#xff0c;伺服电机的作用至关重要。然而&#xff0c;就像所有机械设备一样&#xff0c;也可能会遭遇电机磨损或故障&#xff0c;需要适时的川崎…

vue自定义权限指令

定义v-hasPermi指令 /*** v-hasPermi 操作权限处理*/import useUserStore from /store/modules/userexport default {mounted(el, binding, vnode) {const { value } bindingconst all_permission "*:*:*";const permissions useUserStore().permissions&#xff…

linux - 搭建部署ftp服务器

ftp 服务: 实现ftp功能的一个服务,安装vsftpd软件搭建一台ftp服务器 ftp协议: 文件传输协议 (file transfer protocol),在不同的机器之间实现文件传输功能, 例如 视频文件下载,源代码文件下载 公司内部:弄一个专门的文件服务器,将公司里的文档资料和视频都存放…

基于死区补偿的永磁同步电动机矢量控制系统simulink仿真模型

整理了基于死区补偿的永磁同步电动机矢量控制系统simulink仿真&#xff0c;该模型使用线性死区补偿的PMSM矢量控制算法进行仿真&#xff0c;使用Foc电流双闭环 。 1.模块划分清晰&#xff0c;补偿前后仿真有对比&#xff0c;易于学习; 2.死区补偿算法的线性区区域可调; 3.自…

5.13网络编程

只要在一个电脑中的两个进程之间可以通过网络进行通信那么拥有公网ip的两个计算机的通信是一样的。但是一个局域网中的两台电脑上的虚拟机是不能进行通信的&#xff0c;因为这两个虚拟机在电脑中又有各自的局域网所以通信很难实现。 socket套接字是一种用于网络间进行通信的方…

Python接口自动化测试之动态数据处理

在前面的知识基础上介绍了在接口自动化测试中&#xff0c;如何把数据分离出来&#xff0c;并且找到它的共同点&#xff0c;然后依据这个共同点来找到解决复杂问题的思想。我一直认为&#xff0c;程序是人设计的&#xff0c;它得符合人性&#xff0c;那么自动化测试的&#xff0…

自由职业是种怎样的体验?普通人如何成为一名自由职业者?

自由职业在哪都能办公自由职业在哪都要办公。 放弃幻想&#xff0c;没有不辛苦的工作&#xff0c;5年经验后端开发程序员&#xff0c;已经从事自由职业1年半&#xff0c;今天就来客观分享一下自由职业的利与弊。 时间自由&#xff0c;减少中间商赚差价 自由职业最让人羡慕的就…