打响指针的第一枪:指针家族

news2024/11/26 7:40:58

前言

        指针其实是我们学习C语言中最难的知识点,很多人在学习指针的时候会被绕晕,包括博主也是,当初百思不得其解,脑袋都要冒烟了,本来打算在学习指针的时候就写一篇博客,但是当初自己的能力还是没有办法去完成这个壮举,但今时不同往日,如今也算是一名精通C语言的学生了,所以前来编写一篇关于指针的博客。

        本篇博客会让你对指针和数组的了解更深一步,你会发现其实数组和指针并没有什么区别,你也会知道数组指针其实存的就是数组的地址,而数组的地址是比里面元素的地址还要高一级的指针,这里我只会讲解一级数组指针,毕竟指针是可以无限套娃的,讲一个就理解多个了!

        如有表达不清晰或错误,请大家在评论区帮我指正,让我们的学习可以更加完善,而博主也会不断的来更新和修改!

学习目标

  • 首先要搞懂什么是取地址( & ),什么是解引用( * ),以及指针的加法
  • 学习一级指针,二级指针
  • 搞懂 数组指针和指针数组一维、二维数组名、二维数组的行、&数组名

一、理解取地址&、解引用*和指针的加法

        取地址很好理解,就是对一个变量取出它的地址;然后我们要用指针类型来接收这个地址,所以既然指针可以接收地址,那就说明指针就是地址!

        而指针最重要的其实就是解引用和指针的加法,这篇博客会让你理解什么是解引用和指针的加法:深入理解:指针变量的解引用 与 加法运算-CSDN博客

二、快速学习一级指针和二级指针

1. 一级指针

一级指针:其实存的就是非指针变量的地址,可以是各种非指针类型的地址

        而一级指针也是一个变量,变量一定占空间,有空间就要有地址,所以一级指针也是有地址的,千万不能认为一级指针没有地址!!!

    char c = '2';
    char *pc = &c;            //存char变量的地址
    
    short s = 1;
    short *ps = &s;           //存short变量的地址
    
    int i = 3;
    int *pi = &i;             //存int变量的地址
    
    double d = 4.5;
    double *pd = &d;          //存double变量的地址
    
    float f = 5.6f;
    float *pf = &f;           //存float变量的地址
    
//无符号等基本数据类型

    struct List l;
    struct List *plist = &l;  //存结构体stuct 变量的地址
    
    union All all; 
    union All *pall = &all;     //存联合体union变量的地址

enum、位段等自定义类型

这里面没有涉及对数组的取地址,因为比较特殊,会放在这里讲:🔗

2. 二级指针

二级指针:对一级指针取地址,可以是各种指针类型的地址

所以二级指针就是存放一级指针的地址的指针变量,那同理二级指针也是有地址的,这样就可以实现无限套娃,三级指针、四级指针、n级指针;

    char c;
    char *pc = &c;            
    char **ppc = &pc;

    short s;
    short *ps = &s;          
    short **pps = &ps;
    
    int i;
    int *pi = &i;             
    int **ppi = π
    
    double d;
    double *pd = &d;          
    double **ppd = &pd;
    
    float f;
    float *pf = &f;           
    float **ppf = &pf;
    
//无符号等基本数据类型

    struct List l;
    struct List *plist = &l; 
    struct List **pplist = &plist;
    
    union All all;
    union All *pall = &all;   
    union All **ppall = &pall;
    
//enum、位段等自定义类型    

三、指针数组

1. 指针数组的介绍

        我们先来学习指针数组的原因就是比数组指针好理解,并且数组名和二维数组的行都是和数组指针有关系的。

        那什么是指针数组呢?

        指针数组,顾名思义:是一个数组,数组元素都是指针类型的,说白了,指针数组就是存放地址的数组。

int arr[5] = {1,2,3,4,5};
int *arr[5] = {arr, arr + 1, arr + 2, arr + 3, arr + 4};

        既然有二级指针、三级指针、四级指针等等,就一定会有一级指针数组、二级指针数组和三级指针数组等等,后面的数组指针也是一个道理,所以我们在这里就仅仅讲解一级指针数组;

int **arr[5];    //二级整型指针数组
char ***ch[5];   //三级字符型指针数组

2. 指针数组的计算

        我们另外一篇文章知道了解引用是根据指针的数据类型(除*之外)来访问字节的;所以直接看下面的例题:

