【C语言】初阶指针(2)

news2024/11/13 12:01:37

目录

前言

1. 指针访问数组

1.1 数组名的含义

1.2 使用指针访问数组

2. 一维数组传参的本质

3. 二级指针

4. 指针数组

4.1 指针数组模拟二维数组

结语


前言

在本篇文章中,我们将要一起来探讨指针与数组之间的关系,以及如何理解指针数组及其运用

1. 指针访问数组

1.1 数组名的含义

讲清楚数组名之前,我们先来看一段代码

#include <stdio.h>
int main()
{
	int arr[10] = { 0 };
	int* p = &arr[0];
	printf("%p\n", p);
	return 0;
}

在代码中,我们创建了一个数组,还创建了一个指针 p 来接收数组第一个元素的地址。实际上,数组名就是数组首元素的地址,我们还可以用数组名来重新写一遍上面的代码

#include <stdio.h>
int main()
{
	int arr[10] = { 0 };
	int* p1 = &arr[0];
	int* p2 = arr;
	printf("%p\n", p1);
	printf("%p\n", p2);
	return 0;
}

运行结果如下:

我们发现数组名和首元素地址打印出来的结果一模一样的,那么我们可以认为:

数组名就是数组首元素的地址,也就是说 arr == &arr[0]

但如果这样的话,会有一个“bug”,当我们用 sizeof 来操作数组名时,结果跟我们的预期并不相符

#include <stdio.h>
int main()
{
	int arr[10] = { 0 };
	printf("%zd\n", sizeof(arr));
	printf("%zd\n", sizeof(&arr[0]));
	return 0;
}

运行结果如下:

               

                                                                                    (在X84环境下) 

不是说好了 arr == &arr[0] 吗,那为什么结果会不一样呢

其实,“数组名就是数组首元素的地址”这句话没错,但是有两个例外:

  • sizeof(数组名):当 sizeof 中单独放了数组名,这里的数组名表示的是整个数组,计算出来就是整个数组的大小,单位是字节
  • &数组名:取地址 & 后面加数组名,这里的数组名表示的也是整个数组,因此取出的就是整个数组的地址
  • 除了以上两种情况外,数组名表示的都是数组首元素的地址

所以才会出现上面 sizeof(arr)== 40,而 sizeof(&arr[0])== 4 的情况

明白了这个点之后,我们再来看一段代码:

#include <stdio.h>
int main()
{
	int arr[10] = { 0 };
	printf("arr     = %p\n", arr);
	printf("&arr    = %p\n", &arr);
	printf("&arr[0] = %p\n", &arr[0]);
	return 0;
}

 运行结果如下:

三个结果一模一样的,这是为什么呢?别急,我们这就来揭晓:

//我们把上面的代码抄下来,再各自加上 1
#include <stdio.h>
int main()
{
	int arr[10] = { 0 };
	printf("arr       = %p\n", arr);
	printf("arr+1     = %p\n", arr + 1);
	printf("&arr      = %p\n", &arr);
	printf("&arr+1    = %p\n", &arr + 1);
	printf("&arr[0]   = %p\n", &arr[0]);
	printf("&arr[0]+1 = %p\n", &arr[0] + 1);
	return 0;
}

运行结果如下:

我们可以发现:

  • arr 和 arr + 1 相差了 4 个字节
  • &arr[0] 和 &arr[0] + 1 相差了 4 个字节
  • &arr 和 &arr + 1 相差了 40 个字节

因为 &arr 取出的是数组的地址,当实现 + 1 操作时,是会跳过整个数组的,因此会 + 40 个字节

1.2 使用指针访问数组

在理解了数组名的内涵后,我们就可以很方便地使用指针访问数组了

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

从上面的代码中我们可以看出:数组名 arr 和指针 p 是等价的,我们平时一般使用的是 arr[i] 来访问数组的元素,那 p[i] 是不是也可以访问数组呢?答案是肯定的

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

同理可得:arr[i] 也等价于 *(arr + i)。实际上,数组元素的访问的本质就是转换成首元素的地址加上偏移量来求出元素的地址,然后再解引用来访问具体元素

2. 一维数组传参的本质

我们在使用函数时,偶尔会有参数是数组的情况,下面我们来看一下具体例子

#include <stdio.h>

void test1(int arr[])
{
	printf("%d\n", sizeof(arr));
}

void test2(int* arr)
{
	printf("%d\n", sizeof(arr));
}

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	test1(arr);
	test2(arr);
	return 0;
}

总结:函数的形参可以写成数组的形式,也可以写成指针的形式

3. 二级指针

