第11天理解指针

目录

数组指针

指针数组

字符指针数组

二维字符数组

const 修饰变量为常量

指针常量

常量指针

typedef 重命名数据类型

关于typedef的一般理解

具体示例分析

复杂示例分析

总结回顾--指针的奥秘:深入理解内存地址与数据存储

一、指针的本质

二、地址类型转换的意义


数组指针

整型指针        指针指向整型空间

int *p;

数组指针        指针指向数组空间

int (*p)[3]; //本质上是指针


下面是几个例子需要理解 

int a[5]; // a==&a[0] 数组名a是首元素a[0]的地址

int *p=a;// p=a

int *p=&a[0];//int *p = a;int *p = &a[0];这两种写法是等价的,都是让指针 p 指向数组 a 的首元素。此时,a[0]p[0]是等价的,都表示访问数组的第一个元素。

*(a+1)== *(p+1)//*(a + 1)*(p + 1)也是等价的,都表示访问数组的第二个元素。因为 a 在表达式中会被转换为指针,a + 1指向数组的第二个元素,解引用后得到第二个元素的值;同理,p + 1也指向数组的第二个元素,解引用后得到相同的值。

p=a+1; // p=&a[1]此时 p 指向数组 a 的第二个元素


e.g.

int a[5];//定义了一个指向包含 5 个整数的数组

int (*p)[5]=&a; // p=&a,即p指向整个数组

a[0]==(*p)[0]==**p

a[0](*p)[0]**p是等价的。

  1. (*p)表示解引用指针 p,得到一个包含 5 个整数的数组,(*p)[0]表示这个数组的第一个元素
  2.  **p也是先解引用 p得到数组,再解引用这个数组的首元素指针得到第一个元素。

a[4]     ==    (*p)[4]    ==    *((*p)+4)    ==   *((int *)(p+1)-1)    ==   *(*(p+1)-1)   

a[4](*p)[4]*((*p)+4)*((int *)(p+1)-1)*(*(p+1)-1)也是等价的。

  1. (*p)[4]表示解引用 p 指向的数组的第五个元素
  2. *((*p)+4)先解引用 p得到数组,再对这个数组的首元素指针加上 4,然后解引用得到第五个元素
  3. *((int *)(p+1)-1)先将 p + 1转换为指向整数的指针,然后减去 1,再解引用得到数组的最后一个元素
  4. *(*(p+1)-1)先解引用 p + 1得到一个新的数组(这里实际上是越界访问,但从逻辑上理解),再对这个新数组的首元素指针减 1,解引用得到原数组的最后一个元素。

e.g.

int a[2][3];

int *p=&a[0][0]; //将指针 p 指向二维数组 a 的第一个元素a[0],即*a 。

a[1][2]     ==    a[0][5]     ==    *(p+5)

因为二维数组在内存中是按行优先存储的,a[1][2]在内存中的位置相对于 a[0][0]正好偏移了 5 个整数的位置。

int (*p)[3]=&a[0]; // 让 p 指向二维数组 a 的第一行a[0]

a[1][2]     ==    *(*(p+1)+2)     ==    (*(p+1))[2]      ==    *(p[1]+2)     ==    p[1][2]      

   *(p + 1)表示指向二维数组的第二行,*(p + 1)+2表示第二行的第三个元素的指针,解引用得到该元素的值;

(*(p + 1))[2]先解引用 p + 1得到第二行数组,再访问第三列的元素;

*(p[1]+2)p[1][2]也是类似的逻辑。

int (*p)[2][3]=&a;//将 &a赋值给 p,即 p 指向整个二维数组 a

a[1][2]    ==     (*p)[1][2]

(*p)表示解引用指针 p,得到二维数组 a(*p)[1][2]表示这个二维数组的第二行第三列的元素。

学习理解这部分知识,可以:

  1. 画图辅助理解
    • 对于数组和指针的关系,可以画出数组在内存中的布局图。例如,对于一维数组int a[5],可以将其想象成内存中的连续 5 个存储单元,每个单元存储一个int类型的值。当定义int *p = a;时,在图上可以将指针p指向数组a的第一个元素所在的存储单元。这样在考虑p + 1时,就很容易理解它指向了数组中的下一个元素,因为在内存中,p + 1的地址值是p的地址值加上sizeof(int)(假设int类型占 4 个字节,那么p+1的地址比p的地址大 4)。
    • 对于二维数组,比如int a[2][3],可以将其看作是一个有 2 行 3 列的表格。在内存中,它仍然是连续存储的,按照行优先的顺序。如果int *p=&a[0][0];,那么p就像一个游标,从表格的左上角(第一个元素)开始,p + 1就指向表格中的下一个元素。当定义int (*q)[3]=&a[0];时,q可以看作是指向每行(包含 3 个元素的一维数组)的指针,q + 1就指向二维数组的下一行。
  2. 通过代码示例逐步调试
    • 编写简单的测试程序,在程序中输出数组元素的地址和指针的值。直观地看到指针和数组元素地址的变化情况。对于二维数组也可以采用类似的方法,逐步观察指针的移动和元素的访问过程。

