内存区域:栈、全局区和静态区

先看三个变量:

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;
}
  1. 全局变量自动初始化为0
c 复制代码
int global_value;

没有手动赋值,但程序启动时会自动初始化为:0

  1. 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];

没有手动初始化时也会自动清零。

相关推荐
头像很酷1 小时前
嵌入式虚拟化-xvisor-跨越边界的读客
c语言·hypervisor·xvisor·嵌入式虚拟化
Logic1012 小时前
C语言/数据结构二维数组题解:找出行列O个数均为偶数的格子——奇偶性预处理+二次遍历
c语言·数据结构·算法·二维数组·时间复杂度·动态内存分配·奇偶性
David猪大卫3 小时前
【C++修炼】string类:string类的模拟实现
c语言·开发语言·数据结构·c++·经验分享·笔记·学习
Lxx_Zw3 小时前
先把轮子转稳,才不会烧心:编码器速度环 PID 怎么接回 PWM
c语言·stm32·单片机·嵌入式硬件
头像很酷3 小时前
嵌入式虚拟化-xvisor-物理内存大管家
c语言·hypervisor·xvisor·嵌入式虚拟化
嵌入式学习菌4 小时前
Modbus 透传和 Modbus 转发区别
c语言
jeo陽4 小时前
05-数组与冒泡排序:第一个数据结构与经典算法
c语言·数据结构·算法
水饺编程4 小时前
第2章:Linux 基础,mkdir 命令的简单入门
linux·c语言·汇编
燕落南枝5 小时前
C 语言学习大总结:从入门到进阶的完整知识体系
服务器·c语言·学习