目录
1、内存分区
运行中的内存分区:
分区 | 读写特性 | 变量存放 | 内否取地址 | 变量初始值 |
堆区 | 可读可写 | 必须使用库函数malloc、calloc、realloc动态申请,使用完成后free手动释放 | 不能 | 内容不确定 |
栈区 | 可读可写 | 局部变量、函数形参、返回值>4B | 能 | 内容不确定 |
全局区 | 可读可写 | 全局变量、静态局部变量、静态全局变量 | 能 | 0 |
文字常量区 | 只读 | 字符常量、字符串常量、符号常量等 | 不能 | --- |
代码区 | 只读 | 代码的二进制指令 | 不能 | --- |
综上表,用户定义的变量可以存储在堆区、栈区、全局区和文字常量区。
下面,我们就看看这些变量分别有什么特点。
2、变量的分类
2.1 普通局部变量
定义方式:定义在{ } 中的变量为普通局部变量,且不能被static修饰
作用范围:当前的{ }内有效
生命周期:当前{ }结束,局部变量释放
存储区域:栈区
不初始化问题:不初始化内容不确定
总结:
- 普通局部变量不初始化内容不确定。
- 复合语句结束,普通局部变量被释放
- 局部变量如果同名情况,采用就近原则
2.2 普通全局变量
定义方式:在函数外定义的变量,且不能被static修饰
作用范围:当前源文件和其他源文件都有效(其他源文件使用需要extern声明)
生命周期:整个进程,进程结束,普通全局变量释放
存储区域:全局区
不初始化问题:不初始化内容为0
总结:
- 普通全局变量不初始化内容为0
- 如果其他源文件想使用普通全局变量,需要用extern声明
2.3 静态局部变量
定义方式:定义在{ } 中定义,且被static修饰
作用范围:当前的{ }内有效
生命周期:整个进程,进程结束,静态局部变量释放
存储区域:全局区
不初始化问题:不初始化内容为0
总结:
- 静态局部变量不初始化内容为0
- 整个进程有效,进程结束,静态局部变量才被释放
2.4 静态全局变量
定义方式:定义在函数外,且被static修饰
作用范围:只能在当前源文件使用
生命周期:整个进程,进程结束,静态全局变量释放
存储区域:全局区
不初始化问题:不初始化内容为0
总结:
- 静态局部变量不初始化内容为0
- 只能在当前源文件使用
2.5 总结
普通局部变量,定义在栈区,不初始化内容不确定,当前{ }有效,{ }结束被释放
普通全局变量,存储在全局区,不初始化内容为0,当前源文件和其他源文件有效,进程结束被释放
静态局部变量,存储在全局区,不初始化内容为0,当前{ }有效,进程结束被释放
静态全局变量,存储在全局区,不初始化内容为0,当前源文件有效,进程结束被释放
3、函数的分类
3.1 全局函数
全局函数可以被其他源文件调用,C语言默认都是全局函数。
其他源文件调用时需要外部声明extern修饰
3.2 静态函数
静态函数只能在当前源文件使用,不能在其他源文件使用,即使是外部声明了也不可以
4、C语言编译过程
C语言编译分为四个过程:
①预处理:头文件包含、宏替换、删除注释、条件编译。不做语法检查。
gcc -E main.c -o main.i
②编译:做语法、词法检查,然后生成汇编文件。
gcc -S main.i -o main.s
③汇编:将汇编文件生成二进制文件。
gcc -c main.s -o main.o
④链接:结合二进制文件、所需要的库、启动代码生成可执行文件
gcc main.o -o main
在编译时也可以一步到位:
gcc main.c
或者
gcc main.c -o main
4.1 预编译:头文件包含
头文件包含一般会用到#include <>或者#include " "。
问:#include <>或者#include " "的区别?
答:#include < >:直接从系统指定的目录下寻找头文件,一般在包含系统自带的头文件时使用。
#include " ":先从本地目录下寻找,如果找不到,再从系统指定目录下寻找。一般在包含用户自定义的头文件时使用。
问:什么是头文件 ,头文件里面包含什么?
答:C语言中头文件一般是 .h 文件。
头文件中包含变量的声明,函数的声明,类型定义,宏定义等。
头文件包含:本质就是将头文件(.h文件)中的内容复制到#include的位置上。
注:编译时不需要将.h文件一起编译,只需要编译.c文件即可
4.2 预编译:宏替换
宏的定义,就是使用#define关键字将一个固定的词替换成另一个词,在预编译个过程中,会自动展开。
注:在定义宏时,最后不加 ; 号
4.2.1 不带参数的宏
格式:#define 宏名称 要替换的名称
4.2.2 带参数的宏(宏函数)
宏函数本质是宏,而不是函数。
格式:#define 宏名(参数) 表达式
注意,注意,注意:宏函数在替换时就是单纯的替换,不会进行数据操作!
4.2.3 宏函数和普通函数的区别
宏函数:本质是宏,在预编译阶段展开;没有出入栈的开销;参数没有类型,不能保证参数完整性;如果使用次数多占用内存就大,用空间换时间。
函数:有出入栈的开销;参数有类型,可以保证参数的完整性,占用内存固定,用时间换空间。
5、预编译:条件编译
5.1 定义形式:
第一种:如果XXX为真,则编译语句1,否则编译语句2.
#if XXX
语句1
#else
语句2
#endif
第二种:如果定义了XXX,则编译语句1,否则编译语句2
#ifdef XXX
语句1
#else
语句2
#endif
第三种:如果没有定义XXX,则编译语句1,否则编译语句2
#ifndef XXX
语句1
#else
语句2
#endif
注:条件编译实在预编译阶段处理的,预编译阶段直接将不符合条件的语句过滤掉,被过滤的语句在接下来的编译过程中都不会进行处理,相当于被删除掉了。而 if...else...是条件判断语句,不管符不符合条件都会被编译。
5.2 防止有文件重复包含
linux(推荐):
#ifndef __BASE_H__
#define __BASE_H__
base.h头文件的内容
#endif
windows:
#pragma once
base.h头文件的内容