31 变量的访问方式(直接和间接),内存地址(32 位和 64 位),指针的概念与定义,取址与取值运算符( 与 *)
目录
1 变量的访问方式
1.1 直接访问
1.2 间接访问
2 内存地址
2.1 唯一性
2.2 32 位系统的内存地址
2.3 64 位系统的内存地址
3 什么是指针
4 指针的定义
4.1 定义格式
4.2 三种定义指针的写法
5 取址运算符和取值运算符
5.1 取址运算符(&)
5.2 取值运算符(*)
6 指针应用案例
6.1 指针的赋值和间接访问
6.2 修改指针所指向的值
1 变量的访问方式
内存作为计算机系统中至关重要的组件,承担着程序运行时数据存储的任务。为了高效地利用内存资源,它被细分为多个较小的存储单元,每个单元一般占据 1 个字节的空间。当程序需要使用数据时,这些数据会首先被加载到内存中,形成变量的形式存在。不同的变量类型根据其特性,会在内存中占据不同大小的空间。那么,如何有效地访问这些存储在内存中的变量数据呢?主要存在两种访问方式:
1.1 直接访问
这是最常见的访问方式,通过直接使用变量名即可实现对内存中数据的读取或修改。在大多数编程语言中,当我们声明一个变量并赋予其值后,就可以直接通过该变量名来操作其内存中的数据。例如,在 C 语言中,声明一个整型变量 int number = 10; 之后,可以通过 number 直接访问这个变量的数据。
int number = 10;
printf("%d\n", number); // 直接访问 number 变量的值
1.2 间接访问
这种访问方式则是通过指针来实现的。指针是一个特殊的变量,用于存储其他变量的内存地址。通过指针,我们可以间接地访问或修改指针所指向的内存位置中的数据。这种方式提供了更大的灵活性,尤其是在处理大型数据结构或动态内存管理时。例如,在 C 语言中,如果有一个指针 int *ptr; 并且让它指向变量 number 的地址 ptr = &number; ,那么我们可以通过 *ptr 来访问 number 的值。
int number = 10;
int *ptr = &number; // ptr 是一个指针,存储了 number 的地址
printf("%d\n", *ptr); // 通过指针 ptr 间接访问 number 的值
2 内存地址
2.1 唯一性
为了确保每个内存单元都能被准确无误地访问,每个单元都被赋予了一个唯一的编号,这个编号就是内存地址。由于每个内存单元都拥有自己的地址,因此存储在这些单元中的变量也自然拥有了对应的地址。内存地址的存在使得程序能够精确地定位和操作数据,是计算机内存管理系统的基础。
假设有 int 型变量 num,其在内存中会占用 4 个字节,也就是占用 4 个内存单元,第一个内存单元的地址即是变量 num 的地址。如下图所示:
2.2 32 位系统的内存地址
在 32 位架构的系统中,内存地址通常由 32 位(4 个字节)的二进制数字构成。这意味着理论上可以寻址 2 的 32 次方个不同的内存位置,大约等同于 4GB 的内存空间。这对于早期的个人计算机和某些嵌入式系统来说已经足够,但在现代计算环境中可能显得有些局限。
在 32 位系统中,打印出来的内存地址通常有 8 位十六进制数字。这是因为 32 位系统中的内存地址是 32 位(4 个字节)的二进制数,每 4 位二进制数可以用 1 位十六进制数表示。因此,32 位地址转换为十六进制表示时,会有 8 位(32 / 4 = 8)的十六进制数字。
2.3 64 位系统的内存地址
随着技术的发展,64 位架构的系统成为了主流。这类系统中的内存地址则由 64 位(8 个字节)的二进制数字组成,能够寻址的内存位置数量达到了 2 的 64 次方,这是一个极其庞大的数字,远超目前任何实际应用中所需的内存容量。64 位系统的出现极大地扩展了可寻址内存空间,为高性能计算和大数据处理提供了坚实的硬件基础。
在 64 位系统中,打印出来的内存地址通常有 16 位十六进制数字。这是因为 64 位系统中的内存地址是 64 位(8 个字节)的二进制数,每 4 位二进制数可以用 1 位十六进制数表示。因此,64 位地址转换为十六进制表示时,会有 16 位(64 / 4 = 16)的十六进制数字。
提示:
无论指针指向 int、char、float 还是 double,指针本身的长度是固定的,由系统的位数决定。在 32 位系统中,所有指针的长度都是 4 个字节;在 64 位系统中,所有指针的长度都是 8 个字节。
3 什么是指针
在编程中,如果一个变量专门用于存储另一个变量的内存地址,那么这个变量就被称为指针变量,简称指针。指针提供了一种间接访问内存中数据的方法。通过指针,我们不仅可以访问数据本身,还可以修改数据,甚至可以改变指针所指向的内存地址。
如下图所示,有一个整型变量 num,它的值存储在某个内存地址中。如果创建一个指针变量 ptr 并将 num 的地址赋值给 ptr,那么我们就可以说 ptr 指向了变量 num。通过 ptr,我们可以间接访问和操作 num 的值。
4 指针的定义
指针是一种特殊类型的变量,用于存储内存地址。
4.1 定义格式
定义指针时,需要指定指针所指向的数据类型。指针的定义格式如下:
数据类型 *指针变量名 [=初始地址值];
- 数据类型:指针所指向的地址处的数据类型,例如 int、char、float 等。
- 符号 *:用于通知编译器,这里定义的是一个指针变量。* 符号通常放在变量名前面,表示指针指向的是什么类型的值。例如,char * 表示一个指向字符的指针,float * 表示一个指向浮点数的指针。
- 指针变量名:指针变量的名称,用于在程序中引用该指针。
- 初始地址值(可选):可以为指针变量提供一个初始地址值,通常是另一个变量的地址。
4.2 三种定义指针的写法
在 C 语言中,以下三种定义指针的写法都是合法的,它们在功能上是等价的:
int *ptr;
int* ptr;
int * ptr;
尽管这三种写法都可以正确编译和运行,但为了代码的可读性和一致性,建议选择一种风格并保持统一。常见的做法是将 * 放在变量名前面,如下所示:
int *ptr; // 推荐写法
这样写法的好处是,当定义多个指针变量时,可以更清晰地表达每个变量的类型:
int *ptr1, *ptr2; // 推荐写法
如果将 * 符号紧随数据类型,当定义多个指针变量时,可能会引起误解,误以为都是 int* 类型的指针:
int* ptr1, ptr2;
其实,在这个例子中,ptr1 是一个指向 int 类型的指针,而 ptr2 是一个普通的 int 变量,而不是指针。
5 取址运算符和取值运算符
5.1 取址运算符(&)
- 符号:&
- 作用:取址运算符用于获取变量的内存地址。
- 用法:将 & 符号放在变量名前,可以得到该变量的内存地址。例如,&num 返回变量 num 的地址。
- 格式化输出地址:如果要在 printf 函数中格式化输出地址,需要使用 %p 格式占位符。例如:
printf("num 的地址: %p\n", (void*)&num);
5.2 取值运算符(*)
- 符号:*
- 作用:取值运算符用于获取指针所指向的内存地址处的数据值,也称为解引用运算符或间接引用运算符。
- 用法:将 * 符号放在指针变量名前,可以访问指针所指向的变量的值。例如,如果 ptr 是一个指向 int 类型的指针,*ptr 就表示 ptr 所指向的 int 变量的值。
6 指针应用案例
6.1 指针的赋值和间接访问
创建一个 int 类型的变量,使用取址运算符取出其地址,并将其地址赋值给一个指针,然后分别打印变量的值、变量的地址、指针的值、指针的地址、指针指向的值。
#include <stdio.h>
int main()
{
// 定义一个整型变量 num 并初始化为 100
int num = 100;
// 定义一个整型指针 ptr,它将被用来存储变量 num 的地址
// 使用 & 符号取得 num 的地址,并将这个地址赋值给 ptr
int *ptr = #
// 打印变量 num 的值和地址
printf("num 的值是 %d\n", num);
printf("num 的地址是 %p\n\n", &num); // %p 是用来打印指针(地址)的格式说明符
// 打印 ptr 的相关信息
// ptr 的值:即 ptr 指针本身存储的地址值,这里是 num 的地址
printf("ptr 的值是 %p\n", (void *)ptr); // 强制转换为 void* 以避免平台依赖的警告
// ptr 的地址:即 ptr 指针变量本身的内存地址
printf("ptr 的地址是 %p\n", &ptr);
// ptr 指向的值:即 ptr 指针所指向的内存地址中的值,这里是 num 的值
printf("ptr 指向的值是 %d\n", *ptr);
return 0;
}
输出结果如下所示:
内存示意图:
6.2 修改指针所指向的值
#include <stdio.h>
int main()
{
// 创建 double 类型的变量 num 并初始化为 2.88
double num = 2.88;
// 创建指针 p1,指向变量 num
double *p1 = # // p1 存储的是 num 的地址
// 创建指针 p2,将 p1 的值(即 num 的地址)赋值给 p2
// 此时 p1 和 p2 都指向同一个变量 num
// 注意:指针指向的数据类型和指针类型最好要匹配,这里都是 double 类型
double *p2 = p1;
// 打印 num 的初始值
printf("num的初始值:%.2f \n", num); // 输出: 2.88
// 通过指针 p1 修改 num 的值,并打印
*p1 = 3.88; // 解引用 p1 并将其指向的值修改为 3.88,即修改了 num 的值
printf("p1指针变量修改num后,num的值:%.2f \n", num); // 输出: 3.88
// 通过指针 p2 修改 num 的值,并打印
// 注意:这里使用 += 运算符,先解引用 p2 得到 num 的值,然后加 10
*p2 += 10; // 等价于 num += 10
printf("p2指针变量修改num后,num的值:%.2f \n", num); // 输出: 13.88
return 0;
}
输出结果如下所示: