ARM架构与C语言(韦东山)学习笔记(3)-一些关于局部变量、全局变量、栈和堆的疑问


一、全局变量和静态变量的使用时机

全局变量适用于多个函数或模块之间需要共享数据的情况。在定义全局变量时,需要考虑变量的命名规范、作用域和存储类型等因素,以确保变量的正确性和可读性。

静态变量适用于需要在函数调用之间保持数据的持久性的情况。静态变量的作用域为整个文件,可以在文件中的任何函数中使用,并且在程序运行期间只会被初始化一次,因此可以用来存储一些需要保持状态的数据。

对于多进程场合,是不适用于全局变量的。进程安全是指在多进程环境下,多个进程可以同时访问同一个资源,而不会发生竞争条件等问题。在多进程编程中,由于每个进程拥有独立的地址空间,因此全局变量在不同进程之间是不共享的,即每个进程都会拥有自己的一份全局变量副本。
这就导致了一个问题:如果多个进程同时访问同一个全局变量,每个进程都会操作自己的一份副本,这可能会导致数据不一致、竞争条件等问题,进而影响程序的正确性和稳定性。因此,使用全局变量来实现进程间通信和共享数据是不可靠的。为了解决这个问题,可以使用一些进程间通信的机制,如管道、消息队列、共享内存等,实现进程间数据的共享和通信。

二、局部变量为何有时要加static?加了static后,是否该变量就不在栈里了?

int add_val(volatile int v){
    static volatile int a=321;
    v=v+a;
    return v;
}

(1)这个局部变量a被static修饰,当我们需要在函数调用之间保留局部变量的值,或者需要在不同的函数之间共享某些数据,这时可以使用 static 关键字来声明静态局部变量。在函数内部定义的静态局部变量,它的生命周期不会因为函数的调用而结束,而是在程序运行期间一直存在。它的作用域仍然是在函数内部,但是由于它的生命周期比普通局部变量长,所以可以在多次函数调用之间保持其值不变。此外,静态局部变量的初始化只会在第一次函数调用时执行,以后的调用不会再次执行初始化操作,因此可以用来存储一些需要保持状态的数据。
(2)静态局部变量存储在静态数据区中,而非栈上。静态数据区是程序的一部分,用于存放全局变量、静态变量和静态常量等数据。它的生命周期与程序的运行周期相同,因此静态局部变量在程序运行期间一直存在。

三、KEIL编译代码空间占比

在这里插入图片描述
这段信息是程序的大小信息,其中包括以下四个部分,单位是字节:

Code:代码段的大小,指程序的可执行代码部分,包括函数、指令等。在这个例子中,代码段的大小为 30646。

RO-data:只读数据段的大小,指程序中的只读变量和常量等数据。在这个例子中,只读数据段的大小为 2806。

RW-data:读写数据段的大小,指程序中的可读写变量和数据。在这个例子中,读写数据段的大小为 2736。

ZI-data:未初始化数据段的大小,指程序中未初始化的可读写变量和数据。在这个例子中,未初始化数据段的大小为 2240。

四、递归过深会造成栈溢出吗?

递归过深可能会造成栈溢出。递归函数在每次调用时都会将当前的函数调用信息存储在栈中,当递归次数过多时,栈空间可能会被用尽,导致栈溢出。在大多数编程语言中,栈空间大小是有限制的,通常在几 MB 到几十 MB 之间。如果递归函数调用的次数超过了栈空间的大小,就会导致栈溢出。栈溢出可能会导致程序崩溃或者出现不可预测的行为。

五、FLASH里的代码段和数据段是什么?

在嵌入式系统中,程序通常存储在闪存(Flash)中。在闪存中,通常将程序分为代码段和数据段。
代码段是存放程序指令的区域,通常是只读的。代码段中存储的指令是被编译器翻译成机器码的程序代码,它们是在程序运行期间被处理器执行的。
数据段是存放程序中的有初始值的静态数据和全局变量的区域。数据段通常被分为只读数据段和可读写数据段两种。只读数据段通常包括程序中的常量、字符串等不可修改的数据;可读写数据段通常包括程序中的全局变量、静态变量等可读写的数据。
在程序执行时,程序代码和数据段都会被加载到RAM中。程序代码被加载到指令存储器中,数据段被加载到数据存储器中。程序执行过程中,指令存储器和数据存储器的交互以及数据在存储器中的读写操作都由处理器控制。

六、FLASH里的代码段和数据段可以复制到RAM里吗?

是的,程序中的代码段和数据段可以被复制到RAM中。这个过程通常被称为“代码复制”或“数据初始化”

代码复制的目的通常是为了提高程序的执行效率。由于闪存的读取速度较慢,将程序中的代码段复制到RAM中后,程序执行时就可以直接从RAM中读取指令,减少了从闪存中读取指令的次数,从而提高了程序的执行效率
数据初始化的目的是为了在程序启动时给静态数据和全局变量赋初值。由于闪存中存储的数据是只读的,因此在程序运行时无法对这些数据进行修改。为了能够对这些数据进行修改,需要先将这些数据复制到RAM中。在复制数据的过程中,可以给这些数据赋初值,以满足程序的需求。

七、while(1)和for(;; )

while(1)和for(;;)都是用来表示一个无限循环的语句。
while(1)的意思是当括号内的条件为真时,就一直执行循环内的语句,因为1是真值,所以这个循环会一直执行下去,直到程序被强制终止。
for(;;)的意思是没有循环条件,这个循环会一直执行下去,直到程序被强制终止。它的循环结构和while(1)是等价的,只是语法上稍微简洁一些。

八、#define pi 3.14和static flaot pi=3.14

#define pi 3.14是一个预处理器指令,它会在编译之前对程序中所有出现的pi进行替换,将其替换为3.14。这种方式定义的pi是一个常量,它的值在编译期间就已经确定了,因此在程序执行期间是不能修改它的值的。这种定义方式的优点是简单易懂,缺点是不支持类型检查,容易出现不同类型的数据之间的错误操作。例如,如果有一个函数的参数应该是一个float类型的数值,但是在调用函数时传入了一个pi,就会出现类型不匹配的错误。

static float pi=3.14是一个静态变量的定义方式,它定义了一个名为pi的静态变量,并将其初始化为3.14。这种定义方式的优点是可以支持类型检查,并且在程序执行期间可以修改这个变量的值。静态变量的作用域限定在定义它的函数或文件中,因此不同的函数或文件可以定义同名的静态变量而互不影响。这种定义方式的缺点是需要占用内存空间,因为静态变量的内存空间是在程序运行时分配的,并且在程序结束之前一直存在。

九、RTOS每个任务相当于函数吗?会有栈被释放吗?

在RTOS中,每个任务可以看作一个独立的函数,它具有自己的入口点和执行上下文,可以独立运行和管理。每个任务都有自己的栈空间,用于存储局部变量和函数调用栈等信息

当任务被创建时,RTOS会为它分配一块栈空间,用于存储局部变量和函数调用栈等信息。当任务执行完成后,该栈空间会被释放,以供其他任务使用。因此,每个任务都有自己的独立栈,不会出现栈混乱等问题。

当任务被挂起时,RTOS会将该任务的栈空间保存到内存中,以便下次恢复执行时可以继续使用该栈空间。当任务被删除时,RTOS会释放该任务的栈空间,以供其他任务使用。

十、待补充…

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值