先看三个变量:
c
#include <stdio.h>
int global_value = 10;
void test(void)
{
int local_value = 20;
static int static_value = 30;
printf("%d\n", global_value);
printf("%d\n", local_value);
printf("%d\n", static_value);
}
int main(void)
{
test();
return 0;
}
它们虽然都是 int,但通常存放在不同的内存区域。
局部变量通常放在栈中
c
int local_value = 20;
普通局部变量通常存放在"栈"中。
函数被调用时,系统为它准备一块栈空间,用于保存:
- 局部变量;
- 函数参数;
- 函数返回位置;
- 一部分临时计算数据。
函数结束后,这部分栈空间可以被后续函数重复使用。
全局变量在整个程序运行期间存在
c
int global_value = 10;
全局变量从程序开始运行,一直存在到程序结束。
它不会因为某个函数执行完毕而消失。
static变量也在整个程序运行期间存在
c
static int static_value = 30;
虽然它只能在 test 函数中通过名字访问,但它占用的内存在整个程序运行期间一直存在。
所以:
普通局部变量
作用域:函数内部
生命周期:本次函数调用
static局部变量
作用域:函数内部
生命周期:整个程序
全局变量
作用域:通常可以被多个函数访问
生命周期:整个程序
为什么算法要特别关注栈?
下面的数组:
c
float data[10000];
一个 float 占 4 字节,所以这个数组占:
10000 × 4 = 40000字节
也就是大约 40 KB。
如果它是普通局部变量:
c
void process(void)
{
float data[10000];
}
它通常会占用函数所在任务的栈。
在电脑程序中可能暂时没有问题,但在 MCU 和 FreeRTOS 中,每个任务的栈可能只有几 KB。40 KB 的局部数组很容易造成栈溢出。
栈溢出可能表现为:
- 程序突然跑飞;
- MCU进入 HardFault;
- 变量莫名其妙发生变化;
- 程序复位;
- 调试时行为不稳定。
因此大型雷达数组通常不会直接定义成大型局部变量,而会使用全局或静态数组,例如:
c
static float radar_buffer[10000];
或者
c
float radar_buffer[10000];
这样它们不会占用函数的临时栈空间。
其中:float data10000和float radar_buffer10000的区别
情况一:都写在函数内部
c
void process(void)
{
float data[10000];
float radar_buffer[10000];
}
这两个都是普通局部数组,通常都放在栈中:
c
data → 40000字节栈空间
radar_buffer → 40000字节栈空间
总计 → 80000字节栈空间
在这种情况下,两者确实没有区别,只是名字不同。
情况二:写在所有函数外面
c
float radar_buffer[10000];
void process(void)
{
float data[10000];
}
这时不同:
c
float radar_buffer[10000];
在函数外定义,是全局数组。它在整个程序运行期间一直存在,不占用 process 函数的栈。
而:
c
float data[10000];
定义在函数内部,是普通局部数组,通常占用函数的栈。
可以理解成:
c
全局区域:
radar_buffer[10000] 约40000字节
process函数的栈:
data[10000] 约40000字节
情况三:函数内部加static
c
void process(void)
{
static float radar_buffer[10000];
}
它虽然写在函数内部,但因为有 static,不会放在普通局部变量的栈空间中。
它的特点是:
- 只能在process函数中通过名字访问
- 整个程序运行期间一直存在
- 不随函数结束而消失
- 通常不占用任务栈
关于局部变量、全局变量和 static 变量在没有手动赋值时,行为也有所不同:
如下代码:
c
#include <stdio.h>
int global_value;
static int file_static_value;
void test(void)
{
int local_value;
static int local_static_value;
printf("global_value = %d\n", global_value);
printf("file_static_value = %d\n", file_static_value);
printf("local_static_value = %d\n", local_static_value);
/*
不要直接读取local_value,
因为它还没有被初始化。
*/
}
int main(void)
{
test();
return 0;
}
- 全局变量自动初始化为0
c
int global_value;
没有手动赋值,但程序启动时会自动初始化为:0
- static变量自动初始化为0
c
static int file_static_value;
以及:
c
static int local_static_value;
没有手动赋值时,也会自动初始化为:0
普通局部变量不会自动初始化,它刚创建时的内容是不确定的,不能假设它是 0。
错误用法:
c
int local_value;
printf("%d\n", local_value);
它可能表现为:
- 打印一个随机数;
- 不同编译设置下结果不同;
- Debug 和 Release 结果不同;
- 引发难以定位的问题。
正确做法:
c
int local_value = 0;
数组也是一样。
普通局部数组:float data[100];
内容未初始化,不能直接当成全零数组使用。
如果希望初始化为零,可以写:float data[100] = {0};
静态数组:static float radar_buffer[100];
如果没有手动初始化,它会在程序启动时自动清零。
全局数组:float radar_buffer[100];
没有手动初始化时也会自动清零。