深入分析——为什么未初始化的全局变量是零?

1、前言

#include <stdio.h>

int temp;

int main(void)
{
	//打印temp的值是零
	printf("temp=%d\n", temp);

	return 0;
}
  • 在C语言编程中,我们默认未初始化的全局变量、静态局部变量的初始化值都是零,底层原理如下
    • 未初始化的全局变量、静态局部变量在链接时都属于bss、sbss段,会集中存放
    • 编译器默认添加的启动代码里把bss段都做了清零操作
  • 裸机开发时,如果你在编译时使用-nostdlib、-nostdinc、-T选项,不使用标准库、不使用标准头文件,自己编写汇编启动代码,如果自己添加的启动代码里没有对bss段进行清零,那未初始化的全局变量、静态局部变量的初始化值就不一定是零

2、查看sbss、bss段

在这里插入图片描述

3、查看默认链接脚本

在这里插入图片描述在这里插入图片描述

  • 使用–verbose选项查看默认链接脚本:riscv64-unknown-elf-ld --verbose > build.ld
  • 在链接脚本中可以看到sbss、bss段,两个段是紧挨着的。开始地址是__bss_start标号处,结束地址是__BSS_END__标号处

4、查看编译器默认添加的启动代码

在这里插入图片描述
在这里插入图片描述

  • 通过反汇编来查看编译器添加的启动代码。我们编写的代码是从main函数开始,main函数之前的代码都是编译器添加的,其中_start标号处是程序的入口,也就是执行的第一条代码

5、分析反汇编代码里的bss、sbss段

在这里插入图片描述

6、启动代码中初始化bss段

在这里插入图片描述

  • 编译器调用memset函数来把sbss、bss段的初始值格式化成零
  • memset函数需要传入三个参数:起始地址、格式化的值、格式化空间大小,分别是通过a0、a1、a2寄存器来传递,这是和芯片架构相关的,本文是基于riscv架构来分析,可参考博客:《RISC-V架构的函数调用规范和栈布局》
  • 指定sbss段的起始地址这里没有用__bss_start标号,而是使用temp变量的地址,__bss_start标号也是指向temp变量的地址,两种方式是等价的
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

正在起飞的蜗牛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值