标识符和预处理 day12
十一:标识符作用域和可见性规则
一:变量 (标识符)作用域:
局部作用域 //{ }所在的范围就是局部作用域
全局作用域(静态区) //不在任何一个{ }范围内
二:可见性:
标识符的可见性的规则:
1.先定义,后使用
2.同一作用域中,不能有同名标识符
3.在不同的作用域,同名标识符,相互之间没有影响
4.如果是不同的作用域,但是作用域之间存在嵌套关系,即内层的作用域的同名标识符,会屏蔽外层的作用域的同名标识符(就近原则)
int a = 50;//全局变量也要遵守就近原则void test_int()
{int a = 20;printf("3 a = %d\n",a);//不同作用域
}int main(int argc, const char *argv[])
{int a = 10;printf("1 a = %d\n",a);{int a = 30;printf("2 a = %d\n",a);//嵌套同名作用域,a = 30,就近原则;}return 0;
}
三:auto
auto int a
; //auto----表示说这是一个自动变量
//意味着这个变量的存储空间是开在栈区
//自动申请,自动释放般局部变量
//默认都是auto类别的,所以一般都不写
四:static
static int a;
//static修饰局部变量
//表示将该变量存储空间 开在全局区 (静态区)
//全局区的变量量—生命周期和(auto)局部变量的生命周期不一样
五:生命周期
auto
的局部变量的生命周期:是从定义处开始存在,到作用域结束,此时销毁,这段时间是它生命周期
static
的变量(静态变量)和全局变量的生命周期:从程序运行开始到程序结束
1.只会被初始化一次
2.变量具有继承性
3.不能用变量初始化,只能用常量初始化
4.全局变量也不能用变量初始化
#include<stdio.h>int c = 5;
int a = c;//4.全局变量也不能用变量初始化void test_int(void)
{int b = 30;static int a = b;//不能用变量初始化,只能用常量初始化static int a = 30;//把上两个注释删除后,程序成功编译,此时static int a = 30;只在第一次调用时执行初始化,之后的调用不再初始化!所以静态变量 a 初始为 30,之后每次调用都会自增并打印a++;printf("2 a = %d\n",a);return;
}int main(int argc, const char *argv[])
{int a = 10;printf("1 a = %d\n",a);int i = 0;for(i=0;i<10;i++)test_int();return 0;
}
六:register
寄存器
register int a
;//作用:表示’建议‘将变量a的空间放在寄存器中—以提高访问效率,
//最终有没有放进寄存器,看系统调度。
register int a = 10;printf("1 &a = *p\n",a);//register修饰的变量 不能& 取地址
七:extern
外部修饰
extern int a
//表示变量a不在当前文件,可能在存在于别的文件中
//应用场景多个文件的场合
//也可以修饰函数
一般用在函数声明中exterr int add(int)
#include<stdio.h>extern int add(int a,int b);
extern int sub(int a,int b);
extern int mul(int a,int b);
extern int div(int a,int b);//函数声明int main(int argc, const char *argv[])
{int a,b;scanf("%d%d",&a,&b);printf("add = %d\n",add(a,b));printf("sub = %d\n",sub(a,b));printf("mul = %d\n",mul(a,b));printf("div = %d\n",div(a,b));return 0;
}eg:gcc cal.c main.c //编译两个文件
注意:1.修饰的变量—要求是全局变量
2.修饰函数----一般用在函数声明中
八:static
修饰问题
static
限定修饰全局变量和函数,只能用于本文件中
static
修饰局部变量 //是把局部变量存放在静态区–延长了整个程序生命周期
修饰全局变量 //将全局变量的作用域,限定到本文件—别的文件不能引用(不能通过extern
访问)
//1.保护数据 2.防止命名冲突
修饰函数 //和全局变量的作用一样
static int sub(int a,int b)//此时main函数中,extern无法引用
{return a-b;
}
面试题:你知道static
吗,介绍一下static
答:是一个存储类别关键字,可以来修饰变量:1.局部变量,2.全局变量,(—解释1和2),也可以修饰函数,和修饰全局变量的作用一样。之后在举例说明
十二:预处理命令
一:各个阶段
预处理 将c程序中 预处理 命令执行 得到一个纯的c程序文件
编译 将预处理之后的c源程序文件 处理成汇编代码
汇编 将汇编代码翻译成机器代码 01010
链接 将我们写的代码和用到的别人写的代码(二进制形式)
链接到一起生成最终的可执行文件
gcc -E test.c -o test.i//预处理
gcc -S test.i -o test.s//编译
gcc -c test.c -o test.o//汇编
gcc test.o -o test.out//链接eg:test.c test.i test.o test.out test.s./test.out //运行
二:宏定义
1.用一个指定的标识符子(即名字)来代表一个字符串
#define 标识符 字符串
#define 宏名 宏值
说明:1.宏名-----自己定义一个标识符,符合标识符命名规则,一般建议写成大写与普通变量名区分
2.宏值-------表示宏名要代表的值,它本身只是一个文本字符串 (文本信息),与C语言中字符串常量不是一个概念文
3.预处理阶段------做的工作: 文本的原样替换用宏值替换宏名
4.宏定义 最后不写分号,除非需要
5.出现在" "的字符串中与宏名同名的标识符不会被替换、
6.宏的作用域 //从定义出开始,到文件结束处
7.宏函数不是函数,只是一个带参宏, 参数不会做类型检测,是文本原样替换 使用时,是用宏值代替宏名
#include<stdio.h>
#define N 20;int main(int argc, const char *argv[])
{int n = N;printf("n = %d\n",n);return 0;
}
三:带参数的宏定义
1.能加括号就加括号
#include<stdio.h>
#define ADD(a,b) (a)+(b)
#define MUL(a,b) (a)*(b)int main(int argc, const char *argv[])
{printf("result = %d\n",MUL(1+3,2+4));return 0;
}
四:取消宏定义
#define MAX(a,b) a>b?a:b
#undef MAX(a,b) a>b?a:b
五:文件包含
#include"文件名" //默认在当前位置下寻找,若没有,则区系统路径寻找
或#include<文件名> //在系统路径下寻找
将所包含的文件的内容,最终放到指令所在的文件 //本质:还是文本替换
文件名----格式没有要求 //C语言中,头文件一般是.h结尾
六:条件编译
形式一:如果定义了标识符 则将程序段1替换到最终的代码中,如果没有定义了标识符 则将 程序段2替换到终的代码中
#ifdef标识符程序段1
#else标识符程序段2
#endif------------------------------------------
#define N
int main(int argc, const char *argv[])
{
#ifdef Nprintf("yes\n");
#else printf("no\n");
#endifreturn 0;
}
形式二:逻辑与形式一正好相反
#ifndef标识符程序段1
#else标识符程序段2
#endif
形式三:表达式为真则将程序段1替换到最终的代码中,表达式为假则将程序段2替换到最终的代码中,
#if 表达式(逻辑判断)程序段1
#else程序段2
#endif
条件编译可以解决头文件重复的问题
main.c文件
#include<stdio.h>
#include"add.h"
#include"add.h"//会二次定义,但是有条件编译防止问题int main(int argc, const char *argv[])
{int a = 1;int b = 2; printf("add = %d\n",add(a,b));return 0;
}
-------------------------------
add.c文件
int add(int a,int b)
{return a+b;
}
-----------------
add.h文件
#ifndef _ADD_H_//当第一次#include ,定义了add
#define _ADD_H_//当第二次#include时,由于已经定义了一次,所以不会在第二次定义//条件编译防止头文件重复的功能
int add(int a,int b);#endif