栈, 是硬件, 主要作用表现为一种数据结构, 是只能在一端(栈顶, 另一端为栈底)插入和删除数据的特殊线性表, 在计算机系统的动态内存区域.
关于栈, 有后进先出/先进后出两个说法, 其实都是正确的, 只是关注点不一样, 一个是后入栈的数据, 一个是先入栈的数据. 根据栈的特性, 后入栈的数据总在先入栈的数据上方, 因为栈仅有一个口(出口和入口是统一的), 先入栈的数据要出栈的话, 就必须把在这之后的数据全部出栈. 有点类似从羽毛球桶里面取球和放球的操作, 当然, 另一头是封死的.
从书中看到, “压栈的操作使栈顶的地址减小,弹出的操作使栈顶的地址增大”, 这句是存疑的. 也就是说栈的数据从高地址向低地址的走向, 但是现在是虚拟内存地址, 总感觉这里怪怪的, 栈顶的走向应该体系架构相关的.
堆, 是一种动态存储结构, 实际上就是数据段中的自由存储区, 常常用于存储、分配动态数据.
堆中存入的数据地址向增加方向变动. 关于堆, 有先进先出的特性, 也就是一边是入口, 一边是出口, 像管道一样, 两头通, 只是限制每头的入和出. 堆内存通常malloc()
, calloc()
, realloc()
三个函数来分配, 使用free()
释放.
#include <stdio.h>
#include <stdlib.h>
int main() {
char *a;
char *b;
char *c;
a = (char *)malloc(sizeof(char));
b = (char *)malloc(sizeof(char));
c = (char *)malloc(sizeof(char));
printf("%p\n", a); // 0x55a165fba2a0
printf("%p\n", b); // 0x55a165fba2c0
printf("%p\n", c); // 0x55a165fba2e0
return 0;
}
就实际输出来看(可能这里显示是虚拟内存地址), 这堆内存的分配是从低地址向高地址进行的. 还句话来说, 堆和栈的生长方向相反的, 栈向低地址方向生长, 而堆向高地址方向生长.
由于这里不懂汇编, 只能从C中的树组打印出来的地址观察一下, 栈的生长方向, 首先需要说一下, 树组的压栈顺序是从右到左.
#include <stdio.h>
int main() {
int a[] = {1, 2, 3, 4,5 ,66};
for (int i = 0; i < 6; ++i) {
printf("%p\n", &a[i]);
// 0x7fffab21b330
// 0x7fffab21b334
// 0x7fffab21b338
// 0x7fffab21b33c
// 0x7fffab21b340
// 0x7fffab21b344
}
return 0;
}
从右到左, 在输出中, 从下到上, 确实是从高地址到低地址生长的.