深入理解C语言系列之内存和地址的故事(虚拟内存分区、全局变量与静态变量、外部函数内部函数、选择性编译)

一、内存的分区

1、内存分为:物理内存和虚拟内存。

  • 物理内存︰实实在在存储设备。
  • 虚拟内存:操作系统虚拟出来的内存。
    操作系统会在物理内存和虚拟内存之间做映射;在32位系统下,咱们看到的都是虚拟地址。

2、在32位操作系统中,虚拟内存被分为两个部分,3G的用户空间和1G内核空间,其中用户空间是当前进程所私有的,内核空间,是一个系统中所有的进程所公有的。

二、虚拟内存分区

1、堆区:在动态内存申请的时候,在堆里开辟内存。

2、栈区:主要存放局部变量。

3、静态全局区:1:未初始化的静态全局区
静态变量(定义变量的时候,前面加static修饰),或全局变量﹐没有初始化的,存在此区2:初始化的静态全局区全局变量、静态变量,赋过初值的,存放在此区。

4、代码区:存放程序代码。

5、文字常量区:存放常量的。

三、变量作用域与静态变量

1、普通全局变量: 在函数外(main函数和子函数)定义的变量。

int num= 5;
void main()
{
    
    
}

(1)作用范围:程序的所有地方,在其他文件使用之前需要声明:extern int num;,在当前c文件的函数中可以直接修改它的值。

(2)注意:声明的时候不要给全局变量赋值;定义时默认值为0。

(3)生命周期:程序运行的整个过程中,一直存在,直到程序结束。

2、静态全局变量: 在定义全局变量的时候,加一个static进行修饰,则static int num= 5;是一个静态全局变量。

(1)作用范围:静态全局变量被限定了作用范围,只能在其定义的c文件中进行调用,不能被 其他c文件引用。

(2)生命周期:与普通全局变量相同,存在于程序运行整个过程中。

3、普通局部变量: 在函数内(main或其他自定义函数)定义的,或者在复合语句中定义的变量。

void func()
{
    
    
	int num= 3;
}

(1)作用范围:在函数中定义的,只在函数内有效。在复合语句内定义的,只在复合语句内有效。

(2)生命周期:随函数开始和结束,在函数调用之前,局部变量不占用空间;调用时,临时开辟一块空间;调用结束后,开辟的空间被释放。

4、静态局部变量: 定义局部变量的时候,前面加static修饰。

(1)作用范围:在它定义的函数或复合语句中有效。

(2)生命周期::第一次调用函数的时候,开辟空间赋值,函数结束后,不释放,以后再调用函数的时候,就不再为其开辟空间,也不赋初值,用的是以前的那个变量。

(3)注意:

  • 普通局部变量不进行初始化,默认值是随机值;静态局部变量不进行初始化,默认值是0。

  • 静态局部变量只会初始化一次,直到在函数内修改它的值,它都不会变。

四、外部函数与内部函数

1、外部函数:定义的普通函数,都是外部函数。
函数可以在程序的任何一个文件中调用。只需要将函数的实现过程写在指定的.c文件中,然后将其声明写在指定的.h文件中,其他文件只要包含了头文件,就可以使用外部函数。

2、内部函数:也叫静态函数,在定义函数返回值的时候(也就是定义的函数前),前面加上static。它被限制了作用范围,只能在定义的c文件中引用。

3、内部函数和外部函数的区别:
外部函数,在所有地方都可以调用;
内部函数,只能在所定义的.c中的函数调用。

4、注意:在同一作用范围内,不允许变量重名。作用范围不同的可以重名。
局部范围内,重名的全局变量不起作用。(就近原则)
例如:

int i= 520;
void main()
{
    
    
int i= 1314;
	printf("%d",i);
}

此处就应该取最近的那个赋值,即1314,而不是远的那个520。

五、include与define选择性编译

1、C语言的编译过程:预处理——>编译——>汇编——>链接

(1)预编译
将.c中的头文件展开、宏展开生成的文件是.i文件

(2)编译
将预处理之后的.i文件生成.s 汇编文件

(3)汇编
将.s汇编文件生成.o目标文件

(4)链接
将.o 文件链接成目标文件
#includec<>:用尖括号包含头文件,在系统指定的路径下找头文件
#include"":用双引号包含头文件,先在当前目录下找头文件,找不到,再到系统指定的路径下找。

2、注意:

  • include常常是包含.h文件,不过也可以包含.c文件。但是最好不要包含.c,因为include包含的文件会在预编译被展开,如果一个c被包含多次,展开多次,会导致函数重复定义。

  • 预处理只会对include包含的文件进行处理,并不会进行语法检查,在第二个阶段即编译阶段才进行语法检查。

3、选择性编译
注意:选择性编译都是在预编译阶段干的事情。
(1)#ifdef

#ifdef BB
	代码段1
#else
	代码段2
#endif

如果在当前c文件定义过BB,则编译代码段1,否则编译代码段2。
与if else选择语句的区别:if else中的所有语句都会被编译,通过条件选择来执行代码,而选择性编译只有一块代码会被编译。
测试代码:

#include <stdio.h>
#define BB
int main()
{
    
    
	#ifdef BB
		printf("存在宏定义BB");
	#else
		printf("不存在BB");
	#endif
		return 0;	
}

(2)#ifndef
此外,还有个ifndef BB的用法,如果没有定义过BB的话则执行,与ifdef BB相反的用法。一般用于多文件编译。

(3)#if
另外还有:

#if 表达式
	代码段1
#else
	代码段2
#endif
	return 0;

如果表达式为真,则编译第一段代码,否则编译第二段代码。一般用于注释多行代码,使用#if 0

#include <stdio.h>
int mian()
{
    
    
	int a=1, b=3;
	#if a>b
		printf("这里可以放注释语句");
	#else
		printf("这这这");
	#endif
		return 0;
}

猜你喜欢

转载自blog.csdn.net/Viewinfinitely/article/details/109861603