温馨提示:第三个printf语句需要了解大小端字节序才可以解决问题

#include <stdio.h>
int main()
{
    int arr[5] = {1,40000,3,4,5};
    int *parr[5] = {arr, arr + 1, arr + 2, arr + 3, arr + 4};
    printf("%d\n", **parr);
    printf("%d\n", **(parr + 1));
    printf("%d\n", *parr[1]);
    printf("%d\n", *(char*)parr[1]);
    return 0;
}

第一个printf

首先 **parr ,先看parr 这是一个数组名,是首元素的地址,也就是arr的地址,那parr的数据类型就是int*,*parr解引用是根据 int* 来的,也就是拿出一个指针类型大小的字节(指针类型在32位机器下是4字节,在64位机器下是8字节),取出了arr,那**parr 本质上就是*arr,arr是首元素地址,类型是int*,那*arr解引用就是根据int来的,拿出了一个int类型的大小,4字节,所以**parr = 1;

图解如下:

第二个printf 

同理,这里就是用到了指针+整数,parr的数据类型是int **,那parr + 1,是根据int *来加 的,也就是往后移动一个指针类型的大小,后面的过程都跟第一个相同

图解如下:

第三个printf

就是典型的用下标访问数组元素,但是在这里你就会发现 *(parr + 1) 和 parr[1]是等效的,那我们就可以使用指针的方式和数组下标一起来访问数组元素,因为这是等价的;

图解如下:

第四个printf

这里就涉及到一个强制类型转换,也就会导致我们解引用的时候取出来的字节数是改变的;

具体结果和大小端有关

大端字节序:低地址存放高数字位

小端字节序:低地址存放低数字位

        这里我们能快速地找到parr[1]是arr + 1 这个地址,然后被强制转换为char*类型,这也就表明了解引用的时候,只能取出char类型的字节,1字节。然而这里涉及一个大小端的问题,解引用的时候是从低地址开始解引用,一个字节一个字节取,所以经过char*强转取出来了只有地址最低的一个字节,也就是40;转换为十进制就是64;这是基于小端字节序的结果:

图解如下:

大端字节序的结果为:9c = 156

四、数组名和指针

        终于到了我们的数组名和指针这里了,这里会将数组名和数组指针一起对比着来讲解,大家最好要知道啥是数组指针就行,数组指针就是一个指向整个数组的指针。知道这些我们就开始学习吧!

1. 数组名

我们都知道 数组名表示的是数组首元素的地址,但是有两个特例表示的是整个数组的地址

表示整个数组的地址

  • &数组名
  • sizeof(数组名)

这里想讲解一下 &arr仅仅是一维数组的数组名

int arr[5] = {1,2,3,4,5};

        首先我们知道&arr是整个数组的地址,也就是其数据类型必须是这样: int (*) [5];这也就证实了其实&数组名的本质就是一个数组指针

        那怎样来理解这个类型呢?

        首先我们要让编译器 &arr 知道是整个数组的地址,那就必须让编译器知道有几个数组元素,所以我们会加上[ ],这里大家简单理解一下就行。最后我们只需要知道,&arr表示的是整个数组的地址就行。

接下来看一下下面的题:

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

运行的结果是什么呢?整个数组的地址是啥样的呢?

        我们惊喜地发现,整个数组的地址居然和数组首元素的地址是一样的,那是真的一样吗?继续看下面的代码:

#include <stdio.h>
int main()
{
    int arr[5] = {1,2,3,4,5};
    printf("%p\n", arr);
    printf("%p\n", arr + 1);

    printf("%p\n", &arr);    
    printf("%p\n", &arr + 1);
    return 0;
}

        我们会发现&arr + 1,跳过了20个字节,也就是5个元素的大小

        所以虽然整个数组的地址和数组首元素的地址是一样的,但是加一之后移动的字节是不同的,本质上是因为数据类型的不同导致的

        arr的数据类型:int * ,&arr的数据类型是 int(*)[5]

2. 二维数组名

二维数组名,同样也适用于对数组名的规则;

先说结论:二维数组的数组名 == 二维数组第一行的地址

下面代表的运行结果是什么呢?

