再看这篇博文之前可以先看下我另一篇博文:linux内存映像分配
参考:
1.C语言 子函数return局部变量和栈地址 机制
2.嵌入式linux C语言程序设计(书)
3.什么是内部链接和外部链接,什么是空链接
下面的程序以运行的情况说明:
#define <stdio.h>
#define SUM hello //字符串常量"hello"保存在.rodata段
int a1;//未初始化全局非stati静态存储类型,存放在.bss段(全局非static静态变量编译器在编译时会被初始化为0)
int a2=0;//已初始化全局非static静态存储类型,存放在.bss段
int a3=1;//已初始化全局非static静态存储类型,存放在.data段(只要a3不等于0)
static int b1;//未初始化全局static静态存储类型,存放在.bss段(全部static静态变量编译器在编译时会被初始化为0)
static int b2=0;//已初始化全局static静态存储类型,存放在.bss段
static int b3=1;//已初始化全局static静态存储类型,存放在.data段
[const] char *str="hello1";//只读字符串常量"hello1"存放在.rodata段,指针变量str存放在.data段,不可以通过指针修改值
char array[4]={'a','b'};//部分初始化全局非static静态存储类型,array[0]、array[1]存放在.data段,array[2]、array[3]存放在.bss段
const int c;//全局只读静态存储类型,存放在.rodata段
int func(int a)//局部变量a,自动存储类型,存放在.stack段
{
int b=a;//局部变量a,自动存储类型,存放在.stack段
return b;
}
int main(void)
{
[auto] char array1[4];//局部自动存储类型,四个数组元素存放在.stack段
[auto] int d;//局部自动存储类型,存放在.stack段
static int e1;//未初始化局部stati静态存储类型,存放在.bss段(局部static静态变量编译器在编译时会初始化为0)
static int e2=0;//已初始化局部stati静态存储类型,存放在.bss段
static int e3=1;//已初始化局部stati静态存储类型,存放在.data段
[auto] char *str1="hello2";//只读字符串常量"hello2"存放在.rodata段,指针变量str存放在.stack段
const int f;//局部静态存储类型,存放在.stack段,可以通过指针修改值
printf("hello3\n");//字符串常量"hello3\n"保存在.rodata段
}
说明:
- 在调用函数时第一个进栈的是函数调用语句的下一条可执行语句的地址,然后是函数的各个参数,在大多数的c语言编译器中,参数是由右往左入栈的,然后是函数中的局部变量。当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始的存储地址,也就是调用该函数处的下一条指令,程序由该点继续运行。
- Linux等的C语言中return返回值的机制为:将返回值存入eax寄存器中,然后系统再将eax中的值赋给变量。
总结: - 什么是代码块? 由“
{ }
”括起来的代码。常见的代码块是函数。 - 什么是源文件?源文件即源代码文件,c语言源文件后缀名为
.c
文件。 - 什么是局部变量?函数内部定义的变量。
- 什么是全局变量?函数外部定义的变量。
- 局部变量
a
与全局变量a
之前的关系:在局部变量a
的作用域内,全局变量a
不起作用。 - 外部链接:外部链接的变量可以在多个文件中使用;
- 内部链接:内部链接的变量只能在一个文件中使用;
- 空链接:由定义变量的代码块作用域所私有;
- 全局变量前面加关键字
static
就是内部链接,表明该变量为定义该变量的文件私有。 - 对于全局变量和
static
静态变量,如果在定义时未初始化,编译器会将变量值赋予0,保存在.bss段,即使将变量初始为0,还是会保存在.bss段,除非初始化为非0的数。 - 在一个代码块内(或在一个函数头部作为参量)使用修饰符
register
声明的变量属于寄存器存储类型。register
修饰符暗示编译程序相应的变量将被频繁使用,如果可能的话,应将其保存在CPU
的寄存器中,从而加快其存取速度。该寄存器存储类型与自动类型auto
相似(动态存储期,代码块作用域和空链接)。register
类型的变量未初始化的话值是不确定。 - 使用register修饰符注意一下几个事项:
1)register
变量必须是能被CPU
寄存器所接收的类型,这通常意味着register
变量必须是一个单个的值,并且其长度应小于或等于整型的长度。这与处理器的类型有关。
2)声明为register
仅仅是一个请求,而非命令,因此变量仍然可能是普通的自动变量,没有放在寄存器中。
3)由于变量有可能存储在寄存器中,因此不能用取地址运算符"&
"获取register
变量的地址。编译器会报错。
4)只有局部变量和形参可以作为register
变量,全局变量不行。
5)实际上有些系统并不把register
变量存放在寄存器中,而优化的编译系统可以自动识别使用频繁的变量而把它们放在寄存器中。
- 全局变量的定义与声明
1)全局变量的定义必须在所有的函数之外,且只能定义一次,而全局变量的声明出现在要使用的该变量的各个函数或者要使用的源文件内,可能出现多次。
2)全局变量在定义时就已分配内存单元,并且可做初始赋值。全局变量声明不能再赋初始值,只是表明在函数内要使用某外部变量。
- 对于全局变量的声明可以加上
extern
标识,同样对于函数的声明,也可以使用extern
。如果函数的声明中带有关键字extern
,是告诉编译器这个函数是在别的源文件里定义的。若全局变量前有static修饰,则变量只能在当前文件中被使用,同样若函数的声明前面带有static
限制,是告诉编译器,该函数只能在当前文件中被调用,外部文件不能调用。