c语言指针的应用场景


1.什么是指针?
当我们提起指针的时候,可能第一反应会露出惊喜的表情
在这里插入图片描述
(但是我们其实没必要那么慌,因为当我们随着我们学习的越来越深入就会发现,指针虽然看起来难,实际上也不怎么简单。哈哈哈开玩笑的,我们都要有迎难而上的勇气哦)
好了我们进入正题:
内存会划分为一个个的内存单元,每个内存单元都有一个独立的编号-编号也称为地址。

地址在C语言中也称为指针,指针(地址)需要存储起来,这个变量就被称为指针变量。

2.指针变量的大小

指针(地址)的大小是固定的4/8个字节(在32位平台上是4个字节,在64位平台上是8个字节)。

3.什么是数组指针

其实数组指针并没有我们想象的那么难以理解,可以类比一下:

(1)整型指针—指向整型变量的指针,存放整型变量的地址的指针变量

(2)字符指针—指向字符变量的指针,存放字符变量的地址的指针变量

数组指针—指向数组的指针,存放的是数组的地址的指针变量

这里我们需要搞清楚谁才是主体,就比如好孩子,他主要是在讲孩子;同理,数组指针,他的主体是指针,而不是数组。

4.二维数组

如果我们要定义一个一维数组,我们只需要定义空间的大小就可以了。

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

那如果我们要定义一个二维数组 ,还需要定义它的行数和列数

int arr[3][3]={1,2,3,4,5,6,7,8,9};

我们可以把这个二维数组抽象成这样来理解:

二维数组的每一行可以理解为二维数组的一个元素,每一行又是一个一维数组。所以,二维数组其实是一维数组的数组。

二维数组的数组名就是其首元素的地址,也就是第一行的地址。

5.结合strlen和sizeof来加深对数组和指针的理解

(1) strlen是库函数,是求字符串长度的,统计的是在字符串中\0之前的字符的个数,如果没有\0就会一直往后找。

(2)sizeof是一个单目操作符,主要用来计算类型的大小。
(3)数组名是数组首元素的地址
但是有2个例外:
1.sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节
2.&数组名,这里的数组名表示整个数组,取出的是整个数组的地址
接下来我们一起来看几组代码

int main()
{
  int a[]={1,2,3,4};
  printf("%d\n",sizeof(a));
  printf("%d\n",sizeof(a+0));
  printf("%d\n",sizeof(*a));
  printf("%d\n",sizeof(a+1));
  printf("%d\n",sizeof(a[1]));
  printf("%d\n",sizeof(&a));
  printf("%d\n",sizeof(*&a));
  printf("%d\n",sizeof(&a+1));
  printf("%d\n",sizeof(&a[0]));
  printf("%d\n",sizeof(&a[0]+1));
  return 0;
}

在开始看解析之前大家可以自己先猜一下运行结果分别是什么。
(1)首先我们来看第一个,a是数组名,单独放在sizeof里面,所以它计算的是整个数组的大小,这是一个整型数组,每个数组元素4个字节,共有4个元素,所以它的大小是4* 4 = 16个字节。
(2)第二个,sizeof里面放的不是单独的数组名,所以它表示的是首元素的地址,是地址就是4/8个字节。这里前往不能想当然了,觉得a+0跟单独的a没什么区别,要额外小心。
(3)第三个,数组名是首元素的地址,对它解引用就能找到数组的首元素,所以它表示首元素的大小,4个字节。
(4)第四个,数组名是首元素的地址,数组名加一就是第二个元素的地址,是地址就是4/8个字节。
(5)第五个,表示数组下标为1的元素,也就是第二个元素的大小,4个字节。
(6)第六个,&数组名取出的是整个数组的地址,是地址就是4/8个字节。
(7)第七个,这里的*和&会相当于会相互抵消,所以其实这个表达式是sizeof(a),所以这里表示整个数组的大小,16个字节。
(8)第八个,&数组名取出的是整个数组的地址,所以它加一跳过的是整个数组,但是就算跳过了整个数组,还是地址,是地址就是4/8个字节。如下图所示:
在这里插入图片描述
(9)第九个,&a [0]取出的是数组首元素的地址,是地址就是4/8个字节。
(10)第十个,&a [0] +1取出的是数组第二个元素的地址,4/8个字节。
我们来看一个字符数组:

int main()
{
 char arr[]={'a','b','c','d','e','f'};
 printf("%d\n", sizeof(arr));
 printf("%d\n", sizeof(arr+0));
 printf("%d\n", sizeof(*arr));
 printf("%d\n", sizeof(arr[1]));
 printf("%d\n", sizeof(&arr));
 printf("%d\n", sizeof(&arr+1));
 printf("%d\n", sizeof(&arr[0]+1)); 
return 0;
}

