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

先看三个变量:

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

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

相关推荐
Zwawa2 小时前
两根线怎么挂住一堆设备:I2C 协议原理
c语言·单片机·嵌入式
武子康3 小时前
h3.c 的两个权重槽,怎样把内存压力换成 SSD 等待
c语言·人工智能·agent
§ꦿCFོ༉1 天前
嵌入式八股—C语言入门学习(一)
c语言·编程语言·嵌入式八股文
沐晓时光1 天前
C语言入门,字符串和字符串函数知识讲解
c语言·开发语言
智者知已应修善业1 天前
【51单片机实现从下到上,从上到下,从中间到两边,两边到中间】2024-10-23
c语言·经验分享·笔记·单片机·嵌入式硬件·51单片机
布莱克6052 天前
C/C++ 中变量的声明与定义:区别、联系与常见误区
c语言·开发语言·c++·声明·定义
揽星河..2 天前
【C语言】底层程序设计
c语言·开发语言
揽星河..2 天前
【C语言】结构体
c语言·开发语言
唐青枫2 天前
C 语言 malloc() 详解与实战 Demo
c语言
揽星河..2 天前
【C语言】指针
c语言·开发语言