C语言 #指针数组 #数组指针 #数组参数、指针参数

文章目录

前言

一、指针数组

1、概念:

2、指针数组有什么用呢?

二、数组指针

1、数组指针的定义

2、数组名与 &数组名 的区别

3、数组指针如何初始化?

4、数组指针的用法

三、根据代码区分 指针数组 和 数组指针

四、数组参数、指针参数

1、一维数组传参

2、二维数组传参

3、一级指针传参

4、二级指针传参

总结


前言

路漫漫其修远兮,吾将上下而求索。


一、指针数组

指针数组是一个存放指针的数组。这里就简单讲述,想要了解地更清楚可以戳此链接:http://t.csdnimg.cn/7R5S7

1、概念:

在前面我们学过字符数组、整型数组等,字符数组即数组中的元素是char 类型,char ch [10]; 则表示数组ch 中有10个为char 类型的元素; 整型数组即数组中的元素是 int 类型,int arr [10] ; 则表示数组arr 中有10个为int 类型的元素;(假设有10个元素)显然指针数组就应该其数组有10个元素为指针,即 int* p1[10] ; 则说明数组 p1中有10个为 Int * 类型的元素;

在 int * p1[10] ; 中p1 先与 [10] 结合,说明 p1 为数组,而其前面的int* 代表着数组p1 中的元素为 int * 类型;同理数组的元素也可以是 char * 、short * 、 long * 、 float * 、 double * 等类型。

2、指针数组有什么用呢?

举例:利用指针数组模拟实现二维数组

注:此处并不为二维数组,因为二维数组在内存中是连续存放的;而这里的模拟的二维数组并不是连续存放在一块空间中;

代码如下:

#include<stdio.h>int main()
{int arr1[] = { 1,1,1,1,1 };int arr2[] = { 2,2,2,2,2 };int arr3[] = { 3,3,3,3,3 };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("%d ", parr[i][j]);//利用下标进行访问}printf("\n"); //打印完一行就换行 }return 0;
}

 代码运行结果如下:

分析:arr1 、arr2 、 arr3 是整型数组;因为数组名代表着数组的首元素地址,所以数组parr 中的元素为数组名,本质就是 int* 类型的元素;所以指针数组parr 的类型为 int* ; 数组名可以看作数组名,也可以当作数组首元素的地址;故而想要访问指针数组中存放的数组名中的元素便有两种方式,一是通过数组的下标进行访问;二是利用地址的方式进行访问;

一:

利用下标访问数组parr 中的元素 --> parr [i] ; parr数组中的元素就是数组名,即 parr[0] 相当于 arr1, parr[1] 相当于 arr2 , parr [2] 相当于 arr3 ; 那么访问arr1 arr2 arr3 中的元素也可以利用下标的形式,即得到 parr [i][j] ;

二:

利用地址的方式来访问 parr 中的元素--> 数组名parr 代表着数组parr中首元素的地址,即指针,可以利用指针+1 来跳过一个元素再解引 --> *( parr + i ),所以得到了数组parr 中的元素;然而parr 中的数组名又可以看作其首元素地址 -->  * (*( parr + i ) + j) ;

二、数组指针

1、数组指针的定义

之前我们学过整型指针: int * p1,* 代表着变量 p1 为指针变量,指针p1 指向对象的类型为 int ; 字符指针 : char * p2, * 代表着变量 p2 为指针变量,指针 p2 指向对象的类型为char  ; 浮点型指针: float * p3 ,* 代表着变量 p3 为指针变量,指针变量p3 指向对象的类型为 float ;等,那么数组指针呢?应该就是指向数组的一个指针变量;

在学习数组的时候,曾说过数组的类型,详情可以戳此链接:http://t.csdnimg.cn/DTudO

注:例如: int arr[10] ; --> 整型数组arr的类型为 int [10] ; 即去掉数组名剩下的为数组的类型;

如果将以上例子中的数组arr 的地址存放起来 --> int (*p) [10] = &arr; 

 * 与变量 p 结合代表着变量p 为指针变量,int [10] 为数组arr 的类型,代表着数组有10 个为int 类型的元素; 

注:这里的 [ ] 的优先级高于 * ,所以要用() 将 *p 括起来,以保证 p 先与 * 结合。