大家可以带着自己的答案来看解释。
(1)第一个,数组名单独放在sizeof里面,表示计算整个数组的大小,这是一个字符数组,每个数组元素大小是1个字节,共有6个数组元素,所以是6个字节。
(2)第二个,不是数组名单独放在sizeof里面,所以它表示首元素的地址,是地址就是4/8个字节。
(3)第三个,数组名表示首元素的地址,对它解引用就是首元素,所以这里计算的是首元素的大小,1个字节。
(4)第四个,这里计算数组下标为1,也就是第二个元素的大小,1个字节。
(5)第五个,&数组名,取出的是整个数组的地址,是地址就是4/8个字节。
(6)第六个,&数组名+1,表示跳过整个数组指向的那个地址,是地址就是4/8个字节。
(7)第七个,&arr[0]表示取出第一个元素的地址,再加一就是第二个元素的地址,是地址就是4/8个字节。
我们再来看一组 strlen的,注意, strlen是求字符串长度的,统计的是在字符串中\0之前的字符的个数,如果没有\0就会一直往后找。

int main()
{
char arr[]={'a','b','c','d','e','f'};
printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr+0));
printf("%d\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr+1));
printf("%d\n", strlen(&arr[0]+1));
return 0;
}

(1)第一个,求’\0’之前的字符串长度,但是我们可以看到,这个数组中并没有’\0’,所以它在统计完数组中所有的元素后,还会继续往后面统计,直到遇到’\0’为止,但是在这个数组的后面我们并不知道存放着什么,所以这里产生的结果就是随机值。
(2)第二个,arr+0是数组首元素的地址,但是从首元素开始一直到最后都没有’\0’,没有’\0’,它就不会停下来,所以这里还是随机值。
(3)第三个,*arr找到的是数组的第一个元素,但是strlen()它的参数应该是地址,如果将a传进去,a的ASCII码值是97,那它就会从地址是97的地方开始统计,但是我们并不知道地址是97的内存里面存放了什么,这样就会造成非法访问,所以这里的写法是错误的,error。
(4)第四个,arr [1]表示数组的第二个元素,写法错误,error。
(5)第五个,&数组名取出整个数组的地址,整个数组的地址也是从首元素开始,所以这里还是随机值。
(6)第六个,&数组名+1,跳过整个数组,我们并不知道数组后面的内存情况,所以是随机值。
(7)第七个,&arr [0] +1找到的是第二个元素的地址,结果是随机值。
来看另外一组字符串的,字符串后面默认会有一个’\0’。

int main()
{
char arr[]="abcdef";
printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr+0));
printf("%d\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr+1));
printf("%d\n", strlen(&arr[0]+1));
return 0;
}

数组在这里插入图片描述
这就是数组里面实际的情况。
(1)第一个,统计数组中的字符个数,我们可以看到在’\0’前面共有6个字符,所以结果就是6。
(2)第二个,表示首元素地址,从首元素一直到’\0’有6个字符,所以结果是6。
(3)第三个,*arr表示第一个元素,非法访问,error。
(4)第四个,arr [1]表示第二个元素,非法访问,error。
(5)第五个,&arr,从首元素开始到’\0’,共6个字符,结果是6。
(6)第六个,&arr+1,会跳过整个数组,指向’\0’后面,所以结果是随机值。
(7)第七个,&arr[0] +1,表示指向第二个元素,从第二个元素开始到’\0’共有5个字符,所以结果是5。
我们再来看一组

int main()
{
char arr[]="abcdef";
printf("%d\n", sizeof(arr));
 printf("%d\n", sizeof(arr+0));
 printf("%d\n", sizeof(*arr));
 printf("%d\n", sizeof(arr[1]));
 printf("%d\n", sizeof(&arr));
 printf("%d\n", sizeof(&arr+1));
 printf("%d\n", sizeof(&arr[0]+1)); 
return 0;
}

(1)第一个,计算的是整个数组的大小,前面的6个字符再加上后面隐藏的’\0’,共7个字符,所以结果是7。
(2)第二个,表示首元素地址,4/8个字节。
(3)第三个,表示第一个元素大小,1个字节。
(4)第四个,表示第二个元素大小,1个字节。
(5)第五个,表示整个数组的地址,4/8个字节。
(6)第六个,表示指向跳过整个数组之后的地址,4/8个字节。
(7)第七个,表示指向数组的第二个元素的地址,4/8个字节。
我们再来看一组指针的:

