0%

程序和内存 03.站续

程序和内存 3.站续

在C语言里有一些和数组相关的一些很让人迷惑的特点, 今天我们一起探索为什么会出现这样的情况, 之后给站区做一个总结。

伪装成数组的指针

我们有这样一个函数:

void fun(int a1[])
{
int a2[100];
printf("%d, %d", sizeof(a1), sizeof(a2));
}
// main
int ary[200];
fun(ary);

编译运行这个程序, 我们希望得到的结果是800和400, 但是结果却是8和400, 在三十二位系统上是4和400。

那么为什么会这样, 原因就是把数组作为参数传入其他函数的时候实际上传递的是该数组的内存地址。

所以把函数的数组参数用sizeof运算得到了指针的大小, 如果把数组定义在函数内部的话, 那么sizeof的结果就是数组的大小。

所以说在C语言里数组传参需要一个额外的参数来记录数组的长度, 如果数组的类型是字符串的话就不需要额外的参数了, 因为通常情况下字符串的结尾是’\0’,

只要检测这个’\0’就知道长度了, 但是其他类型的数组就没办法知道传过来的数组有多长。

在C语言里为什么必须在编译阶段确定其大小

此外C语言的数组和其他语言有个显著的差距, 先看一段java数组初始化代码

final int LEN = 100;
int[] ary = new int[LEN];

同样的代码在C语言里行不通, 先学习其他语言之后在学习C语言的人肯定踩过这个坑。

const int LEN = 100;
int ary[LEN];

这是原封不动的编译错误输出:

D:\Src\c\test\mem\part3>cl demo6.c
用于 x86 的 Microsoft (R) C/C++ 优化编译器 19.33.31630 版
版权所有(C) Microsoft Corporation。保留所有权利。

demo6.c
demo6.c(6): error C2057: 应输入常量表达式
demo6.c(6): error C2466: 不能分配常量大小为 0 的数组
demo6.c(6): error C2133: “ary”: 未知的大小

这个就很不可理解了, 难道const常量不是常量表达式么? 这个还真不是。

C语言要求数组的大小必须在编译阶段确定, 而const常量是在程序运行阶段确定的, 所以又叫只读变量。

看这个例子:

int i;
scanf("%d", &i);
const int ID = i;

这里ID的值是在运行阶段有用户的输入决定的, 所以在c语言里定义数组的正确姿势是这样:

#define LEN 100
// 省略
int ary[LEN];

1.为什么数组传递地址? 为了加快程序运行速度, 虽然不符合人的直觉。

2.为什么c语言的数组的大小在编译阶段确定, 而其他语言则可以在运行阶段动态确定? 因为C语言的数组是在站区分配空间的, 而其他语言也许在战区分配, 也许放在了堆区, 这个就不太好说了, 估计是有一定的分配策略, 比如。

  • 分配到战区 数据量比较小, 编译阶段能够确定大小

  • 分配到堆区 数据量比较大, 编译阶段无法确定大小

    大家可以在前面的stcakOverflow函数里放一个数组, 然后观察运行结果, 看看栈溢出是不是更快了。

管中窥豹……站区的总结

在数据结构里有个重要成员叫站stack, 而我们的函数调用巧妙的利用了站的特点来实现的……

站是一种后进先出的数据类型, 两个重要操作压入push和弹出pop。

函数开始调用把所有的局部变量全部压入站里, 函数返回把函数的所有局部变量从站里弹出,

如果你只管压入不给弹出时间长了肯定溢出的, 毕竟它的容量是有限的Windows2mb; Linux8mb,

当然这些东西在编译阶段可以调整, 不过一般情况下默认的大小足够你发挥了。

最后的总结如下, 请牢记!

  • 战区是函数调用时存取相关局部变量的空间

  • 最大容量在编译阶段确定, 运行阶段不可超过其最大容量, 超过了就是栈溢出错误

  • 战区在一个连续的内存空间里, 从高地址往低地址增长, 函数返回之后退回到高地址处

  • 函数返回后原来被占据的低地址空间被释放, 后续不可随意操作, 只能等待下一次的函数调用

    函数的调用是不是特别像海浪, 随着程序的运行起起落落, 函数调用潮起; 函数返回潮落; 递归调用钱塘江大潮; 栈溢出海啸!

    这里我们格外避免两种情况, 其他的有编译器和操作系统帮我们搞定了。

  • 避免栈溢出, 避免海啸

  • 避免访问函数调用结束后的无效地址, 海浪退潮后避免搁浅半死不活

    避免栈溢出不难, 避免访问无效地址这个就说来话长了, 尤其是扯上指针和堆区更是内存管理的世纪难题。

    关于这些内容下一次继续, 未完待续……