从基本概念出发,逐步深入理解

  • 首先要牢记数组名在很多情况下会转换为指向数组首元素的指针这个基本概念。对于指针的算术运算,要理解+1操作对于不同类型指针(如指向int的指针、指向数组的指针等)的实际意义,它是根据指针所指向的类型的大小来移动指针的。然后再深入理解多级指针(如指向数组的指针的指针)的概念,从简单的一维数组指针开始,逐步过渡到二维、三维数组等更复杂的指针应用场景。

指针数组

整型数组 ----数组元素是整型     

指针数组 ----数组元素是指针

int *a[5]; //本质上是数组

a[0] a[1] a[2] 都是指针

int *a[2][3];

a[0] a[1] 都是一维指针数组

a[0][0] a[0][1] a[1][1] 都是指针

字符指针数组

char *a[5]={“123”, “abcdef”, “45789”, “zxcvb”, “000123456”};

char b= ‘A’;

a[0]=&b;

a[1]= “123456”; 字符指针指向首字符地址,不能修改字符串的数据

sizeof(a) == 40

定义了一个字符指针数组 a,它包含五个指针,分别指向五个不同的字符串常量。

接着定义了一个字符变量 b,初始化为 'A'。然后让 a[0]指向 b 的地址,此时 a[0]不再指向字符串,而是指向单个字符 b。这就像把原来指向一个字符串物品堆的标签,改指向了单独的一个字符物品。

之后又让 a[1]指向新的字符串常量 “123456”。由于字符串常量在内存中通常是不可修改的,所以虽然指针可以指向不同的字符串,但不能改变字符串常量本身的内容。就像标签可以指向不同的物品堆,但不能改变物品堆里的物品。

这里的两个赋值语句有一定区别:

  1. 赋值内容不同
    • a[0]=&b;是让a[0]指向一个字符变量b(存储单个字符)。
    • a[1]= “123456”;是让a[1]指向一个字符串常量(包含多个字符和结束符)。
  2. 可修改性不同
    • 通过a[0]可以修改b的值。
    • 不能修改a[1]指向的字符串常量内容,否则可能出错。

最后,sizeof(a)等于 40,因为在这个环境下指针可能占用 8 个字节,而数组 a 有五个指针元素,所以总共占用 40 个字节。就像一个盒子里装了五个特定大小的东西,通过测量盒子大小可以知道这些东西总共占用的空间。

 

二维字符数组

定义一个二维字符数组

char a[3][80]={ “123”, “abcdef”, “45789”};

其中a[0] 最多存储 80 个字符

a[1][0]= ‘h’; //数组是变量的集合,可以更改数据

总长度sizeof(a) == 240

const 修饰变量为常量

const int a=5; // a 是只读变量,const修饰谁,谁就是常量

a=6; // 不合法,因为a被声明为const,不能被赋值修改。

int *p=&a;//用一个指向int类型的指针p指向这个const修饰的变量a的地址。

*p = 6; // 合法,通过指针去修改这个地址上的值

指针常量

数字常量         1         值不能改变

指针常量         int * const p;         p 被 const 修饰,所以 p 的值不能改变

  • int a=5,b=10;
  • int * const p=&a;//定义了一个常量指针p。这里的 “常量指针” 是指指针本身的值(即它所指向的地址)不能被改变,但它所指向的内容可以被改变。
  • *p=6; // 合法,虽然p不能指向其他地址,但它所指向的内容是可以修改的。这里通过*p修改了p所指向的变量(即a)的值,将其从5改为6
  • p=&b; // 不合法,初始化时让p指向了变量a的地址,之后就不能再让p指向其他变量的地址了

常量指针

整型指针         int *p; 指针指向整型

常量指针         char const *p; 指针指向常量         const 修饰*p,*p 不能改变

                        const char *p;

  • char a=57,b=32;
  • char const *p=&a;

