C语言基础(10)之指针(2)

news2024/11/24 17:03:15

        在上一篇文章中我们谈到了指针,并给老铁们讲解了什么是指针、指针类型、野指针以及指针运算等知识。在这篇文章中小编将继续带大家了解指针的相关知识点。

1. 指针和数组

        指针和数组之间又能有什么联系呢?在谈这个之前,我们先来讲讲指针和数组之间的关系

                1.指针变量就是指针变量,不是数组,指针变量的大小是4/8个字节,专门用来存放地址的。
                2.数组就是数组,不是指针,数组是一块连续的空间,可以存放1个或者多个类型相同的数据。

        那既然指针变量是指针变量,数组是数组,二者之间能有什么联系呢?来看看下面的例子:

        从程序的运行结果中可以得知,数组名和数组首元素的地址是一样的。那么可以得出的结论是:数组名表示的是数组首元素的地址。(2种情况除外,数组部分讲解了)

        既然可以把数组名当成地址存放到一个指针中,那我们使用指针来访问数组就成为可能。再看看如下例子:

#include<stdio.h>

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int* p = arr;  // 指针用来存放数组首元素的地址
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("&arr[%d] = %p <====> p+%d = %p\n", i, &arr[i], i, p + i);
	}

	return 0;
}

        我们将数组首元素的地址存放在指针p中。从运行结果来看,p+i 其实计算的是数组 arr 下标为i的地址。那我们就可以直接通过指针来访问数组。

        因此指针与数组的联系便是:在数组中,数组名其实就是数组首元素的地址(2种情况除外)。当我们知道数组首元素的地址的时候,因为数组又是连续存放的,所以通过指针就可以遍历访问数组,数组是可以通过指针来访问的。

        个人理解:数组名 == 地址 == 指针。

2. 二级指针

        在次之前,我们谈到的指针都是一级指针,那二级指针是干什么的呢?

        对于一级指针来说,一级指针变量也是变量,是变量就会在内存中开辟空间,是变量就会有地址。因此二级指针变量就是用来存放一级指针变量的地址。

#include<stdio.h>

int main()
{
	int a = 10;
	int* p = &a; // p是一级指针
	
	int** pp = &p; // pp就是二级指针变量

	*(*pp) = 100;

	printf("%d\n", **pp); // 解引用2次
	printf("%d\n", a);

	 同理
	//int*** ppp = &pp;
	
	return 0;
}

        对于上述代码,p是一级指针变量,pp是二级指针变量,int** 其中的 int* 是在说明pp指向的是int*的类型变量,即p的完整类型,*是说明pp是指针变量。

        由于二级指针变量存储的是一级指针变量的地址,因此我们对二级指针pp解引用(即 *pp ),实则访问的是一级指针变量p;所以如果想通过指针pp来找到变量a,那就需要再次进行解引用(即 **pp )。

        换句话说,*pp 等价于 p,对指针 p 解引用找到a,即 *p。所以 *p 等价于 **pp。故 **pp = 100  等价于  *p = 100  等价于  a = 100。

        那么三级指针该怎么写呢?讲到这相信很多老铁都会举一反三了,那便是 int*** ppp = &pp;

3. 指针数组

        指针数组?那究竟是指针还是数组呢?我们可以联想到整型数组、字符数组指的都是数组,那指针数组自然也是指数组了!

        那指针数组是怎样的呢?下面举例来说明:

整型数组:int arr1[5];


字符数组:char arr2[6];


指针数组:int* arr3[5];

所以:

        整型数组  ——  存放整型的数组。
        字符数组  ——  存放字符的数组。
        指针数组  ——  存放指针(地址)的数组。

#include<stdio.h>

int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "hello xxx";
	char arr3[] = "cuihua";

	char* parr[] = { arr1,arr2,arr3 };

	printf("%c\n", *parr[1]); 

	printf("%c\n", *(parr[1] + 1)); 

	printf("%c\n", (*parr)[1]); 

	return 0;
}

        在上述代码中,数组parr便是指针数组了。在前面我们提到,数组名是数组首元素的地址,因此指针数组parr中存储的是三个字符数组首元素的地址,而不是存储的三个数组内的元素。

        那可能就有老铁好奇怎么通过指针数组来访问三个字符数组中的元素了,下面我将对上述代码中三个printf语句进行讲解,来帮助大家更进一步了解指针数组的的使用和解引用

                1. 对于 *parr[1],因为 [] 的优先级要比 * 高,所以parr先于 [] 结合,即取出数组parr中下标为1的元素,即是 数组arr2 首元素的地址,再对其进行解引用操作,所以取出的是 数组arr2 的首元素,为 h。

                2. 对于 *(parr[1] + 1),同样的 parr[1] 是 数组arr2 首元素的地址。我们可以假设 char* p = parr[1],则 p = arr2,所以 *(parr[1] + 1) 等价于 *(p + 1) 等价于 arr2[1]。故取出的是 数组arr2 中下标为1的元素,即是 e 。

                3. 对于 (*parr)[1],这里与第一点不同,因为有(),所以 parr 先于 * 结合。而指针数组也是数组,parr是数组名,故对数组名解引用 (*parr) 则是取出数组首元素,即是 数组arr1 首元素的地址。所以 *parr 等价于 arr1,即 (*parr)[1] 等价于 arr1[1],故结果为 b

        那指针数组有什么作用呢?如果我们想要打印数组arr1,arr2,arr3,此时指针数组parr就类似于二位数组,通过双层循环便可将三个数组的内容输出出来。

