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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AI绘画:Stable Diffusion 终极炼丹宝典:从入门到精通

一、为什么要学习使用Stable Diffusion&#xff1f; 1.1 Stable Diffusion能干嘛&#xff1f;它是有多强大&#xff1f; Stable Diffusion的应用领域包括&#xff1a;真人AI美女&#xff0c;生成头像、壁纸、绘画辅助 我相信各位在浏览视频时&#xff0c;多多少少已经见过许多…

StarCloud开源行动:激发算力调度的创新潜力

01 关于StarCloud OpenCSG StarCloud 是一个集开源系统(Kubernetes ,K8S)与高性能计算(High Performance Computing,HPC)一体的混合算力调度平台。它专注于大模型训练和推理&#xff0c;并提供一站式服务&#xff0c;包括从训练到部署&#xff0c;以及多模型比较等。除了在人…

男士内裤哪个牌子质量好又舒服?五款不容错过的男士内裤

男士内裤&#xff0c;作为男士日常穿着的重要贴身衣物&#xff0c;其舒适度和透气性至关重要。尽管有些男士可能习惯长时间穿着同一条内裤&#xff0c;但为了确保健康和舒适&#xff0c;建议每3-6个月更换一次内裤。长时间不更换内裤会导致其舒适性和透气性下降&#xff0c;同时…

python数据分析——数据可视化(图形绘制基础)

数据可视化&#xff08;图形绘制基础&#xff09; 前言一、图形绘制基础Matplotlib简介使用过程sin函数示例 二、常用图形绘制折线图的绘制plot示例 散点图的绘制plot示例 柱状图的绘制bar示例 箱型图绘制plot.box示例 饼状图的绘制pie示例 三、图形绘制的组合情况多个折线图的…

基于PID控制的无人车侧向运动阿克曼转向控制仿真

写在前面&#xff0c;本文为研一下智能控制课程的课程作业报告&#xff0c;主要为基于无人车侧向运动模型的PID控制器设计&#xff0c;控制器设计比较简单&#xff0c;主要是对阿克曼转向模型进行搭建&#xff0c;PI参数调节部分的研究。设计内容分为两部分&#xff0c;分别是简…

Digimat在电池壳体SMC复合材料成型工艺中的应用

SMC工艺介绍及挑战 SMC&#xff08;Sheet Molding Compound的缩写&#xff0c;即片状模塑料&#xff09;是一种复合材料制造工艺。该工艺可以有效地代替金属&#xff0c;实现车辆轻量化目标。该工艺不仅能够显著降低车身重量&#xff0c;而且设计灵活&#xff0c;操作简单、易…

市场领先者MySQL的挑战者:PostgreSQL的崛起

最新的DB-Engines的排名&#xff0c;可以看到有个DB的上升趋势非常的猛&#xff0c;那就是PostgreSQL。今天我们就来看看这个数据库。 “The worlds most advanced Open Source Database” 这简介比较霸气&#xff1a;世界上最先进的开源数据库 发展史 PostgreSQL&#xff0c…

【Linux】进程间通信(一)---- 匿名管道

【Linux】进程间通信&#xff08;一&#xff09;---- 匿名管道 一.序1什么是进程间通信2.进程间通信的标准3.为什么需要进程通信 二.匿名管道1.原理2.使用3.四种情况4.五个特点 一.序 1什么是进程间通信 进程间通信 通信我们大致知道是啥&#xff0c;就是互相传递信息 那进程…

MySQL 8.4参考手册

5.1 连接到服务器和断开服务器连接 host 和 user 表示主机名&#xff0c;其中 MySQL服务器正在运行&#xff0c;并且您的MySQL帐户的用户名。 为您的设置替换适当的值。代表您的密码;输入它 当 MySQL 显示提示时。********Enter password: 5.2 输入查询 mysql> SELECT VERSI…

哪些软件格式在win跟linux上都能运行?

在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「linux的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01; 有一些软件格式在Windows和Li…

什么是Google SEO优化,如何做好谷歌seo排名?2024年谷歌搜索引擎优化(谷歌SEO)3分钟速通教程指南

