1. 计算机内存的基本概念
计算机的内存(RAM,随机存取存储器)是用来存储程序运行时的数据和指令的地方。内存被划分为许多小单元,每个单元有一个唯一的地址,这些地址从0开始编号。
- 内存单元:每个内存单元通常是一个字节(8位),用来存储一个字符或部分整数。
- 内存地址:每个内存单元都有一个唯一的地址,用于标识它。例如,地址
0x1000
表示内存中某个特定位置。
2. C语言中的内存模型
C语言中的内存可以大致分为以下几个区域:
(1)代码区(Text Segment)
- 存储程序的机器指令(即编译后的代码)。
- 这部分内存是只读的,防止程序意外修改自己的指令。
(2)全局/静态区(Data Segment)
- 存储全局变量和静态变量。
- 又分为两部分:
- 初始化数据区:存储已初始化的全局变量和静态变量。
- 未初始化数据区(BSS段):存储未初始化的全局变量和静态变量。
(3)堆区(Heap)
- 用于动态内存分配(如
malloc
、calloc
、realloc
等函数分配的内存)。 - 程序员需要手动管理堆区的内存分配和释放(使用
free
函数)。 - 堆的大小不固定,可以根据需要增长。
(4)栈区(Stack)
- 用于存储函数调用时的局部变量、函数参数、返回地址等。
- 栈的大小是固定的,通常较小。
- 栈是自动管理的,当函数调用结束时,栈上的局部变量会自动销毁。
3. 指针与内存地址的关系
指针是C语言中直接操作内存的核心工具。指针本质上是一个变量,它存储的是另一个变量的内存地址。
(1)指针的基本概念
- 指针变量本身占用内存空间(通常是4字节或8字节,取决于系统架构)。
- 指针变量的值是一个内存地址。
(2)指针与内存地址的关系
4.通过*解引用指针p,可以直接访问和修改变量a存储在内存中的数值(数据内容),只是改变了该变量a在内存地址中存储的数据内容,而变量a
的内存地址本身是保持不变的。
(1)变量在内存中的内存地址 vs 存储值的关系
变量在内存中的内存地址
- 每个变量在声明时 系统为变量分配了一块内存空间, 这块内存空间有一个唯一的固定的地址(0000007BF96FF5D4) 在变量的生命周期内该内存中地址是固定不变的,不会因为值的变化而改变。并且在这块内存空间(也可以叫这个对应的内存地址上)中存储了变量的初始值(比如10)。
- 内存地址是用来标识变量在内存中的位置。每个变量在内存中都有一个唯一的内存地址。
- 而存储的数值是该“位置”编号中存放的内容。
- 修改这个变量的数值并不会改变该变量的内存地址编号。
存储的值
- 内存地址对应的存储单元中存放的具体数据。对于变量
a
来说,存储值就是10
。 - 可以通过直接访问变量或通过指针解引用来修改。
图解说明
(2)示例代码
/*
系统为变量a2分配了一块内存空间, 这块内存空间有一个唯一的地址(0000007BF96FF5D4),并且在这块内存(也可以叫这个对应的内存地址上)中存储了变量的初始值(比如10)。
*/
int a2 = 10; // 声明并初始化变量a
int* p_a2 = &a2; // 定义一个指针p,指向变量a2的地址
// 打印初始状态
printf("变量2a的初始值=%d\n", a2); //变量2a的初始值=10
printf("变量a2在内存中的内存地址编号=%p\n", (void*)&a2); //变量a2在内存中的内存地址编号=0000007BF96FF5D4
//通过*解引用指针p,可以直接访问和修改变量a2 存储在内存地址 (0000007BF96FF5D4)上对应的数值(数据内容)从10改为20,此时的内存地址没有变,但该内存地址上存储的值已经变为20
* p = 20; // 相当于 a2 = 20;
// 打印修改后的状态
printf("变量a2的修改后值=%d\n", a2); //变量a2的修改后值=10
printf("变量a2在内存中的地址编号=%p\n", (void*)&a2); //变量a2在内存中的地址编号=0000007BF96FF5D4
(3)通过以上实例代码,为什么内存地址不变?
- 变量在内存里的内存地址是由编译器和操作系统在程序运行时分配的,并且在变量的生命周期内是固定不变的。
- 修改值只会影响内存里单元中的内容(数值),而不会改变变量本身的在内存里的内存地址。
内存地址是变量的位置标识,而值是该位置中存储的内容。修改值只是改变了内容,而位置本身不会发生变化。