深入如理解指针2

news2024/11/26 9:42:38

hello,各位小伙伴,本篇文章跟大家一起继续深入学习指针,感谢大家对我上一篇的支持,如有什么问题,还请多多指教

如果本篇文章对你有帮助,还请各位点点赞!!!

本篇主要学习数组在指针里的使用 ,话不多说,进入正题

1. 数组名的理解

想必大家对数组并不陌生,但是也有不少萌新并没有熟悉数组名的意思,来看如下代码:

int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int *p = &arr[0];
printf("%p\n",p);
printf("%p\n",arr);

 p已经得到了arr[0]的地址,打印p就是打印arr[0]的地址,那么下一行打印arr是什么东西,是把整个数组的地址,给打印出来吗?并不是,在这里,arr代表数组首元素的地址,所以这两个打印出来的是同一个地址,都为arr[0]的地址。

咱们再举一个例子:

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

这时候有小伙伴说:arr不就是数组首元素地址嘛,所以打印出来的结果就是4或者8嘛

注:编译器在不同环境下,地址大小所占字节不一样 

但事实上并不是,打印出来的结果是:40

这里的arr表示的是整个数组,那么整个数组的大小,不就是这个数组所占用的空间吗,所以答案40 = 4(每个元素占4个字节) X 10数组申请了10个元素的空间) ,就有小伙伴蒙了,这该怎么区分arr什么时候表示首元素地址、什么时候表示整个数组呢?其实并不难区分

其实数组名就是数组⾸元素(第⼀个元素)的地址是对的,但是有两个例外:

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

可以看到,打印出来的结果都为地址,有些地址一样,有些地址不一样

#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]);//取出arr[0]的地址
 printf("&arr[0]+1 = %p\n", &arr[0]+1);//取出arr[0]的地址,并以arrr[0]为单位对该地址进行+1操作,也就是得到arr[1]的地址
 printf("arr = %p\n", arr);//取出arr的地址,就是数组首元素arr[0]的地址
 printf("arr+1 = %p\n", arr+1);//取出整个数组的地址,并以一个元素为单位对该地址进行+1操作,即得到arr[1]的地址
 printf("&arr = %p\n", &arr);//取出arr的地址,也就是数组首元素的地址————arrr[0]的地址
 printf("&arr+1 = %p\n", &arr+1);//取出整个数组的地址,并以整个数组为单位对该地址进行+1操作
 return 0;
}

只要数组名前面有&或者sizeof就表示整个数组,除此之外都表示数组首元素

那么&arr+1就是数组的地址以整个数组为单位,进行+1操作,在上述例子中,从arr[0]的地址跳过了40个字节

相应的,&arr[0] +1就表示着:取出arr[0]的地址,并以arrr[0]为单位对该地址进行+1操作,也就是得到arr[1]的地址,跳过了4个字节

arr+1也不例外,不过意思不一样: 取出整个数组的地址,并以一个元素为单位对该地址进行+1操作,即得到arr[1]的地址,跳过了4个字节

 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);//指针+1,即以指针为单位进行+1操作
    //scanf("%d", arr+i);//也可以这样写
 }
 for(i=0; i<sz; i++)
 {
     printf("%d ", *(p+i));
 }
     return 0;    
}

arr+1 = p+1,思索片刻,那么:arr[0] ?= p[0]

实践出真知

#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);//也可以这样写
	}
	//输出
	for (i = 0; i < sz; i++)
	{
		printf("%d ", p[i]);
	}
	return 0;
}

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

同理arr[i] 应该等价于 *(arr+i),数组元素的访问在编译器处理的时候,也是转换成⾸元素的地址+偏移 量求出元素的地址,然后解引⽤来访问的。  

3. ⼀维数组传参的本质 

⼀维数组传参的本质其实就是将数组的地址传过去,让我们来看看下面的代码:

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

sz1和sz2仿佛都是在求数组里元素的个数,但sz2真的能够得到数组里与那稣的个数吗? 

 注:这是X86环境下得到的结果,X64环境下sz2结果为2

显然并不能,上个小节我们学习了:数组名是数组首元素的地址;那么在数组传参的时候,传递的是数组名,所以:本质上数组传参本质上传递的是数组首元素的地址。

 所以函数形参的部分理论上应该使⽤指针变量来接收⾸元素的地址。那么在函数内部我们写 sizeof(arr) 计算的是⼀个地址的大小(单位字节)而不是数组的大小(单位字节)。正是因为函数的参数部分是本质是指针,所以在函数内部是没办法求的数组元素个数的。

在传参的时候,我们有两种写法: 

void test(int arr[])//参数写成数组形式,本质上还是指针
{
 printf("%d\n", sizeof(arr));
}
void test(int* arr)//参数写成指针形式
{
 printf("%d\n", sizeof(arr));//计算⼀个指针变量的⼤⼩
}
int main()
{
 int arr[10] = {1,2,3,4,5,6,7,8,9,10};
 test(arr);
 return 0;
}

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

 4. 冒泡排序

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