1 - 什么是SEO&#xff1f; 谷歌排名优化&#xff08;SEO&#xff1a;Search Engine Optimization&#xff09;是指当您在谷歌搜索那里输入一个您正在推广的产品或服务的关键词时&#xff0c;如何在使您的站在Google里获得一个较高的排名位置而做的优化过程。谷歌排名优化的意…

第二证券资讯:多模态AI应用提速 机构扎堆调研相关个股

当地时间5月13日&#xff0c;OpenAI发布一款名为GPT-4o的新旗舰生成式AI模型&#xff0c;并计划在接下来的几周内“迭代”推出到公司产品中。 据介绍&#xff0c;GPT-4o的文本、推理、编码才能到达GPT-4 Turbo水平&#xff0c;速度是上一代AI大模型GPT-4 Turbo的两倍&#xff…

短视频世上无人再似她:成都鼎茂宏升文化传媒公司

短视频世上无人再似她 —— 记忆中的光影传奇 在短视频盛行的今天&#xff0c;每一位创作者都在用镜头捕捉生活&#xff0c;记录世界&#xff0c;但有那么一位艺术家&#xff0c;她的作品如同夜空中最亮的星&#xff0c;即便是在信息洪流中&#xff0c;也依然闪耀着独一无二的…

javaSE:类和对象

面向对象 java是一种面向对象的编程语言&#xff0c;面向对象就是把能为我们所用的东西直接拿来使用&#xff0c;省去中间过程&#xff0c;比如洗衣服&#xff0c;要完成这一个动作&#xff0c;我们本来需要一个盆&#xff0c;放水&#xff0c;放衣服&#xff0c;换水&#xf…

使用 cloudflare 免费服务,搭建临时邮箱,无需暴露自己的真实邮箱地址,保护个人隐私

使用 cloudflare 免费服务&#xff0c;搭建临时邮箱 地址 在线演示 &#x1f310;Github地址 https://github.com/find-xposed-magisk/cloudflare_temp_email 功能/TODO Cloudflare D1 作为数据库 使用 Cloudflare Pages 部署前端 使用 Cloudflare Workers 部署后端 email 转…

一文讲懂“预测滞后性”:详细解析

当我们谈论预测时&#xff0c;经常会遇到一个现象叫做“预测滞后性”。简单来说&#xff0c;预测滞后性指的是预测结果往往落后于实际发生的事件。这可能是由于数据收集、模型训练、预测算法的时间延迟&#xff0c;或者是由于预测模型本身的特性导致的。 目录 一、预测滞后性的…

Google I/O 2024:有关AI的一切已公布|TodayAI

2024年谷歌I/O大会圆满落幕&#xff0c;谷歌在会上发布了一系列更新&#xff0c;涵盖从最新的人工智能技术到Android系统的多项改进。此次大会特别关注于谷歌的Gemini人工智能模型&#xff0c;并详细介绍了这些模型如何被融入到Workspace、Chrome等多个应用程序中&#xff0c;展…

IT行业现状与未来趋势分析

IT行业现状与未来趋势显示出持续的活力和变革&#xff0c;以下是上大学网&#xff08;www.sdaxue.com&#xff09;关于IT行业现状与未来趋势分析&#xff0c;供大家参考。 当前现状&#xff1a; 市场需求持续增长&#xff1a;随着信息时代的深入发展&#xff0c;各行各业对信息…

汽车电子笔记之-012:旋变工作原理及软解码硬解码

目录 1、旋变 1.1、激励的产生 1.2、旋变的标定 2、旋变解码 2.1、旋变硬解码 2.2、软解码 1、旋变 旋转变压器&#xff08;旋变&#xff09;&#xff0c;是电机控制中常用的一种位置传感器&#xff0c;用来反应电机的转子位置&#xff0c;提供给软件做电机控制等相关算法…

Nginx+Keepalived高可用集群

NginxKeepalived高可用集群 服务器准备 服务器名IP软件包主从n1RIP 192.168.99.111 VIP 192.168.99.200nginx keepaliveMASTERn2RIP192.168.99.122 VIP 192.168.99.200nginx keepalivedh1RIP 192.168.99.133 VIP 192.168.99.200httpdh2RIP 192.168.32.144 VIP 192.168.99.200h…