#include <stdio.h>
int main()
{
    int arr[2][2] = {{1,2},{3,4}};
    printf("arr: %p\n", arr);
    printf("arr + 1: %p\n", arr + 1);
    return 0;
}

        结果是移动了8个字节,也就是两个int类型的大小啊,两个int类型的大小不就是第一行吗?所以通过这个现象可以知道,二维数组的首元素是整个第一行,所以二维数组的数组名就是整个第一行的地址啊!

3. 二维数组的行

二维数组的行:是表示该行这个一维数组的数组名,是该行首元素的地址

        讲解二维数组的行之前

        大家先想一下一维数组的每个元素是什么?

        通过一维数组能不能推出二维数组的每个元素是什么呢?

int arr[2][2] = {{1,2},{3,4}};

        不难想出,二维数组的每个元素其实就是每一行的一维数组,因为上面也隐含了二维数组的数组名是第一行的地址,而数组名又是首元素的地址,那就侧面印证了二维数组其实就是一维数组的数组。但是这跟我们的行有什么关系呢?接下来就是要学习的知识了

        大家先理清一下思路,二维数组的行是什么?二维数组的行就是第一个方括号[ ],而我们要访问一个一维数组元素的时候,是这样访问的:

int a[5] = {1,2,3,4,5};
a[1] = 8;

访问二维数组的第一行的元素,是这样访问的:

int arr[2][2] = {{1,2},{3,4}};
arr[0][1] = 8;

它们之间的共同之处: 都要用数组名+下标引用

一维数组:arr + [1]

二维数组:arr[0] + [1]

所以我们会发现二维数组的行,其实就相当于一维数组的数组名!既然二维数组的行相当于一维数组的数组名了,那就是首元素的地址,arr[0] == &arr[0][0]!

我们学完这些,根本上来说二维数组就可以相当于一级数组指针的数组了!

数组和指针拓展知识

  • a[ i ] = *( a + i )
  • b[ x ][ y ] = *( b[ x ] + y ) = *( *( b + x ) + y )

五、数组指针

终于来到数组指针了!!!!

数组指针,顾名思义:是一种指向数组的指针

我们只讨论一级数组指针,多级数组指针大家有兴趣可以私信我

        我们来思考这样一个问题,既然一个指针是可以指向整个数组的,并且指针是存放地址的变量,那数组指针是如何做到指向整个数组的呢?

        其实不难理解,我只要存放整个数组的地址就可以了呀,那如何存放数组的地址呢?别忘了&数组名代表的就是整个数组的地址哦!而&数组名中的数组名是首元素地址,但是对首元素地址取地址,那不就相当于一个二级指针了吗?既然这样,数组指针就相当于一个二级指针,那二维数组名,实际上也相当于一个二级指针,这也是为什么数组指针和二维数组名拿元素要解引用两次的原因

        但是解引用要涉及到数据类型,那数组的数据类型又是什么呢?

int arr[5] = {1,2,3,4,5};
int (*parr) [5] = &arr;

        提到这里就不得不拓展一个知识点,int arr[5]是一个数组,那这个数组的类型是什么呢?大多数人都没有去研究过吧,我们不妨可以通过以往的经验来看

        比如 int a 的 a是一个变量名,a 的类型是 int ,double d 的 d是一个变量名,类型是double;那int arr[ 5 ]的变量名是什么呢?没有变量名,一定有数组名!所以数组名是arr,那数据类型是int [5] ;这表示这个变量arr是一个数组类型,是一个有5个int类型的数组。

        因为解引用和加法是涉及到类型的问题,所以我们必须要明白数组指针的数据类型是什么,虽然我上面说了数组指针是相当于二级指针的,但是仅仅是为了让我们来理解 解引用2次的原因。

        那到底数组指针的数据类型到底是什么呢?

        首先依旧是拿出数组名parr,剩下的就是数据类型:int (*) [5],这个的意思就是为一个数组的指针类型,但是这里还有数组元素的个数,只有知道元素个数,解引用的时候才知道拿出来多少字节,加的时候才知道移动多少字节。

        *表示这是一个指针,int 表示 元素类型,而[5]表示有多少个元素;

对于加法:数组指针移动的是整个数组的大小;

对于解引用:作者目前没有搞懂深层,但是有一种方法简单易懂:

        因为parr 是 &arr,那 *parr 就是 *&arr, * 和 & 相互抵消了,就是arr,这样我们也就是可以理解为啥是指针降级了。

        所以*parr == arr,那对*parr的解引用或者是加法,就是对arr来的。