2、数组名与 &数组名 的区别

eg .  int arr [10];

显然,数组名arr 代表着其首元素的地址;

数组名不是为首元素地址吗?为什么此处 sizeof(arr) 的结果为40 byte? 因为数组名不能只能理解为其首元素地址;

数组名通常表示的都是其首元素的地址;

但是有两个例外

1、sizeof(数组名) ,这里的数组名表示整个数组,所以计算的是整个数组的大小

2、&数组名,这里的数组名表示的依然是整个数组,所以 &数组名 取出的是整个数组的地址;

分析:&arr[0] 、arr 针对的是数组中的首元素地址,即此地址的类型为一个整型,也就是 int* ,那么这些地址 +1 便会跳过 4 byte;而&arr 取出来整个数组的地址,即此地址的类型为40byte ,那么&arr +1 便会跳过 40byte ;

观察代码的运行结果也可得知,0136FBD8 --> 0136FBDC 之间差距了 4byte;  而 0136FBD8--> 0136FC00 --> 十六进制差了:28 --> 十进制:2*16+8*1 = 40,单位为字节;

3、数组指针如何初始化?

注:想要存放数组的地址,指针变量类型中的[ ]里必须要标明数组的元素个数

站在数组arr 的角度来看,它有10个元素,但站在指针变量 p 的角度来看,它认为自己指向的数组里面没有元素,即为0--> 指针变量p 的类型为 int (*)[0] ;

所以初始化数组指针时,一定要标明数组元素的个数;

4、数组指针的用法

数组指针不适合用于一维数组中,至少是用到二维、三维数组之中;

例二:(常见用法)

代码如下:

#include<stdio.h>void print(int(*p)[4], int r, int c)
{int i = 0;int j = 0;for (i = 0; i < r; i++){for (j = 0; j < c; j++){printf("%d ", *(*(p + i) + j)); //利用地址进行访问//printf("%d ", p[i][j]); 利用下标进行访问}printf("\n");}
}int main()
{int arr[3][4] = { {1,2,3,4},{1,2},{1,2,3}};print(arr, 3, 4);//二维数组的首元素是其第一行的元素,即一维数组return 0;
}

代码运行结果如下:

注:二维数组数组名代表着其首元素的地址,而二维数组的首元素是其一行元素,即一维数组,所以在函数形参部分要用数组指针来接收 (当然,实参传的是二维数组,也可以用二维数组来接收);既然形参是一维数组,即一维数组名,而一维数组名又为其首元素地址;于是乎此处便又有两种方式来访问二维数组arr 中的所有元素;一是利用数组的下标进行访问,二是利用地址进行访问;

利用数组下标进行访问: p[i][j] ; 形参 int(*p)[4] 接受了实参 arr ,arr 为二维数组,其数组名代表着其第一排元素的地址即一维数组的地址,而变量 p 为指向一维数组的指针变量,即 可以将p看作二维数组名arr  ;那么利用数组的下标进行访问即可得到   p[i][j] ;

利用地址进行访问:*(*(p + i) + j) ;形参中变量 p为一个指向一维数组的指针变量;指针变量的类型为 int ( * )[4] ,所以 p+1 ,便会跳过此一维数组而访问到下一个一维数组(即二维数组的下一个元素);又可以将一维数组名当作其首元素的地址,*( p + i ) 为一维数组的数组名,*( p + i) + j 代表着访问到各个元素的地址,想要得到元素就要对地址进行解引用操作,即 *(*(p + i) + j ) ; 

三、根据代码区分 指针数组 和 数组指针

int arr[5] ;

int * parr1[10] ;

int (*parr2)[10] ;

int (*parr3[10]) [5];

分析:

1、arr 与 [ ] 先结合,代表着 arr 为数组,即数组arr 有10个int 类型的元素;所以arr 是整型数组;

2、 由于 [ ] 的优先级高于 * ,故而 parr1 先于 [ ] 结合,代表着parr1 为数组,即数组 parr1 有10个为 int * 类型的元素;所以 parr1 是整型指针数组;

3、( ) 将* 与 parr2 括起来,代表着parr2 先与 * 结合,代表着变量 parr2 为指针变量,指针变量 parr2 指向了有10个为 int* 元素的数组,即此数组的类型为 int (*) [10] ;所以 parr2 为数组指针;

4、由于[ ] 的优先级高于* ,所以parr3 先与 [ ] 结合,代表着parr3 为数组,即有10个为 int (*)[5] 类型的数组,所以数组 parr3 为存放数组指针的数组;所以parr3 为数组指针数组;(可以理解为二维数组)

四、数组参数、指针参数

1、一维数组传参

一维数组的数组名有两种理解方式,一是理解为数组名代表着整个数组,想要访问数组中的元素的话就用下标访问即可;二是,将一维数组名当作其首元素的地址,那么想要访问此一维数组中的元素就要利用地址进行访问;

所以形参接收形式上可以划分为两种,一是用一维数组来接受,二是利用其元素类型的指针来接收;

注:形参用一维数组来接收实参传过来的一维数组时,形参部分可以不用写数组元素的个数,但是写了也不会报错,因为编译器根本就不会用这个值;

例3:

 int arr1[10] = {0} ;

若是将数组arr1传参,那么形参部分便可以写作:

int arr[ ]         //形参部分用一维数组来接收,省略了数组的元素个数

int arr[10]       //形参部分用一维数组来接收,并未省略了数组的元素个数

int * p             //形参部分用传过来数组元素类型的指针来接收

同理,例4:

int* arr2[10] = { 0 } ;

若是将数组arr2传参,那么形参部分便可以写作:

int* arr [ ]       //形参部分用一维数组来接收,省略了数组的元素个数

int* arr [10]        //形参部分用一维数组来接收,并未省略了数组的元素个数

int** p      //形参部分用传过来数组元素类型的指针来接收;数组arr2 中的元素类型为int* ;

此处 int** p第二个 * 代表了变量p 是指针变量,即指针变量p 指向的对象类型为 int* ;

2、二维数组传参

同理,二维数组的数组名也有两种理解,一是理解为数组名代表着整个数组,想要访问数组中的元素的话就用下标访问即可;二是,将一维数组名当作其首元素的地址,只不过二维数组的首元素是其第一行的元素,那么想要访问此二维数组中的元素就要利用地址进行访问;

所以二维数组的形参接收形式上可以划分为两种,一是用二维数组来接受,二是利用此二维数组首元素(其第一行元素)的类型的指针来接收;

注:

1、形参用二维数组来接收实参传过来的二维数组时,形参部分可以不用写数组行的信息,但是写了也不会报错,因为编译器根本就不会用这个值;但是不可省略列的信息;

2、二维数组的首元素为其第一行的元素

例4:

int arr3 [3][5] = { 0 } ;

若是将数组arr3传参,那么形参部分便可以写作:

int arr[ ][5]     //形参部分用二维数组来接收,省略了数组行的信息

int arr[3][5]     //形参部分用二维数组来接收,没有省略了数组行的信息

int (*p) [5]       //形参部分用此二维数组的首元素类型的指针来接收;

                       //二维数组首元素的类型为int [5] , * 代表着变量 p 为指针变量,指针变量p 的类型为 int (*) [5] ,指针变量指向的对象的类型为 int [5];

3、一级指针传参

若是实参传一级指针,那么实参是怎么样的类型,形参就用什么类型的指针接收即可;

那么有哪些数据传参需要用一级指针变量来接受呢:

1、一级指针变量

2、对变量进行& 操作

3、数组的数组名(其数组的元素不为指针,数组名为其首元素地址;不论几维数组)

4、二级指针传参

若是实参传二级指针,那么实参是怎么样的类型,形参就用什么类型的指针接收即可;

那么有哪些数据传参需要用二级指针变量来接受呢:

1、二级指针变量

2、对一级指针变量进行& 操作

3、指针数组的数组名(指针数组的元素为指针;不论几维数组)


总结

1、指针数组是一个存放指针的数组。

2、想要访问指针数组中存放的数组名中的元素便有两种方式,一是通过数组的下标进行访问;二是利用地址的方式进行访问;

3、数组名通常表示的都是其首元素的地址;

但是有两个例外

1、sizeof(数组名) ,这里的数组名表示整个数组,所以计算的是整个数组的大小

2、&数组名,这里的数组名表示的依然是整个数组,所以 &数组名 取出的是整个数组的地址;

4、数组指针的初始化:想要存放数组的地址,指针变量类型中的[ ]里必须要标明数组的元素个数

5、[ ] 的优先级高于 *

6、那么有哪些数据传参需要用一级指针变量来接受呢:a、一级指针变量     b、对变量进行& 操作  c、数组的数组名(其数组的元素不为指针,数组名为其首元素地址;不论几维数组)

7、那么有哪些数据传参需要用二级指针变量来接受呢:a、二级指针变量  b、对一级指针变量进行& 操作   c、指针数组的数组名(指针数组的元素为指针;不论几维数组)

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

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

相关文章

VLAN通讯实验

目录 拓扑图 需求 需求分析 配置过程 1、手工配置 2、 使用DHCP获得IP地址信息 3、测试全网是否可达 拓扑图 需求 1、PC1、PC3属于VLAN 2 2、PC2、PC4属于VLAN 3 3、通过DHCP使得PC获取IP地址信息 4、全网可达 需求分析 1、先手工配置网段&#xff0c;VLAN 2为192.168.1…

在invidia jetpack4.5.1上运行c++版yolov8(tensorRT)

心路历程(可略过) 为了能在arm64上跑通yolov8,我试过很多很多代码,太多对库版本的要求太高了; 比如说有一个是需要依赖onnx库的,(https://github.com/UNeedCryDear/yolov8-opencv-onnxruntime-cpp) 运行成功了报错error: IOrtSessionOptionsAppendExecutionProvider C…

【网络安全的神秘世界】文件包含漏洞

&#x1f31d;博客主页&#xff1a;泥菩萨 &#x1f496;专栏&#xff1a;Linux探索之旅 | 网络安全的神秘世界 | 专接本 | 每天学会一个渗透测试工具 一、概述 文件包含&#xff1a;重复使用的函数写在文件里&#xff0c;需要使用某个函数时直接调用此文件&#xff0c;而无需再…

使用代理IP进行本地SEO优化:如何吸引附近的客户?

在今天竞争激烈的互联网时代&#xff0c;如何利用代理IP进行本地SEO优化并吸引附近的客户已经成为许多企业和网站面临的关键挑战。本文将探讨使用代理IP的策略和技巧&#xff0c;以帮助公司提高在本地市场的可见性和吸引力&#xff0c;从而扩大本地客户群体。 1. 代理IP在本地…

打卡第24天------回溯算法

表达一下自己每天的刷题想法。希望我刷完代码随想录,自己的进步能有大幅度的提升。 一、复原IP地址 leetcode题目链接:93.复原IP地址 题目描述: 给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s 中插入 . 来形成。…

Blackbox AI-跨时代AI产物,你的私人编程助手

1. 引言 随着人工智能技术的飞速发展&#xff0c;我们的生活方式正在经历前所未有的变革。从智能家居到自动驾驶&#xff0c;AI已经渗透到我们生活的方方面面。而在这场科技革命中&#xff0c;Blackbox 网站凭借其先进的技术和全面的功能&#xff0c;成为了众多AI产品中的佼佼者…

ubuntu一些好用的开发工具及其配置

1 终端模糊搜索fzf https://github.com/junegunn/fzf 输入某命令&#xff0c;比如 conda &#xff0c;按下ctrlR&#xff0c;会显示和该命令匹配的历史命令的列表 有了这个工具再也不用记忆太复杂的命令&#xff0c;只需要知道大概几个单词&#xff0c;输入即可搜索。 其搜索…

基于SVPWM的霍尔传感器FOC实现过程

FOC算法笔记 起源&#xff1a;使用ST WorkBench配置的HALL BLDC控制算法抖动严重。 ST的电机控制算法&#xff0c;代码非常高端大气&#xff0c;值得学习。 HAL库与LL库混用&#xff0c;效率很高。很多中断回调都直接重写了&#xff0c;没有使用HAL库那一套。 只是好多地方…

JavaScript关键词

JavaScript 关键词 JavaScript 语句常常通过某个关键词来标识需要执行的 JavaScript 动作。 下面的表格列出了一部分将在教程中学到的关键词&#xff1a; 关键词 描述 break 终止 switch 或循环。 continue 跳出循环并在顶端开始。 debugger 停止执行 JavaScript&…

matplotlib 画图函数,最常用的

并排显示2个图片 import os import numpy as np from PIL import Image import matplotlib.pyplot as pltimage1 Image.open(a.png) image2 Image.open(a2.png)# Create a figure with two subplots (1 row, 2 columns) fig, axes plt.subplots(1, 2, figsize(10, 5))# Di…

双非二本毕业生如何进入嵌入式系统领域?

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「嵌入式的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01; 二本的话学历上会吃点亏…

docker Docs相关使用文档链接

Docker Docshttps://docs.docker.com/ docker compose | Docker Docshttps://docs.docker.com/reference/cli/docker/compose/docker | Docker Docshttps://docs.docker.com/reference/cli/docker/

axios请求大全

本文讲解axios封装方式以及针对各种后台接口的请求方式 axios的介绍和基础配置可以看这个文档: 起步 | Axios中文文档 | Axios中文网 axios的封装 axios封装的重点有三个&#xff0c;一是设置全局config,比如请求的基础路径&#xff0c;超时时间等&#xff0c;第二点是在每次…

MySQL零散拾遗(四)--- 使用聚合函数时需要注意的点点滴滴

聚合函数 聚合函数作用于一组数据&#xff0c;并对一组数据返回一个值。 常见的聚合函数&#xff1a;SUM()、MAX()、MIN()、AVG()、COUNT() 对COUNT()聚合函数的更深一层理解 COUNT函数的作用&#xff1a;计算指定字段在查询结果中出现的个数&#xff08;不包含NULL值&#…

PostgreSQL 中如何重置序列值:将自增 ID 设定为特定值开始

我是从excel中将数据导入&#xff0c;然后再通过sql插入数据&#xff0c;就报错。 需要设置自增ID开始值 1、确定序列名称&#xff1a; 首先&#xff0c;需要找到与的增字段相关的序列名称。假设表名是 my_table 和自增字段是 id&#xff0c;可以使用以下查询来获取序列名称…

H264编码标准环路滤波原理

方块效应产生原因 原因 1&#xff1a;最重要的一个原因是基于块的帧内和帧间预测残差的 DCT 变换。变换系数的量化过程相对粗糙&#xff0c;因而反量化过程恢复的变换系数带有误差&#xff0c;会造成在图像块边界上的视觉不连续。原因 2&#xff1a;其次原因自于运动补偿预测。…

元素标签的attr属性的巧妙利用(值得收藏)

前言 需求如图&#xff1a; 虽然可以通过一个标签&#xff0c;直接赋值&#xff0c;然后通过定位也能实现需求;但是另一种方式更巧妙&#xff0c;有时候可以通过少量代码实现多样的需求&#xff0c;把一个元素展示在盒子上的方法&#xff0c;通过使用元素的attr属性&#xf…

【漏洞复现】APP分发签名系统index-uplog.php存在任意文件上传漏洞

漏洞描述 APP分发签名系统index-uplog.php存在任意文件上传漏洞 免责声明 技术文章仅供参考,任何个人和组织使用网络应当遵守宪法法律,遵守公共秩序,尊重社会公德,不得利用网络从事危害国家安全、荣誉和利益,未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵…

刷题了:232.用栈实现队列| 225. 用队列实现栈 |20. 有效的括号 | 1047. 删除字符串中的所有相邻重复项

232.用栈实现队列 题目链接:https://leetcode.cn/problems/implement-queue-using-stacks/description/ 文章讲解:https://programmercarl.com/0232.%E7%94%A8%E6%A0%88%E5%AE%9E%E7%8E%B0%E9%98%9F%E5%88%97.html 视频讲解:https://www.bilibili.com/video/BV1nY4y1w7VC/?sp…

[Meachines] Lame smbd3.0-RCE

信息收集 IP AddressOpening Ports10.10.10.3TCP:21,22,139,445,3632 $ nmap -p- 10.10.10.3 --min-rate 1000 -sC -sV 21/tcp open ftp vsftpd 2.3.4 |_ftp-anon: Anonymous FTP login allowed (FTP code 230) | ftp-syst: | STAT: | FTP server status: | …