指针变量实质上也是一种变量,是变量就有地址。那么二级指针就是指针变量的指针(指针套娃)

 对于二级指针:

  • *pp 通过对 pp 中的地址进行解引用,这样访问的就是 p 的地址
  • **pp 就是先通过 *pp 找到 p 的地址,再对 p 进行解引用操作,最后找到的就是 a

4. 指针数组

指针数组,顾名思义就是存放指针的数组

既然指针数组的每个元素是地址,那么又可以指向其他变量的地址

4.1 指针数组模拟二维数组

例子:按照二维数组的形式打印

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

运行结果如下:

 

原理:

parr[i] 是访问 parr 数组的元素,这时候找到的是一维数组,parr[i][j] 访问的就是一维数组中的元素

该代码可以模拟出二维数组的效果,但和真正的二维数组完全不是一回事。因为它的每一个元素的空间并不是连续的,对于真正的二维数组中元素的空间是连续的。

结语

今天我们一起学习了指针与数组之间的关系,包括二级指针等;如有总结不到位的地方还请多多谅解,若有出现纰漏,希望大佬们看到错误之后能够在私信或评论区指正,博主会及时改正,共同进步!
欢迎各位在评论区友好讨论。如果觉得不错的话,麻烦您点个赞吧,十分感谢!

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

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

相关文章

Vue.js 中子组件向父组件传值的方法

Vue.js 是一款流行的 JavaScript 前端框架&#xff0c;它提供了一套完整的工具和 API&#xff0c;使得开发者可以更加高效地构建交互式的 Web 应用程序。其中&#xff0c;组件化是 Vue.js 的一个核心概念&#xff0c;通过组件化可以将一个复杂的应用程序拆分成多个独立的部分&a…

通过与chatGPT交流实现零样本事件抽取

1、写作动机&#xff1a; 近来的大规模语言模型&#xff08;例如Chat GPT&#xff09;在零样本设置下取得了很好的表现&#xff0c;这启发作者探索基于提示的方法来解决零样本IE任务。 2、主要贡献&#xff1a; 提出了基于chatgpt的多阶段的信息抽取方法&#xff1a;在第一阶…

VSCode 插件集

文章目录 翻译(英汉词典)Auto Close TagAuto Rename TagBetter CommentsBracket Pair Colorization TogglerChinese (Simplified) (简体中文)colorizeHighlight Matching TagImage previewJAR ViewerLive ServerMarkdown Preview EnhancedMaterial Icon ThemeMaterial Themeope…

uniapp 使用canvas 画海报,有手粘贴即可用(拆成组件了,看后面)

1.直接使用 html部分 <view click"doposter">下载海报</view> <canvas canvas-id"myCanvas" type2d style"width: 370px; height: 550px;opcity:0;position: fixed;z-index:-1;" id"myCanvas" />js 部分 drawBac…

【Vue】2-11、组件的生命周期

一、生命周期 & 声明周期函数 生命周期&#xff08;Life Cycle&#xff09;是值一个组件从 创建 -> 运行 -> 销毁 的整个阶段&#xff0c;强调的是一个时间段。 生命周期函数是由 Vue 框架提供的内置函数&#xff0c;会伴随着组件的生命周期&#xff0c;自动按次序…

【数据分享】1929-2023年全球站点的逐日最高气温数据(Shp\Excel\免费获取)

气象数据是在各项研究中都经常使用的数据&#xff0c;气象指标包括气温、风速、降水、湿度等指标&#xff0c;其中又以气温指标最为常用&#xff01;说到气温数据&#xff0c;最详细的气温数据是具体到气象监测站点的气温数据&#xff01; 之前我们分享过1929-2023年全球气象站…

机器学习数学基础

机器学习基础 1、标量、向量、矩阵、张量2、概率函数、概率分布、概率密度、分布函数3、向量的线性相关性4、最大似然估计5、正态分布(高斯分布)6、向量的外积(叉积)7、向量的内积(点积)8、超平面(H)1、标量、向量、矩阵、张量 标量、向量、矩阵和张量是线性代数中不同…

周期承压下的徐工机械:收入持续负增长,大肆并购风险犹存

撰稿|行星 来源|贝多财经 工程机械行业的发展程度是衡量工业化水平的关键指标&#xff0c;亦是一直以来备受国家与市场关注的高成长板块。 在探索新发展增量的大军中&#xff0c;徐工机械&#xff08;SZ:000425&#xff09;活跃工程机械市场&#xff0c;寻求着利润与品质的最…

springboot140体育馆使用预约平台的设计与实现

简介 【毕设源码推荐 javaweb 项目】基于springbootvue 的 适用于计算机类毕业设计&#xff0c;课程设计参考与学习用途。仅供学习参考&#xff0c; 不得用于商业或者非法用途&#xff0c;否则&#xff0c;一切后果请用户自负。 看运行截图看 第五章 第四章 获取资料方式 **项…