六、总结

        其实我们对指针和数组这里的考点基本都是在解引用和指针➕整数这里出题,因为对于学C的大家,这里算是难题了,它往往可以伴随着强制类型转换,隐式类型转换和大小端字节序等多方面出题,但是万变不离其宗,你只要弄清是啥数据类型就OK,仔细画图就一目了然了。

        最后给大家推荐一下我的C语言刷选择题的专栏,这里是我在牛客网上精选出来的题,里面有我的个人解析,如有错误,请大家指正,有不懂的不会的可以私信哦!

https://blog.csdn.net/2302_76941579/category_12492707.html?spm=1001.2014.3001.5482

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

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

相关文章

智能优化算法应用:基于人工电场算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于人工电场算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于人工电场算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.人工电场算法4.实验参数设定5.算法结果6.…

Linux发行版比较:Ubuntu、CentOS、Red Hat与其他系统的优劣分析

导言 Linux作为开源操作系统&#xff0c;有众多不同的发行版&#xff0c;每个发行版都有其独特的特性和适用场景。本文将聚焦于比较Ubuntu、CentOS、Red Hat和其他系统&#xff0c;深入分析它们的优势、用途以及在不同领域的应用。Linux操作系统的生态系统中&#xff0c;Ubuntu…

【Hive】——DDL(TABLE)

1 查询指定表的元数据信息 如果指定了EXTENDED关键字&#xff0c;则它将以Thrift序列化形式显示表的所有元数据。 如果指定了FORMATTED关键字&#xff0c;则它将以表格格式显示元数据。 describe formatted student&#xff1b;2 删除表 如果已配置垃圾桶且未指定PURGE&…

用23种设计模式打造一个cocos creator的游戏框架----(二十)解析器模式

1、模式标准 模式名称&#xff1a;解析器模式 模式分类&#xff1a;行为型 模式意图&#xff1a;给定一个语言&#xff0c;定义它的文法的一种表示&#xff0c;并定义一个解释器&#xff0c;这个解释器使用该表示来解释语言中的句子。 结构图&#xff1a; 适用于&#xff1…

【论文阅读笔记】A Recent Survey of Vision Transformers for Medical Image Segmentation

Khan A, Rauf Z, Khan A R, et al. A Recent Survey of Vision Transformers for Medical Image Segmentation[J]. arXiv preprint arXiv:2312.00634, 2023. 【论文概述】 本文是关于医学图像分割中视觉变换器&#xff08;Vision Transformers&#xff0c;ViTs&#xff09;的…

Apache CouchDB 垂直权限绕过漏洞 CVE-2017-12635 已亲自复现

Apache CouchDB 垂直权限绕过漏洞 CVE-2017-12635 已亲自复现 漏洞名称影响版本影响版本 漏洞复现环境搭建漏洞利用 漏洞名称 影响版本 Apache CouchDB是一个开源的NoSQL数据库&#xff0c;专注于易用性和成为“完全拥抱web的数据库”。它是一个使用JSON作为数据存储格式&…

城市智能图书柜需求说明书

1. 简介 1.1 项目概括 本项目主要实现智能图书柜对图书的借出、还回、续借、查询、上下架、盘点的功能&#xff0c;对于读者&#xff0c;可以进行读者证的办理&#xff0c;读者信息的录入和完善。 1.2 项目背景 ​ 目前大量读者距离图书馆较远&#xff0c;无法方便、快捷地…

董宇辉“小作文事件”:东方甄选的危机与挑战

导言 近期&#xff0c;东方甄选公司的创始人董宇辉因涉及“小作文事件”而引起轩然大波。东方甄选作为一家在招聘领域崭露头角的公司&#xff0c;经历了充满曲折的发展历程。本文将深入探讨这一事件对东方甄选公司的发展带来的危机和挑战&#xff0c;以及公司可能采取的解决策略…

阿里云主导《Serverless 计算安全指南》国际标准正式立项!

日前&#xff0c;在韩国召开的国际电信联盟电信标准分局 ITU-T SG17 全会上&#xff0c;由阿里云主导的《Serverless 计算安全指南》国际标准正式立项成功。 图 1 项目信息 在现今数字化时代&#xff0c;Serverless 计算正逐渐成为云计算的一个新的发展方向&#xff0c;其灵活…

