标识符的作用域 与 可见性问题:
说明:
1.变量名
2.函数名
3.数组名
作用域:
发挥作用区域
{
} //C语言中 一个 {} 就是一个作用域
作用域:
局部作用域
只要是在{} 范围内的 都是局部作用域
局部作用域定义的变量 --- 局部变量
全局作用域
不在任何一个 {} 范围内的,是全局作用域
全局作用域定义的变量 --- 全局变量
可见性:
站在程序角度看,程序运行到某一刻时,哪些标识符可见(可用)
标识符的可见性的规则:
1.先定义,后使用
2.同一作用域中,不能有同名标识符
3.在不同的作用域,同名标识符,相互之间没有影响
4.如果是不同的作用域,但是作用域之间存在嵌套关系, 则,内层的作用域的同名标识符,会屏蔽外层的作用域的同名标识符。 (就近原则)
C语言5个区:
栈
堆
全局区(静态区)
字符串常量区
代码区
存储类别关键字:
main()
{
int a = 10; //局部变量 --- 定义变量 意味着开空间 --- 空间开在哪里?
}
指定关键字 来表示 数据可以放到哪来
auto int a = 10; //auto 就表示这个变量空间开在栈上
static int a = 10; //static 静态的 表示将变量 空间 开在 静态区
//静态区变量的特点:
// 1.只会被初始化一次
// 2.不能用变量初始化
// 全局变量也不能用变量初始化 --- 全局变量和静态变量,都是放在全局区(静态区)
// 3.静态局部变量的值,具有继承性
从变量角度看,
空间上:
局部变量
全局变量
时间上:
生命周期
局部变量 的生命周期 ---从定义处开始,到所在作用域结束
//程序执行到定义时,变量创建,程序执行到 作用域结束 变量销毁
全局变量
静态变量 的生命周期 ---程序运行开始后,就存在,直到整个程序结束,才销毁
./a.out
(整个程序的生命周期)
register int a = 10; //register --- 寄存器
//告诉编译器 ,将变量a 存放到 寄存器中
//关键字 ---- 建议性关键字
//考点
// 如果是register修饰的变量
// 不能做 &(取地址)运算
// 原因 --- 内存空间可以取地址,寄存器取不了
拓展:
计算机的存储体系:
寄存器
cache
内存
硬盘
从上到下,容量越来越大,价格越来越便宜,速度原来越慢
CPU + 内存 + 硬盘
unsigned int i = 0;
for (i = 0; i < INT_MAX; ++i)
{
}
//声明
extern int a; //extern --- 外部的意思 --- extern相当于是扩展了变量的作用域,
//相当于将a的作用域扩展到本文件
//修饰表示,变量存储在别的文件中,要使用式到别的文件中寻找
extern 和 static:
static
也可以修饰全局变量,表示 将该变量的作用域,限定为本文件
别的文件中无法引用(extern int a;) //static修饰全局变量 --- 限定了作用域
也可以修饰函数
//多文件编程
作用和修饰全局变量作用相同
用途:分模块
好多个函数
全局变量 int cnt;
函数
static
保护数据作用
关键字:
auto
static //背诵的第一个面试问题
// 你知道static吗?
// 解释
register
extern
预处理命令:
#include <stdio.h> //预处理命令
C提供的预处理功能主要有以下3种:
1. 宏定义
2. 文件包含
3. 条件编译
注意:
1.所有的预处理命令 都 以 # (井号)开头
编译:
gcc hello.c //编译
实际的编译过程:
预处理:
将程序中的预处理命令,执行完,将代码处理成 纯的C语言代码
编译:
将预处理完的文件,进行语法,词法分析,检查问题
生成 一份 汇编代码
汇编:
将 前面的汇编代码,最终翻译成机器代码
链接:
将用到的第三方代码 链接(拼接)成为 最终可执行程序
预处理:
gcc -E hello.c -o hello.i
编译:
gcc -S hello.i -o hello.s
汇编:
gcc -c hello.s -o hello.o
链接:
gcc hello.o -o hello
1. 宏定义
#define 标识符 字符串
#define 宏名 宏值
作用:
用宏名代替 一串文本信息
注意:
1. 宏名 --本质还是一个标识符
2. 宏名 建议 大写
3. 宏名 --符号常量
用了一个 符号(宏名) 代替 常量值
4. 宏工作的本质
文本原样替换
但是:
代码中出现在 "" (双引号)中的 宏名 ,不会被替换
5. 用途
可以用宏名 来代替一些常量
符号常量
好处,可以做到一改全改
可以提高代码可读性
#define ARRAY_SIZE 10
int array[ARRAY_SIZE]
练习:
计算一下 圆的周长和面积
带参宏:
#define 宏名(参数表) 宏值
说明:
1.带参宏 ,可以 给一些参数
形式 特别像函数调用
宏函数
2.带参宏 和 函数有本质的区别
a.函数的参数 是带类型的
b.带参宏 参数,没有类型 --就是文本
c.函数什么时候可以用?
程序运行之后
d.带参宏
在预处理阶段
就处理完了
3.带参宏 工作过程
本质上还是文本原样替换
参数是一一对应的
4.带参宏副作用
建议, 能加括号都加括号可能会改变的值,我们建议都加括号
5.所有宏定义
以换行作为结束符
6.宏的作用域
从定义出开始
往下
都发挥作用
取消宏的作用域
#undef 宏名
eg:
两个数相乘
int mul(int a,int b)
{
return a * b;
}
#define MUL(a,b) a*b
MUL(1+2,3+4) 1+2*3+4
#define MUL(a,b) ((a)*(b))
练习:
实现一个宏,找出两个数的最大值
[预处理]:
1.预处理 不是 c语言的一部分
2.预处理 命令 都是以 # 开头
3.宏定义
工作的过程 (本质)
文本替换
4.文件包含
5.条件编译
预处理---本质上都是做做文本替换
文件包含:
#include <文件名> //默认到系统的标准路径下寻找要包含的头文件
#include "文件名" //双引号 默认先到当前位置下 寻找要包含的头文件,如果找到,则包含进来
// 如果没找到,才到系统的标准路径下寻找
#include <stdio.h>
说明:
1. include 这个预处理命令执行之后,
用文件中的内容 ,替换这个 命令所在位置
2. 将来使用
<> //如果是系统的相关头文件 --- 用 <>
"" //如果是自己写的 一般用 ""
条件编译:
#if 0
......
#endif
(1)
#ifdef 标识符
程序段1
#else
程序段2
#endif
语义:
判断 标识符 是否定义了?
如果定义了 ---真
选择 程序段1
如果没定义 ---假
选择 程序段2
#ifdef 标识符
程序段1
#endif
满足条件的时候 ,添加 程序段1
不满足条件时候,啥都没有
(2).
#ifndef 标识符
程序段1
#else
程序段2
#endif
语义:
没有定义 标识符 则选择程序段1
有定义 标识符 则选择程序段
(3).
#if 表达式
程序段1
#else
程序段2
#endif
#if 表达式
程序段
#endif
#if 0
......
#endif
用途:
可以用来实现 多文件编程
printf //系统里面的函数
#include <stdio.h>
printf();
实现好的功能 .c 独立的c程序提供 或者 打包成库文件
.c //写好了功能的点c文件
.c 对应 有一个 .h (头文件)
.c --- 就是实现功能的具体代码 ---函数
.h --- 头文件 --放的是 自身需要用到 宏 类型说明 函数声明 //变量的定义 不能放头文件
//全局的
int a = 10; //a一定得开空间
extern int a; //
说明:
头文件包含
注意 --- 一定要写
#ifndef 头文件名 //要大写 前后 下划线
#define 头文件名
#endif
命令:
vsp //竖着分屏
sp
窗口之间切换:
ctrl + 两下w
编译:
所有 .c 一起编译
gcc main.c isLeapYear.c getAllDays.c .....//这些需要一起编译