草图导入3d之后渲染模型发光怎么回事?---模大狮模型网

在草图大师中&#xff0c;当导入3D模型之后发现模型发光通常是由于模型的材质属性或灯光设置所导致的。以下是一些可能的原因和解决方法&#xff1a; 材质属性设置&#xff1a;某些3D模型文件可能包含了发光材质属性&#xff0c;导致模型在草图大师中显示为发光状态。您可以尝试…

第四次项目(配置dns服务的正反向解析)

目录 实验要求 实验步骤 一、基础配置 1.1、服务端配置静态IP 1.2、客户端配置静态IP 二、配置dns服务的正向解析 2.1、服务端编辑主配置文件named.conf 2.2、服务端编辑主配置文件named.rfc1912.zones 2.3&#xff0c;服务端编辑数据配置文件&#xff0c;使用拷贝…

opencvb 十七 使用cmake配置opencv c++项目

1、cmake简介 1.1 cmake是什么 CMake是一个开源、跨平台的编译&#xff08;Build&#xff09;工具&#xff0c;是用来构建、测试和打包软件的。它能够用简单的语句来描述所有平台的编译过程。它能够输出各种各样的makefile或者project文件&#xff0c;能测试编译器所支持的C特…

【ASP.NET Core 基础知识】--Web API--创建和配置Web API(一)

一、简介 Web API&#xff08;Web Application Programming Interface&#xff09;的重要性在于其在现代软件开发中扮演着关键的角色。以下是一些关于Web API重要性的方面&#xff1a; 跨平台交互&#xff1a; Web API允许不同平台、不同技术栈的应用程序进行通信。无论是Web…

C#网络爬虫之TianyaCrawler实战经验分享

互联网时代的到来带来了大量的数据&#xff0c;而网络爬虫技术成为了获取这些数据的重要途径之一。如果你是一名C#开发者&#xff0c;那么你可能会对TianyaCrawler这个强大的网络爬虫框架感兴趣。本文将带你深入了解TianyaCrawler&#xff0c;分享它的技术概况、使用场景&#…

为什么光纤目前取代不了网线?

早上好&#xff0c;我的网工朋友。 在布线行业中&#xff0c;光纤与铜缆之间的较量已持续了十多年。 现如今随着云计算、5G等新型业务的不断涌现&#xff0c;数据中心规模不断的扩大&#xff0c;其架构与布线也越来越复杂。 但光纤的轻量化及逐渐降低的成本&#xff0c;使得…

Ubuntu Linux 下安装和卸载cmake 3.28.2版本

一、安装cmake 1.首先&#xff0c;先从cmake官网下载cmake-3.28.2-linux-x86_64.tar.gz 2.用FinalShell 等文件上传工具&#xff0c;将这个压缩包上传到 虚拟机的某个路径去&#xff08;自选&#xff09; 3. cd /usr/local/bin/&#xff0c;然后创建cmake文件夹&#xff0c;…

2024.2.1每日一题

LeetCode 今天看到一个评论挺有意思的&#xff0c;非常符合我现在的状况 简单题 – 稍加思索&#xff0c;嘴角上扬 中等题 – 认真对待&#xff0c;眉头一皱 困难题 – 绞尽脑汁&#xff0c;Ctrl cv 数字游戏 LCP 24. 数字游戏 - 力扣&#xff08;LeetCode&#xff09; 题目…

递归再认识----【详解】内含迷宫和八皇后问题

目录 一.递归&#xff1a; 1.1什么是递归&#xff1f; 1.2 递归示例&#xff1a; ①.打印问题&#xff1a; ②.阶乘问题&#xff1a; 1.3.递归需要遵守的规则&#xff1a; 二.迷宫问题&#xff1a; 说明&#xff1a; 代码详解&#xff1a; 三.八皇后问题&#xff1a; …

秋招面试—JS篇

2024 JavaScript面试题 1.new 操作符的工作原理 ①.创建一个新的空对象 ②.将这个对象的原型设置为函数的 prototype 对象 ③.让函数的this指向该对象&#xff0c;为函数添加属性和方法 ④.最后返回这个对象 2.什么是DOM&#xff0c;什么是BOM? DOM&#xff1a;文档对象…

回归预测 | Matlab实现CPO-GRU【24年新算法】冠豪猪优化门控循环单元多变量回归预测

回归预测 | Matlab实现CPO-GRU【24年新算法】冠豪猪优化门控循环单元多变量回归预测 目录 回归预测 | Matlab实现CPO-GRU【24年新算法】冠豪猪优化门控循环单元多变量回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现CPO-GRU【24年新算法】冠豪猪优化…