int main()
{
char* p="abcdef";
printf("%d\n",sizeof(p));
printf("%d\n",sizeof(p+1));
printf("%d\n",sizeof(*p));
printf("%d\n",sizeof(p[0]));
printf("%d\n",sizeof(&p));
printf("%d\n",sizeof(&p+1));
printf("%d\n",sizeof(&p[0]+1));
return 0;
}

z这里先提前说明一点,指针指向一个字符串,其实指向的是字符串的第一个字符的地址。所以这里p指向的是a的地址。
(1)第一个,p指向的是a的地址,所以是4/8个字节。
(2)第二个,p指向的是a的地址,p+1指向的就是b的地址,4/8个字节。
(3)第三个,p指向的是a的地址,对它解引用找到的就是a,所以这里计算的是a的大小,1个字节。
(4)第四个,p [0] = *(p+0) = *p,和上面的一样,计算的是a的大小,1个字节。
(5)第五个,&p表示取p的地址,4/8个字节。
(6)第六个,&p+1,表示指向p后面的地址,4/8个字节。
如果大家对这里比较疑惑的话,可以看图理解:
在这里插入图片描述

(7)第七个,&p [0]表示取出a的地址,再加一表示指向b的地址,4/8个字节。
我们再来下一组:

int main()
{
char* p="abcdef";
printf("%d\n",strlen(p));
printf("%d\n",strlen(p+1));
printf("%d\n",strlen(*p));
printf("%d\n",strlen(p[0]));
printf("%d\n",strlen(&p));
printf("%d\n",strlen(&p+1));
printf("%d\n",strlen(&p[0]+1));
return 0;
}

(1)第一个,因为p指向数组的第一个字符,所以从a开始一直到字符串最后的’\0’,共6个字符,所以结果是6。
(2)第二个,p+1指向第二个字符,结果是5。
(3)第三个,*p表示第一个字符,这里strlen 需要的参数是地址,所以是非法访问,error。
(4)第四个, p [0] = *(p+0) =*p,表示第一个字符,非法访问,error。
(5)第五个,&p表示取出p的地址,p里面的情况我们并不清楚,所以是随机值。
(6)第六个,&p+1,表示指向p后面的地址,p后面的情况我们并不清楚,所以是随机值。
(7)第七个,p [0] = *p,表示第一个字符,再对它取地址就是取a的地址,再加一就是指向b的地址,5个字节。
好啦,我们再来看最后一组重量级的压轴选手:

int main()
{
int a[3][4]={0};
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(a[0][0]));
printf("%d\n",sizeof(a[0]));
printf("%d\n",sizeof(a[0]+1));
printf("%d\n",sizeof(*(a[0]+1)));
printf("%d\n",sizeof(a+1));
printf("%d\n",sizeof(*(a+1)));
printf("%d\n",sizeof(&a[0]+1));
printf("%d\n",sizeof(*(&a[0]+1)));
printf("%d\n",sizeof(*a));
printf("%d\n",sizeof(a[3]));
return 0;
}

在这里插入图片描述
这里给大家一张二维数组的抽象图来帮助理解。每一行就相当于一个一位数组,如果想描述一个具体元素的位置,就必须标明它的行和列,比如a [1] [1] ,就是第一行第一列的元素,但如果是a [1] ,则表示第一行的那个数组的数组名,而我们找到数组名通常表示数组首元素的地址,那一维数组a[1] 的首元素就是a[1] [0] ,对照上图理解会更容易。
(1)第一个,数组名单独放在sizeof里,表示整个数组的大小,这个数组共有12个元素,每个整型元素4个字节,所以结果是12 * 4 = 48个字节。
(2)第二个,表示求第0行第0列元素的大小,4个字节。
(3)第三个,a [0] 是第0行这个一维数组的数组名,所以这里是数组名单独放在sizeof里的情况,表示整个数组的大小,第0行共有4个元素,所以大小是16个字节。
(4)第四个,a [0]作为第0行的数组名,没有单独放在sizeo内部,没有&,a[0]表示数组首元素的地址,也就是a[0][0]的地址,所以a [0] +1是第0行第2个元素的地址,是地址就是4/8个字节。
(5)第五个,a [0] +1则表示第0行第2个元素的地址,对它解引用就可以找到第0行第2个元素,其大小是4个字节。
(6)第六个,数组名a表示首元素的地址,对二维数组来说,第0行就是它的首元素,a+1指向它的第二个元素的地址,也就是第1行的首元素的地址,是地址就是4/8个字节。
(7)第七个,* (a+1)= a [1] ,这个[ ]的运算在前面也出现过几次,需要我们格外小心注意,(我错好多次了呜呜呜),所以这里算是一维数组的数组名单独放在sizeof里的情况,计算第一行的元素大小,第一行4个元素,结果是16个字节。
(8)第八个,&a [0]表示取第0行的地址,加一跳过第0行,指向第1行的地址,即a [1] 的地址,是地址就是4/8个字节。这里不是整型指针,而是整型数组指针,即 int (*) [4] ,加一要跳过一个数组。
(9)第九个,对上面的数组指针解引用,得到的就是第一行的数组的元素,第一行共4个元素,16个字节。
(10)第十个,数组名a表示二维数组首元素,即第0行的地址,对其解引用得到的就是第0行的元素,16个字节。
(11)第十一个,看到a [3] 我们的第一反应是不对劲,这个数组一共才2行,哪里来的第三行,肯定是越界了,会出错。但如果我们去运行代码的话,会发现它还是会正常运行,是因为sizeof其实不会真正去访问内存,它只负责识别类型,然后计算它的大小,这里的a [3] 表示的情况还是数组名单独放在sizeof里的情况,计算的是整个数组的大小,我们给出的这个数组一行有4个元素,所以结果是16个字节。
本次的分享就到此为止啦,希望能够对你有帮助哦!

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

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