//定义了一个指向常量字符的指针p,并初始化为指向a。这意味着不能通过这个指针来修改它所指向的内容。

  • *p=97; // 不合法

//不能通过这个指针来修改p所指向的内容。

  • p=&b; // 合法

//不能通过p修改它所指向的内容,但是可以改变指针p本身的值,让它指向其他的地址

  • *p=97; // 不合法

//虽然,现在p指向b,但仍不能通过这个指针来修改p所指向的内容。

  • printf(“%d\n”,*p); // 合法

//只是读取p所指向的内容并输出其对应的整数值,没有尝试修改它所指向的内容

const int *const p; //p 和*p 都是只读

const int *const p,这里有两个const关键字。第一个const修饰int,表示指针所指向的内容是常量。第二个const修饰指针p本身,表示指针p是一个常量,它存储的地址值不能被改变。

  1. 指针指向的内容只读(*p
    • 另一个const表示指针指向的变量的值不能被修改。即使p指向了某个变量,也不能通过p来改变这个变量的值。 
  2. 指针本身只读(p
    • const修饰指针p,就像把指针 “钉” 在了一个地址上,不能再让它指向别的地方。比如,不能重新给p赋值一个新的变量地址。

typedef 重命名数据类型

关于typedef的一般理解

typedef A B; // A 代表数据类型 B 代表新名称

A m;

B n; //m 和 n 数据类型相同

具体示例分析

typedef int * a;//定义了a作为int*类型的别名

a n; // 等同于int* n;,即定义了一个指向整数的指针n

int a,*b; // int a; int *b;声明了一个整数变量a和一个指向整数的指针变量b

int *a,b; // int *a; int b;声明了一个指向整数的指针变量a和一个整数变量b

typedef int * INT;//定义了一个别名INT代表int*类型。

INT a,b; // INT a; INT b; === int *a; int* b;//即声明了两个指向整数的指针变量ab

复杂示例分析

typedef int*(*F[2])[3];

F m; // 等同于int*(*m[2])[3];,即定义了一个变量m

sizeof(F) == 16

这定义了一个较为复杂的类型别名。F是一个数组类型,这个数组有 2 个元素,每个元素是一个指针,这个指针指向一个包含 3 个整数指针的数组。

从逐步分析这个类型定义:

分析F的长度

  • 先看FF被定义为一个数组类型,名为F的数组有 2 个元素,即F[2]
  • 接着看每个元素的类型,每个元素是一个指针,所以是(*F[2]),指针指向的对象是一个包含 3 个元素的数组。
  • 最后看这个包含 3 个元素的数组的元素类型,这个数组的元素是指向整数的指针,即int*,所以完整的类型就是int*(*F[2])[3],表示F是一个包含 2 个元素的数组,每个元素是一个指针,指向一个包含 3 个指向整数指针的数组。
  • 首先看 int*(*)[3]这个类型:

    • 它表示一个指针,这个指针指向一个包含 3 个指向整数指针(int*)的数组。
    • 在假设指针大小为 8 字节的系统中,这个指针本身占用 8 字节的空间。
  • 接着看 F的类型:

    • F被定义为 int*(*F[2])[3],这意味着 F是一个包含 2 个元素的数组。
    • 每个元素的类型是前面提到的指向包含 3 个指向整数指针的数组的指针。
    • 所以 sizeof(F)就等于 2 倍的这种指针的大小,即 2 * sizeof(int*(*)[3])

所以 sizeof(F) = 2 * sizeof(int*(*)[3]) = 2 * 8 = 16字节。

总结回顾--指针的奥秘:深入理解内存地址与数据存储

在 C 和 C++ 等编程语言中,指针是一个强大而又复杂的概念。它就像是一把钥匙,能够打开内存世界的大门,让我们直接访问和操作计算机内存中的数据。

一、指针的本质

指针的核心作用是保存地址。在计算机内存中,每个数据都存储在特定的空间位置,而这个位置可以用地址来表示。不同类型的指针保存不同类型数据的地址,这意味着指针的类型与它所指向的地址的类型紧密相关。例如,一个指向整数的指针只能保存整数变量的地址,而不能保存其他类型变量的地址。

在定义指针时,我们先确定指针指向的空间变量的类型,然后在变量名前面加上*来表示这是一个指针。例如,int *ptr表示ptr是一个指向整数的指针。当我们使用*解引用指针时,得到的是指针所指向的地址对应的空间变量

此外,指针的大小通常是固定的,并且与计算机的位数有关。在 32 位系统中,指针通常占用 4 个字节;而在 64 位系统中,指针通常占用 8 个字节。

二、地址类型转换的意义

地址类型转换主要是在转换地址所代表的空间大小。这是因为不同类型的数据在内存中占用的空间大小可能不同。例如,一个整数可能占用 4 个字节,而一个浮点数可能占用 8 个字节。当我们进行地址类型转换时,实际上是在告诉编译器如何解释特定地址上存储的数据。

让我们来看一个具体的例子,在地址 0x8000010002 的位置存储一个浮点型数据

float *p=(float *)0x8000010002;

*p=3.14;

(long)p+2 == 0x8000010004

首先,将地址 0x8000010002 强制转换为 float * 类型指针并赋值给 p,使 p 指向该地址的浮点型数据。接着,用 *p 将 3.14 存储到这个地址。然后看 (long)p + 2,由于指针大小与计算机位数有关,假设系统中指针占 8 字节。将 p 强制转换为 long 类型,是把地址值转为长整型数。而 (long)p + 2 是将地址值加 2 字节,因 long 占 8 字节、float 占 4 字节,加 2 字节相当于指向下一个浮点数地址,此时 (long)p + 2 的值为 0x8000010004。 

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【K8S系列】Kubernetes Service 基础知识 详细介绍

在 Kubernetes 中,Service 是一种抽象的资源,用于定义一组 Pod 的访问策略。它为这些 Pod 提供了一个稳定的访问入口,解决了 Pod 可能频繁变化的问题。本文将详细介绍 Kubernetes Service 的类型、功能、使用场景、DNS 和负载均衡等方面。 1.…

class 36 二叉树高频题目 - 上 (不含有树形dp)

1. BFS 的两种方式 如下图, 是一个二叉树. 我们需要按照层的方式来遍历这棵树. 1.1 使用 JDK 自带的类实现(链表实现, 经典 BFS) 首先我们实现一个队列, 这个队列从头进, 从尾出.然后将根节点放入其中, 然后将放入的节点弹出,然后继续验证弹出的节点有没有左孩子, 若是有, 将…

【HTML】之form表单元素详解

HTML表单是网页与用户交互的关键组成部分,它允许用户输入数据并将数据提交到服务器进行处理。本文将全面详细地介绍HTML表单的各个方面,从基础元素到高级用法,并提供丰富的代码示例和中文注释,帮助你彻底掌握表单的使用。 1. 表单…

强大!Spring Boot 3.3 集成 PDFBox 轻松实现电子签章功能!

强大!Spring Boot 3.3 集成 PDFBox 轻松实现电子签章功能! 随着数字化办公和电子合同的普及,PDF 文档已经成为很多业务场景中的标准文件格式。为了确保文档的安全性和法律效力,电子签章技术应运而生。电子签章不仅可以证明文件的…

视频美颜平台的搭建指南:基于直播美颜SDK的完整解决方案

众所周知,直播美颜SDK是实现视频美颜功能的核心。本文将详细解析如何基于直播美颜SDK搭建一个完整的视频美颜平台。 一、视频美颜SDK的核心功能 直播美颜SDK作为平台的技术核心,能够提供丰富的美颜效果和稳定的视频处理能力。通常,SDK具备以…

传输层TCP

报头 1.报头和有效载荷如何分离将,有效载荷向上交付? tcp有个标准报头长度为20,那是不是以为我们可以像udp一样分离依靠报头大小去分离,我们仔细去看我们报头中还有个选项没包含到。 我们还有个首部长度,四位可以表…

【Axure高保真原型】分级树筛选中继器表格

今天和大家分享分级树筛选中继器表格的原型模板,点击树的箭头可以展开或者收起子级内容,点击内容,可以筛选出该内容及子级内容下所有的表格数据。左侧的树和右侧的表格都是用中继器制作的,所以使用也很方便,只需要在中…

SwiftUI:单个App支持设置多语言

SwiftUI 全新多语言方案 简化本地化的字符串- WWDC21 - 视频 本地化您的SwiftUI app - WWDC21 - 视频 构建全球化App:本地化的示例- WWDC22 - 视频 构建支持多语言的App - WWDC24 - 视频 单个App支持设置多语言 工程 Info.plist里添加 键值UIPrefersShowingLangua…

论1+2+3+4+... = -1/12 的不同算法

我们熟知自然数全加和, 推导过程如下, 这个解法并不难,非常容易看懂,但是并不容易真正理解。正负交错和无穷项计算,只需要保持方程的形态,就可以“预知”结果。但是这到底说的是什么意思?比如和…

【AI换装整合及教程】CatVTON:时尚与科技的完美融合

在当今数字化时代,时尚行业正经历着一场前所未有的变革,而 CatVTON 作为一款由中山大学、Pixocial 等机构联合研发的轻量化 AI 虚拟换装工具,无疑是这场变革中的璀璨明星。 一、独特的技术架构 CatVTON 基于 Stable Diffusion v1.5 inpainit…

css 切角实现(全)

效果 样式代码 <template><div class"container"><div class"clip-all-angle"> 4个角全部剪切 </div><div class"clip-two-angle"> 切底部两个角 </div><div class"clip-two-top-angle"> …

新鲜出炉,ECCV2024.9.25 首次提出基于 YOLO 目标检测的无源域自适应

原文标题&#xff1a;Source-Free Domain Adaptation for YOLO Object Detection 中文标题&#xff1a;基于 YOLO 目标检测的无源域自适应 论文地址&#xff1a; https://arxiv.org/abs/2409.16538 代码地址&#xff1a; GitHub - vs-cv/sf-yolo 1、Abstract 无源域自适应&…

ACL访问控制

要求&#xff1a; PC1与PC2不能通信。PC1可以和PC3通信。PC2可以和PC3通信。 1. VLAN配置 根据拓扑图的连接&#xff0c;PC1、PC2、PC3属于不同的VLAN。我们需要确保交换机上的端口已经正确划分到不同的VLAN。假设交换机接口的VLAN配置已经完成&#xff08;其他博文有)&…

【Linux】线程池详解及其基本架构与单例模式实现

目录 1.关于线程池的基本理论 1.1.线程池是什么&#xff1f; 1.2.线程池的应用场景&#xff1a; 2.线程池的基本架构 2.1.线程容器 2.2.任务队列 2.3.线程函数&#xff08;HandlerTask&#xff09; 2.4.线程唤醒机制 3.添加单例模式 3.1.单例模式是什么&…

多IP访问网站

1.创建挂载点 mount /dev/sr0 /mnt vim /etc/yum.repos.d/base.repo [BaseOS] nameBaseOS baseurlfile:///mnt/BaseOS gpgcheck0 [Appstream] nameAppStream baseurlfile:///mnt/AppStream gpgcheck0 2.关闭防火墙等 systemctl stop firewalld setenforce 0 3.下载nginx…

【我的 PWN 学习手札】setcontext + shellcode

目录 一、setcontext gadget 二、setcontext shellcode &#xff08;一&#xff09;覆写__free_hook为setcontext53 &#xff08;二&#xff09;在堆块布置了一块sigframe &#xff08;三&#xff09;覆写__free_hook0x8__free_hook0x10 &#xff08;四&#xff09;从__…

流媒体协议.之(RTP,RTCP,RTSP,RTMP,HTTP)(一)

闲着没事做&#xff0c;记录一下开发项目用过的协议&#xff0c;项目中&#xff0c;大多是是实时显示播放的&#xff0c;通过私有协议&#xff0c;传输到上位机&#xff0c;实时播放&#xff0c;延时小于200ms&#xff0c;仿照这些协议&#xff0c;定义的数据格式。如果用这些协…

新王Claude 3.5的6大应用场景

Anthropic AI深夜发布了备受期待的Claude 3.5系列更新&#xff0c;包括了全新升级的Claude 3.5 Sonnet和首发的Claude 3.5 Haiku。 Claude 3.5 Sonnet能够理解细微的指令和上下文&#xff0c;识别并纠正自身错误&#xff0c;还能从复杂数据中生成深入的分析和洞察。 结合最先进…

10.22.2024刷华为OD C题型(三)--for循环例子

脚踝动了手术&#xff0c;现在宾馆恢复&#xff0c;伤筋动骨一百天还真不是说笑的&#xff0c;继续努力吧。 文章目录 靠谱的车灰度图恢复灰度图恢复 -- for循环使用例子 靠谱的车 https://www.nowcoder.com/discuss/564514429228834816 这个题目思路不难&#xff0c;就是要自…

手把手教你安装最强文生图工具ComfyUI

ComfyUI 是一款专为稳定扩散&#xff08;Stable Diffusion&#xff09;设计、基于节点的高效用户界面&#xff0c;因其高度的可定制性&#xff0c;正逐渐成为广大用户的新宠。本文教你如何在 Windows 和 Mac 上安装 ComfyUI&#xff0c;并提供一些快速上手的小贴士。 1 ComfyU…