#include<stdio.h>

int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 5,6,7,8,9 };
	int arr3[] = { 0,2,4,6,8 };

	// 指针数组
	int* parr[] = { arr1,arr2,arr3 };

	// 类似于(模拟)的二维数组
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			//printf("%d ", *(parr[i] + j));
			printf("%d ", parr[i][j]);
		}
		printf("\n");
	}

	return 0;
}

4. const 修饰指针的理解

        在之前我们谈到const 修饰的变量 变成了常量,即该变量的值不可修改。那么当const修饰指针时,指针是否可修改呢?下面我将通过两个例子来为各位老铁们讲解。

        如上两图中,我们用const修饰指针p。此时const 放在 * 的左边,再通过指针修改其指向的内容时,编译器无法编译通过会报错;而修改指针变量指向的对象时,代码正常运行。也就是说,当const 放在 * 的左边时,限制的是指针指向的内容,不能通过指针变量来改变指针指向的内容,但指针变量的本身是可以改变的。

        如上两图中,同样用const修饰指针变量p,但此时 const 放在了 * 的右边,我们通过指针修改其指向的内容时,编译器却能正常运行了,而修改指针变量所指向的对象时,编译器却报错。也就是说,当 const 放在 * 右边时,限制的时指针变量本身,指针变量本身是不能改变的,但是指针指向的内容是可以通过指针来改变的。

总结:

        1. 当const 放在 * 的左边时,限制的是指针指向的内容,不能通过指针变量来改变指针指向的内容,但指针变量的本身是可以改变的。(限制的是 *p)

        2. 当 const 放在 * 右边时,限制的时指针变量本身,指针变量本身是不能改变的,但是指针指向的内容是可以通过指针来改变的。(限制的是 p)

        不知道各位老铁们看完这篇文章后,有没有对指针有更进一步的了解呢?那我们下篇文章见吧!

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

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

相关文章

深入分析——为什么未初始化的全局变量是零?

1、前言 #include <stdio.h>int temp;int main(void) {//打印temp的值是零printf("temp%d\n", temp);return 0; }在C语言编程中&#xff0c;我们默认未初始化的全局变量、静态局部变量的初始化值都是零&#xff0c;底层原理如下 未初始化的全局变量、静态局部…

揭秘AI写作工具:如何改变内容创作新格局

小伙伴们&#xff0c;今儿咱们来聊聊那些个让人眼前一亮、脑洞大开的AI写作神器——笔灵AI写作、宙语AI写作、博思白板AI写作&#xff0c;还有讯飞星火&#xff0c;它们啊&#xff0c;简直就是文案人儿的超级辅助&#xff0c;让咱们写东西的时候&#xff0c;灵感嗖嗖地往外冒&a…

TinyOS -数据发布实验

文章目录 简介分析 简介 分发协议主要用于维护网络共享配置的一致性&#xff0c;这里的共享配置可以是节点传感器采样的周期、节点LPL醒睡的周期或者节点运行程序的映像等。每个节点都会维护一份这样的配置&#xff0c;分发协议负责通知每个节点这些配置的改变&#xff0c;并通…

国产分布式数据库开启新篇章!详解安全可靠测评结果公告(2024年第2号)

重磅消息&#xff01;2024年的安全可靠测评结果&#xff08;数据库篇&#xff09;终于揭晓了&#xff01;&#x1f680; 9月30日&#xff0c;国庆前夕&#xff0c;中国信息安全测评中心和国家保密科技测评中心联合发布了今年第二号测评结果&#xff0c;这份名单有效期三年&…

大数据新视界 --大数据大厂之大数据驱动智能客服 -- 提升客户体验的核心动力

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

MATLAB与R语言在建模中的合作与应用(下篇)

目录 目录 模型训练的协同使用 1. 使用 R 语言进行统计建模 2. 使用 MATLAB 进行机器学习建模 模型评估与调优 1. 在 R 中评估模型性能 2. 在 MATLAB 中进行模型优化 实战示例&#xff1a;MATLAB 与 R 的协同建模 总结 在上篇文章中&#xff0c;我们介绍了 MATLAB 和 R…

打工人狂喜,轻松定时发圈