相关文章

Vitis HLS 学习笔记--Syn Report解读(1)

目录 1. 介绍 2. 示例一 2.1 HLS 代码 2.2 Report 解读 2.2.1 General Information 2.2.2 Timing Estimate 2.2.3 Performance & Resource Estimates 2.2.4 HW interfaces 2.2.4.1 硬件接口报告 2.2.4.2 导出至 Vivado 中的 IP 2.2.4.3 Port-Level Protocols 端…

如何安全进行速卖通自养号测评操作?

对于新加入的卖家而言,进行销量测评显得尤为关键。速卖通平台上的新店往往难以获得活动的扶持,且初始流量相当有限。因此,开店的首要任务便是积极展开测评工作,努力积累初始的评论和销售记录。测评的益处颇为显著,它不…

【Redis 开发】Redisson

Redisson RedissonRedisson分布式锁Redisson可重入锁Redission解决超时释放的问题Redission解决锁的判断一次性问题Redission分布式锁主从一致性问题 Redisson Redisson是一个在Redis的基础上实现的java驻内存数据网格,就是提供了一系列的分布式的java对象 官方地址…

嵌入式学习Day19

输入一个数字,实现数字的逆置,不使用字符串截取的方式 代码: #!/bin/bash echo number reverse read -p "please number:" num t0 while [ $num -ne 0 ] dot$((t*10num%10))((num/10)) done echo $t运行结果&#xff…

机器人系统ros2-开发实践03-监听节点的参数变化(C++)

背景: 通常,节点需要响应其自身参数或另一个节点参数的更改。 ParameterEventHandler 类可以轻松侦听参数更改,以便您的代码可以响应它们。本教程将向您展示如何使用 ParameterEventHandler 类的 C 版本来监视节点自身参数的更改以及另一个节…

el-table-column 表格列自适应宽度的组件封装说明

针对组件业务上的需求,需要给 el-table-column 加上限制,需保证表头在一行展示,部分列的内容要一行展示,自适应单项列的宽度; 1、先计算数据渲染后的 el-table-column 文本宽度; 因列表的有些数据需要做到…

MVP+敏捷开发

MVP敏捷开发 1. 什么是敏捷开发? 敏捷开发是一种软件开发方法论,旨在通过迭代、自组织的团队和持续反馈,快速响应需求变化并交付高质量的软件。相较于传统的瀑布模型,敏捷开发强调灵活性、适应性和与客户的紧密合作。敏捷开发方…

RestfulApi RestTemplate代码规范介绍