示例代码:

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);
 for(i=0; i<sz; i++)
 {
     printf("%d ", arr[i]);
 }
 return 0;
}

5. ⼆级指针

二级指针,就是存放指针地址的指针。

我们都说指针变量存放地址,那么指针变量也是变量,它也有地址,我们再创建一个指针来存放指针的地址:

int a = 10;              //创建a变量;

int *p1 = &a;          //指针p1存放a的地址;

int **p2 = p1;         //指针p2存放指针p1的地址; 

p2就是二级指针,它的类型为int**,我们可以拆分来看:

int * *p 红色的*是为了告诉编译器,这是一个指针;

int *p 红色的*是为了告诉编译器,这个指针指向的类型为整型指针

int a = 10;
int *p1 = &a;
int **p2 = p1;
printf("%d\n",*p2);//*p2其实访问的就是p1
printf("%d\n",**p2);//**p2其实就是找到p1,然后再对p1进行解引用,找到a

 6.指针数组

首先,我们要搞明白,指针数组到底是个什么东西。举个例子:红色苹果,本质上是红色还是苹果?那肯定是苹果,那么指针数组,肯定是数组嘛

就和整型数组一样,整型数组存放的是整形;那指针数组存放的就是指针啦

结合上述所学,我们可以用指针数组来模拟二维数组:

#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] = {arr1, arr2, 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("\n");
     }
     return 0;
 }

二维数组其实就是数组存放数组,那么我用指针数组来存放数组的地址,不久能够模仿出二维数组了吗。解析图:

parr[i]是访问parr数组的元素,parr[i]找到的数组元素指向了整型⼀维数组,parr[i][j]就是整型⼀维数 组中的元素。

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

好啦,本篇文章对于指针就讲到这里,如果有什么问题,还请指教指教,希望本篇文章能够对你有所帮助,我们下一篇见!!! 

如你喜欢,点点赞就是对我的支持,感谢感谢!!!

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

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

相关文章

【机器学习】K近邻算法

一、原理 参数k的确定 带权重的k近邻算法 与 模糊k近邻算法 KNN算法用于回归问题 在使用欧氏距离时应将特征向量归一化 mahalanobis距离 如何确定数据的协方差矩阵 Bhattacharyya距离 距离度量学习 距离度量学习大边界最近邻分类 二、示例代码1 import numpy as np # 导入nump…

链接2:静态链接、目标文件、符号和符号表

文章目录 静态链接符号解析 (symbolresolution)重定位 (relocation) 目标文件1.可重定位目标文件2.可执行目标文件3.共享目标文件 可重定位目标文件text:rodata:.data.bss.symtab.rel.text.rel.data:debug:line:strtab: 符号和符号表由m定义并能被其他模块引用的全局符号由其他…

【开题报告】基于深度学习的驾驶员危险行为检测系统

研究的目的、意义及国内外发展概况 研究的目的、意义&#xff1a;我国每年的交通事故绝对数量是一个十分巨大的数字&#xff0c;造成了巨大的死亡人数和经济损失。而造成交通事故的一个很重要原因就是驾驶员的各种危险驾驶操作行为。如果道路驾驶员的驾驶行为能够得到有效识别…

奇数求和(C++)