自动定时发圈软件有哪些优势&#xff1f; 1、不用下载安装软件 2、不需要绑定手机或电脑 3、不对电脑或手机做限制 4、不越狱不刷机 5、能更新迭代 6、有售后服务 7、使用安全登录&#xff0c;保障账号安全的 8、不用去做独立部署&#xff08;可以选择&#xff09; 9、…

class 032 位图

这篇文章是看了“左程云”老师在b站上的讲解之后写的, 自己感觉已经能理解了, 所以就将整个过程写下来了。 这个是“左程云”老师个人空间的b站的链接, 数据结构与算法讲的很好很好, 希望大家可以多多支持左程云老师, 真心推荐. 左程云的个人空间-左程云个人主页-哔哩哔哩视频…

重生之我们在ES顶端相遇第 20 章 - Mapping 参数设置大全(进阶)

文章目录 0. 前言1. 前置知识 - _source2. copy_to3. doc_values4. index5. enabled6. normalizer7. null_value8. 总结 0. 前言 在基础篇&#xff0c;我们只介绍了索引 Mapping 的基本用法。 本章将深入探讨日常中较经常使用的 Mapping 其他参数配置。 不想看过程&#xff0c…

Qt 实现动态时钟

1.实现效果 2.widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget>QT_BEGIN_NAMESPACE namespace

陪护小程序|护理陪护系统|陪护小程序成品

智能化&#xff0c;作为智慧医疗宏伟蓝图的基石&#xff0c;正引领着一场医疗服务的深刻变革。在这场变革的浪潮中&#xff0c;智慧医院小程序犹如璀璨新星&#xff0c;迅速崛起&#xff0c;而陪护小程序的诞生&#xff0c;更是如春风化雨&#xff0c;细腻地触及了老年病患、家…

Spring Cloud Netflix Ribbon 负载均衡详解和案例示范

1. 引言 在传统的集中式架构中&#xff0c;负载均衡器一般是放置在服务器端的&#xff0c;例如 Nginx等。随着微服务架构的兴起&#xff0c;服务实例的数量和部署地点变得更加动态和分布式&#xff0c;这使得在客户端进行负载均衡成为了一种可行且更灵活的方案。Netflix Ribbo…

【Linux】Linux命令与操作详解(二)权限管理、存储管理(磁盘分区、格式化、挂载)、进程管理、yum软件包安装

文章目录 一、前言二、权限管理2.1、用户权限2.2、权限说明2. 3、常用命令1、chmod2、chown3、chgrp 三、存储管理磁盘的分区、格式化与挂载1、新建一块硬盘2、在新建硬盘中进行分区3、格式化4、挂载 四、进程管理进程管理命令1、ps2、top3、nice 五、YUM软件包安装1、修改默认…

算法6:模拟运算

文章目录 z字形变幻外观数列数青蛙 题目均来自于力扣 z字形变幻 class Solution { public:string convert(string s, int numRows) {int n s.size();if(n < numRows || numRows 1) return s;int d 2 * numRows - 2;string res;for(int j 0; j < n; j d){res s[j]; …

Chromium 中<a> 标签href 属性实现分析c++

HTML 链接&#xff08;Anchor&#xff09;是网页之间跳转的核心部分。 HTML 使用链接与网络上的另一个文档相连。 HTML中的链接是一种用于在不同网页之间导航的元素。 链接通常用于将一个网页与另一个网页或资源&#xff08;如文档、图像、音频文件等&#xff09;相关联。 …

如何解决位置在表里的二维码的错行问题

众所周知&#xff0c;sap 里的二维码&#xff0c;在从其他形式转换成二维码之后&#xff0c;会经常有数据位置改变的情况&#xff0c;想让它老老实实待在原地很难&#xff0c; 这里要注意设置行间距&#xff0c;如果行间距不合适&#xff0c;就会导致错位

桥梁检测系统源码分享

桥梁检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vision 研究…

Vue入门-指令学习-v-on

v-on 作用&#xff1a;注册事件 添加监听 提供处理逻辑 语法&#xff1a; v-on:事件名"内联语句" v-on:事件名"methods中的函数名" 注意&#xff1a;" v-on&#xff1a;"可以替换为" " v-on:click"XXX" --> cli…

CPU中的寄存器是什么以及它的工作原理是什么?

在计算机科学中&#xff0c;寄存器是数字设备中的一个重要组成部分&#xff0c;它用于存储数据和指令以快速处理。寄存器充当临时存储区&#xff0c;信息可以在这里被快速访问和操作&#xff0c;以执行复杂任务。寄存器是计算机中最基础的存储类型&#xff0c;它们在帮助机器高…

如何写出Pythonic的代码?

f-string、三元操作、各种解析式、生成器装饰器的熟练运用&#xff0c;“内库”引用和函数封装再加持PEP8&#xff0c;撰写的脚本不pythonic都难。&#x1f60e; (笔记模板由python脚本于2024年10月07日 18:03:27创建&#xff0c;本篇笔记适合特别喜欢python的coder翻阅) 【学习…