五、指针的概念与使用
00 分钟
2023-8-11
2023-9-15
type
status
date
slug
summary
tags
category
icon
password

1、指针的定义

在内存中,每个字节都有一个对应的编号,这个编号就是”地址“。如果定义一个变量,系统就会给这个变量分配内存,而这个内存是有一个地址值的。这个地址值就叫做指针,按照这个地址值读写变量值的方式叫做**“直接访问“;有字节访问当然就有“间接访问”,即将这个变量(这里暂时叫做A)的地址值用一个新类型的变量(这里暂时把他叫叫做B)储存起来,用B变量的值去找到A变量,最后获取到内容。其中B变量就称为指针变量**。
指针与指针变量是两个概念,指针是一个变量的地址,而指针变量是用来存放指针的变量。

1.1、指针的声明

指针的定义和声明使用星号(*)运算符。下面是指针的定义语法:
在这里,<数据类型>表示指针所指向的数据类型,<指针变量名>是指针变量的标识符。
例如,以下是一个指向整数类型的指针的定义:
这里,ptr是一个指向整数的指针变量。
注意,指针的类型与它所指向的数据类型必须匹配。这是因为指针解引用时,它会根据自身的类型来读取相应大小的内存。
另外,还可以通过在指针变量名前添加const关键字,将指针声明为常量指针。常量指针指向的内存地址是固定的,不可更改。
例如,以下是一个指向整数的常量指针的定义:
这里,ptr是一个指向整数的常量指针,它指向的整数值是不可更改的。

1.2、指针的空间大小

现如今大多数家用电脑都是64位架构的电脑,所以在大多数情况下,指针的的大小是8个字节。但是开发环境不同,受不同机器限制,指针会有不同的大小:
  1. 在32位体系结构上,指针的大小通常为4字节(32位),它可以表示内存中的地址范围为2^32个字节(4GB)。
  1. 在64位体系结构上,指针的大小通常为8字节(64位),它可以表示内存中的地址范围为2^64个字节(16EB)。
需要注意的是,指针的大小不仅取决于计算机体系结构,还取决于所使用的编程语言和编译器的实现。例如,某些编程语言中的指针可能具有固定的大小,无论计算机体系结构如何。
此外,指针的空间大小和指针所指向的数据类型无关。不论指针指向的是一个字节、整数、浮点数还是其他类型的数据,指针本身的大小都是固定的。
需要注意的是,在某些特定的情况下,指针的大小可能会有所不同。例如,嵌入式系统或某些特殊的编程环境中可能存在非标准的指针大小。在这种情况下,具体的系统文档或编译器文档会提供有关指针大小的详细信息。

2、指针的使用

2.1、指针的运算符

2.1.1、运算符介绍

指针在编程语言中有一些特定的运算符用于操作和处理指针。以下是常见的指针运算符:
  1. 取地址运算符(&):也称作引用运算符,用于获取变量的地址。例如,&variable将返回变量variable的地址。
  1. 取值运算符(*):也称解引用运算符,用于访问指针所指向的存储位置上的值。例如,ptr将返回指针ptr所指向的值。
  1. 指针赋值运算符(=):用于将一个指针的值赋给另一个指针变量。例如,ptr1 = ptr2将使ptr1指向与ptr2相同的地址;或者ptr1 = &ptr2将使ptr2的地址赋值给ptr1
  1. 指针加法运算符(+):用于将指针的值增加某个偏移量。例如,ptr = ptr + 1将使指针ptr指向下一个相邻位置。
  1. 指针减法运算符(-):用于将指针的值减去某个偏移量。例如,ptr = ptr - 1将使指针ptr指向上一个相邻位置。
  1. 指针比较运算符(==、!=、>、<、>=、<=):用于比较两个指针的值。这些运算符通常用于检查指针是否相等、指针的相对顺序等。
需要注意的是,指针运算通常是基于指针所指向的数据类型的大小。例如,对指针进行加法或减法时,会根据指针所指向的数据类型的大小进行偏移量的计算。此外,指针运算还受到指针的有效范围的限制,避免越界访问。

2.1.2、运算符的使用