系列文章目录 进阶的卡莎C++_睡觉觉觉得的博客-CSDN博客数1的个数_睡觉觉觉得的博客-CSDN博客双精度浮点数的输入输出_睡觉觉觉得的博客-CSDN博客足球联赛积分_睡觉觉觉得的博客-CSDN博客大减价(一级)_睡觉觉觉得的博客-CSDN博客小写字母的判断_睡觉觉觉得的博客-CSDN博客纸币(…

详细介绍如何使用深度学习自动车牌(ALPR)识别-含(数据集+源码下载)

深度学习一直是现代世界发展最快的技术之一。深度学习已经成为我们日常生活的一部分,从语音助手到自动驾驶汽车,它无处不在。其中一种应用程序是自动车牌识别 (ALPR)。顾名思义,ALPR是一项利用人工智能和深度学习的力量自动检测和识别车辆车牌字符的技术。这篇博文将重点讨论…

Servlet-Vue-JSON交互

Servlet-Vue-JSON交互 统一结果返回 定义 package org.example.result;import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;Data NoArgsConstructor AllArgsConstructor public class Result<T> {private Integer code;private St…

人工智能_AI服务器安装清华开源_CHATGLM大语言模型_GLM-6B安装部署_人工智能工作笔记0092

看到的这个开源的大模型,很牛,~关键让我们自己也可以部署体验一把了,虽然不知道具体内部怎么构造的但是,也可以自己使用也挺好. 可以部署在自己的机器上也可以部署在云服务器上. 安装以后,是可以使用python代码进行提问,然后返回结果的,这样就可以实现我们自己的chat应用了, …

5面试题--redis

慢查询⽇志&#xff1a;⽤于记录执⾏时间超过给定时⻓的命令请求&#xff0c;⽤户可以通过这个功能产⽣的⽇志来监视和 优化查询速度。 布隆过滤器&#xff1a;⼆进制数组进⾏存储&#xff0c;若判断元素存在则可能实际存在&#xff0c;若判断不存在则⼀定不存在。 redis中inc…

Linux文件目录结构_文件管理

Linux文件目录结构 Linux目录结构简洁 windows:以多根的方式组织文件 C:\ D:\ E:\ Linux: 以单根的方式组织文件/ Linux目录结构视图 注意区分&#xff1a; 系统管理员&#xff1a;中文“根”&#xff0c;root 系统目录&#xff08;文件夹&#xff09;&#xff1a;根&#xf…

解决ssh使用public key远程登录服务器拒绝问题

目录 使用场景windows安装ssh客户端使用powershell ssh登录服务器生成密钥文件ubuntu ssh服务器配置使用vscode远程登录使用Xshell远程登录使用MobaXtem远程登录Server refused our key问题解决方案 使用场景 使用vscode远程ssh登录使用public key不需要输入密码,比较方便. w…

使用opencv实现更换证件照背景颜色

1 概述 生活中经常要用到各种要求的证件照电子版&#xff0c;红底&#xff0c;蓝底&#xff0c;白底等&#xff0c;大部分情况我们只有其中一种&#xff0c;本文通过opencv实现证件照背景的颜色替换。 1.1 opencv介绍 OpenCV&#xff08;Open Source Computer Vision Librar…

Leetcode 380. O(1) 时间插入、删除和获取随机元素

文章目录 题目代码&#xff08;11.28 首刷看解析&#xff09; 题目 Leetcode 380. O(1) 时间插入、删除和获取随机元素 代码&#xff08;11.28 首刷看解析&#xff09; 1.length:表示的是数组的长度 数组 2.length():表示的是字符串的长度 字符串 3.size():表示的是集合中有多…

人工智能|机器学习——感知器算法原理与python实现

感知器算法是一种可以直接得到线性判别函数的线性分类方法&#xff0c;它是基于样本线性可分的要求下使用的。 一、线性可分与线性不可分 为了方便讨论&#xff0c;我们蒋样本增加了以为常数&#xff0c;得到增广样向量 y&#xff08;1;;;...;&#xff09;,则n个样本的集合为&a…

vue项目中使用jsonp跨域请求百度联想接口

一. 内容简介 vue项目中使用jsonp跨域请求百度联想接口 二. 软件环境 2.1 Visual Studio Code 1.75.0 2.2 chrome浏览器 2.3 node v18.14.0 三.主要流程 3.1 代码 核心代码 // 这个是请求函数doLeno() {// 挂载回调函数&#xff0c;不挂载&#xff0c;会报不存在window…

短视频账号矩阵系统源码/saas独立源头技术开发

一、批量剪辑&#xff08;采用php语言&#xff0c;数学建模&#xff09; 短视频合成批量剪辑的算法主要有以下几种&#xff1a; 1. 帧间插值算法&#xff1a;通过对多个视频的帧进行插帧处理&#xff0c;从而合成一段平滑的短视频。 2. 特征提取算法&#xff1a;提取多个视频中…

viple模拟器使用(四):unity模拟器中实现沿右墙迷宫算法

沿右墙迷宫算法 引导 线控模拟可以使得通过用户手动操作&#xff0c;实现机器人在模拟环境下在迷宫中行走&#xff08;即&#xff1a;运动&#xff09;&#xff0c;算法可以使得机器人按照一定的策略自动行走&#xff0c;沿右墙迷宫算法就是其中的一种策略。 目的 运行程序后&…

MFC容器中使用标准库容器,内存违规

问题描述 CArray中元素不管是直接或间接使用标准库容器&#xff0c;会引发内存违规。与CArray内部实现有关。测试代码如下&#xff1a; struct tagData {std::vector<int> m_Values; }; CArray<tagData, tagData> mIntVecArray; {tagData mData;mData.m_Values.p…

【08】Python运算符

文章目录 1.算术运算符2.赋值运算符3.条件运算符4.逻辑运算符5.比较运算符6.运算符的优先级本期博客中,我们将学习python中常用的运算符的用法。              1.算术运算符 1.加法运算符(+): a = 10 b = 5 c = a + b print(c

仿制剧情吧网站源码 帝国CMS剧情介绍模板

帝国CMS7.5剧情介绍模板&#xff0c;仿制剧情吧网站的风格。该模板并非用于直接播放电影&#xff0c;而是用文字描述剧情&#xff0c;同时包含手机版。本站免费分享供站长学习研究使用。采用伪静态技术&#xff0c;无需生成HTML。出于美观考虑&#xff0c;自带数据仅供本地环境…

跨越威胁的传说:揭秘Web安全的七大恶魔

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…