智能优化算法应用:基于蛾群算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于蛾群算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于蛾群算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.蛾群算法4.实验参数设定5.算法结果6.参考文献7.MA…

docker在线安装nginx

1、查看所有镜像 1、不带容器卷常规启动nginx&#xff0c;命令如下 docker run --name nginx-test -p 8089:80 -d a6bd71f48f68 2、在宿主机创建/usr/local/data/nginxdocker/目录&#xff0c;在此目录下创建html和logs文件夹&#xff0c;然后将容器内的 nginx.conf 和 html 下…

React基础巩固日志1

书写了一篇vue3的基础构建之后&#xff0c;不能带着各位一起学习vue3了&#xff0c;因为我要面试上海的前端岗位了&#xff0c;所以从现在开始&#xff0c;我要带着大家一起学习React了。 以下是我使用react书写的要掌握的react的知识点&#xff1a; ** ** 那么下面我们就一一通…

美颜SDK技术对比,深入了解视频美颜SDK的工作机制

如何在实时视频中呈现更加自然、美丽的画面&#xff0c;而这正是美颜SDK技术发挥作用的领域之一。本文将对几种主流视频美颜SDK进行深入比较&#xff0c;以揭示它们的工作机制及各自的优劣之处。 随着科技的不断进步&#xff0c;美颜技术已经从简单的图片处理发展到了视频领域…

【Filament】绘制矩形

1 前言 Filament环境搭建中介绍了 Filament 的 Windows 和 Android 环境搭&#xff0c;绘制三角形中介绍了绘制纯色和彩色三角形&#xff0c;本文将使用 Filament 绘制纯色和彩色矩形。 2 绘制矩形 本文项目结构如下&#xff0c;完整代码资源 → Filament绘制矩形。 2.1 自定义…

【Matlab算法】灰狼优化算法问题(Grey Wolf Optimization)(附MATLAB完整代码)

灰狼优化算法问题 前言算法描述&#xff1a;算法特点&#xff1a; 正文代码实现 前言 灰狼优化算法&#xff08;Grey Wolf Optimization&#xff0c;GWO&#xff09; 是一种模拟灰狼社会行为的启发式优化算法。它是由Seyedali Mirjalili等人于2014年提出的&#xff0c;灵感来源…

Acrel-1000DP分布式光伏系统在某重工企业18MW分布式光伏中应用——安科瑞 顾烊宇

摘 要&#xff1a;分布式光伏发电特指在用户场地附近建设&#xff0c;运行方式以用户侧自发自用、余电上网&#xff0c;且在配电系统平衡调节为特征的光伏发电设施&#xff0c;是一种新型的、具有广阔发展前景的发电和能源综合利用方式&#xff0c;它倡导就近发电&#xff0c;就…

React实现全局Loading

css #__loading {position:fixed;top: 0;left: 0;z-index: 99999;display: flex;align-items: center;justify-content: center;width: 100%;height: 100%;background: rgba(0, 0, 0, 0); } 页面代码 使用了antd的Spin组件 import React from react import ReactDOM from re…

C语言入门(字符串正反连接(fgets()、gets()用法))

目录 ​编辑 题目描述 输入格式 输出格式 样例输入 样例输出 解题思路 题目描述 所给字符串正序和反序连接&#xff0c;形成新串并输出 输入格式 任意字符串&#xff08;长度<50&#xff09; 输出格式 字符串正序和反序连接所成的新字符串 样例输入 123abc 样例输出 12…

2023.12.18杂记

今天特地搜了一下国内不错的博客网站&#xff0c;本来想在掘金上写的&#xff0c;但是怕被人喷&#xff08;&#xff0c;所以还是决定在csdn上写了哈哈哈。 这篇文章主要整理一下我今天写代码时遇到的疑惑以及记录一下思考过程吧。 第一个注意的地方&#xff0c;我们的get查询…

前端开发中的webpack打包工具

前端技术发展迅猛&#xff0c;各种可以提高开发效率的新思想和框架层出不穷&#xff0c;但是它们都有一个共同点&#xff0c;即源代码无法直接运行&#xff0c;必须通过转换后才可以正常运行。webpack是目前主流的打包模块化JavaScript的工具之一。 本章主要涉及的知识点有&am…