1.介绍 1.1 RestfulApi Restful API 是一种设计风格,代表了使用 HTTP 协议构建 web 服务的一种架构原则。REST(Representational State Transfer)的核心思想是,通过 URL 定位资源,使用 HTTP 方法(GET, POS…

Kafka 3.x.x 入门到精通(06)——Kafka进阶

Kafka 3.x.x 入门到精通(06)👉👉👉👉 Kafka进阶 3. Kafka进阶3.1 Controller选举3.2 Broker上线下线3.3 数据偏移量定位3.4 Topic删除3.5 日志清理和压缩3.7 页缓存3.8 零拷贝3.9 顺写日志3.10 Linux集群部…

12 c++版本的坦克大战

前言 呵呵 这大概是 大学里面的 c 贪吃蛇了吧 有一些 面向对象的理解, 但是不多 这里 具体的实现 就不赘述, 仅仅是 发一下代码 以及 具体的使用 坦克大战 #include<iostream> #include<windows.h> #include<conio.h> #include<ctime> #include…

基于FastGPT搭建知识库问答系统

什么是 FastGPT &#xff1f; FastGPT 是一个基于 LLM 大语言模型的知识库问答系统&#xff0c;提供开箱即用的数据处理、模型调用等能力。同时可以通过 Flow 可视化进行工作流编排&#xff0c;从而实现复杂的问答场景&#xff01; FastGPT 允许用户构建本地知识库&#xff0c;…

C# APS.NET CORE 6.0 WebApi在IIS部署报错

今天尝试着把基于 APS.NET CORE6.0开发的webAPI程序部署到IIS中&#xff0c;当打开网站地址时报错&#xff0c;无法打开&#xff0c;于是查找资料最终进行了解决。 打开 IIS →模块 查看列表中是否存在 AspNetCoreModuleV2&#xff0c;如下&#xff1a; 对应的应用池需要选择“…

node.js egg.js

Egg 是 Node.js 社区广泛使用的框架&#xff0c;简洁且扩展性强&#xff0c;按照固定约定进行开发&#xff0c;低协作成本。 在Egg.js框架中&#xff0c;ctx 是一个非常核心且常用的对象&#xff0c;全称为 Context&#xff0c;它代表了当前 HTTP 请求的上下文。ctx 对象封装了…

施耐德 Unity Pro 编程软件导入导出变量

适用范围 施耐德中高端PLC&#xff0c;使用的编程软件为 UnityPro &#xff08;最新版更名为 Ecostructure Control Expert&#xff09; 中端 PLC&#xff1a;Premium&#xff0c;M340高端 PLC&#xff1a;Quantum&#xff0c;M580 导出/导入变量 导出变量可导出【变量和 FB…

JavaScript进阶(十五):JS 垃圾回收机制_vue gc

内存&#xff1a;由可读写单元组成&#xff0c;表示一片可操作空间&#xff1b;管理&#xff1a;人为的去操作一片空间的申请、使用和释放&#xff1b;内存管理&#xff1a;开发者主动申请空间、使用空间、释放空间&#xff1b;管理流程&#xff1a;申请-使用-释放&#xff1b;…

社交巨头与去中心化:解析Facebook在区块链的角色

在数字化时代&#xff0c;社交媒体已经成为人们日常生活中不可或缺的一部分。作为全球最大的社交媒体平台&#xff0c;Facebook 在社交领域的影响力无可置疑。然而&#xff0c;随着区块链技术的崛起&#xff0c;Facebook 也开始探索如何将这一技术应用于其平台&#xff0c;以适…

基于LSTM算法实现交通流量预测(Pytorch版)

算法介绍 LSTM&#xff08;Long Short-Term Memory&#xff09;算法是一种特殊设计的循环神经网络&#xff08;RNN, Recurrent Neural Network&#xff09;&#xff0c;专为有效地处理和建模序列数据中的长期依赖关系而开发。由于传统RNN在处理长序列时容易遇到梯度消失和梯度…

ElasticSearch语句中must,must_not,should 组合关系

前言&#xff1a; 在实际应用中&#xff0c;发现当bool中同时使用must和should 没有达到想要的想过&#xff0c;而是只展示了must中的命中数据&#xff0c;所以打算探究一下bool中 三种逻辑关系的组合。 上述查询语句只展示了must的结果&#xff0c;没有should中的结果&#…

本地Windows主机,使用pycharm通过wsl的ubuntu来创建django项目

Windows主机在pycharm中通过wsl的ubuntu来创建django项目 需求&#xff1a;在windows主机中创建python项目再转接到linux服务器中运行&#xff0c;有点麻烦。【特别是存放日志文件或其他文件路径时需要修改为linux中的路径】 1&#xff1a;我的是windows主机 2&#xff1a;有…

基于java+springboot+vue实现的个人博客系统(文末源码+Lw)200

摘 要 随着国内市场经济这几十年来的蓬勃发展&#xff0c;突然遇到了从国外传入国内的互联网技术&#xff0c;互联网产业从开始的群众不信任&#xff0c;到现在的离不开&#xff0c;中间经历了很多挫折。本次开发的个人博客系统&#xff0c;有管理员&#xff0c;用户&#xf…