这是引用运算符和解引用运算符的使用:
需要注意:
  1. 指针变量前的 ”*“ 表示该变量为指针型变量,例如:
    1. 此时指针的变量名为pointer,而不是*pointer
  1. 在进行指针定义时,我们必须做到类型对应,即int类型对应int指针等。例如:
    1. 而以下说错误的赋值方式:
  1. 有时候我们会看到一个奇葩语句,&*p或者&p,例如:
      • &p 分析:首先p 间接访问到变量 i ,然后&取地址变量 i ,最后的结果还是 &i 或 p
      • &p 分析:首先 &p 取地址得到 p 变量的地址,然后通过 p 变量的地址间接访问到 p ,最后的结果还是 &i 或 p
      在代码中甚至还会出现**p或者&&p等,可以根据上面的推理自己了解。
  1. 我们在使用指针声明时,最好遵守一个规范,把*和变量名写在一起,如 int *a;当有多个变量时我们可以: int *a,*b,*c

3、指针的使用场景

从简单的来说,指针的使用场景主要分为两个,一个是传递,另一个是偏移。传递一般是在函数之间的调用;偏移一般用在数组的操作。
如下是一些使用场景,由ChatGPT生成:
  1. 动态内存分配:指针允许在运行时动态地分配内存。通过使用指针和相关的内存管理函数(如malloc()free()),可以在程序执行期间动态地分配和释放内存,以满足灵活的内存需求。
  1. 数组操作:指针与数组密切相关。C语言中的数组名实际上是指向数组首元素的指针。通过使用指针算术运算,可以遍历数组元素、进行数组操作和传递数组作为函数参数。
  1. 字符串处理:字符串在C语言中是以字符数组的形式表示的。通过使用指针,可以对字符串进行遍历、复制、连接和比较等操作。指针还可以用于字符串的动态分配和释放。
  1. 函数传递和返回:指针允许在函数之间传递和返回数据。通过传递指针作为函数参数,可以在函数内部直接修改传递的变量的值。指针还可以用于返回函数内部动态分配的内存。
  1. 数据结构:指针在构建各种数据结构时非常有用。例如,链表、树和图等数据结构通常使用指针来连接不同的节点或元素,实现灵活的数据结构操作。
  1. 处理硬件和底层操作:指针在与底层硬件交互、进行位操作和直接访问内存等方面非常有用。通过指针,可以直接读取和写入内存位置,实现对硬件设备的控制和底层操作。

3.1、指针的传递

在C语言中,可以通过指针来实现函数之间的参数传递。通过传递指针作为函数参数,可以在函数内部直接访问和修改指针所指向的变量,从而实现对原始数据的修改。这种方式称为指针传递或引用传递。例如:
还有传递指针的指针:

3.2、指针的偏移

指针的偏移大多用在数组之上,我们声明一个数组 int a[5] = {10,20,30,40,50} ,这时a的值就是一个指针,而且他指向的是数组的第0个元素。我们可以通过*a 访问到元素10 ,以此类推我们可以访问到 *a + 1, *a + 2, ··· ,例如:
在使用指针偏移时,我们不能使其访问越界,否则会造成访问到其他内存中的数据,导致代码运行过程中出错,同时传递的指针不能获取到数组的长度。原因与解决方案参考:
三、数组的访问越界和传递
三、数组的访问越界和传递

4、指针与动态内存申请

我们知道,在声明一个数组时,我们需要指定数组的长度。但是在实际开发当中,我们并不知道数组的长度是多少,需要一个变量作为数组的长度。
其实C语言中数组长度固定是因为他的数组变量是在栈空间中的,栈空间的大小在编译时时固定的,如果空间的大小不确定,我们就要使用堆空间。例如:
首先是 malloc 函数,在执行 malloc(size * sizeof(int)) 时,我们申请了一块内存,但由于编译器不知道我们需要空间存放什么类型的数据,所以需要 (int *)malloc(size * sizeof(int)) 来强制类型转换。
同时,因为堆空间由我们自己管理,所以我们在使用完堆空间后,需要通过 free(ptr); 自己手动释放内存。

5、栈空间与堆空间的差异

如果我们不自己手动释放内存,堆内存中的数据不会因为函数的栈空间释放而释放,导致留在内存中占用过大。例如:
 
上一篇
Harmony OS应用开发-工程目录介绍
下一篇
四、字符数组(字符串)的使用

评论
Loading...