目标:
- 初步了解关键字分类
- 深刻理解变量
- 深刻理解定义与声明
- auto关键字的理解
- 站在存储结构角度,理解register关键字
目录
1.关键字分类
关键字 | 说明 |
auto | 声明自动变量 |
short | 声明短整形变量或函数 |
int | 声明整形变量或函数 |
long | 声明长整型常量或函数 |
float | 声明浮点型变量或函数 |
double | 声明双精度变量或函数 |
char | 声明字符型变量或函数 |
struct | 声明结构体变量或函数 |
union | 声明共享数据类型 |
enum | 声明枚举类型 |
typedef | 用以给数据类型起别名 |
const | 声明只读变量 |
unsigined | 声明无符号变量或函数 |
signed | 声明符号变量或函数 |
extern | 声明变量是在其他文件中声明 |
register | 声明寄存器变量 |
static | 声明静态变量 |
volatile | 说明变量在程序执行中可被隐含地改变 |
void | 声明函数无返回值或无参数,声明无类型指针 |
if | 条件语句 |
else | 条件语句否定分支(与if连用) |
switch | 用于开关语句 |
case | 开关语句分支 |
for | 一种循环语句 |
do | 循环语句的循环体 |
while | 循环语句的循环条件 |
goto | 无条件跳转 |
continue | 结束当前循环,开始下一轮循环 |
break | 跳出当前循环 |
default | 开关语句中的 ‘其他’ 分支 |
sizeof | 计算数据类型长度 |
return | 子程序返回语句(可以带参数,也可以不带参数)循环条件 |
2.第一个C程序(补充内容)
#include<stdio.h>
#include<Windows.h>
/*在编译的时候,把文本代码 --> 可执行程序(二进制文件)->windows下双击可以执行文件*/
//编译执行 就会生成解决方案,清理解决方案 生成的那些文件就没了
int main()
{
printf("hello world\n");
system("pause");//如果用VS2013 不会自动停屏,需要用system防止程序一闪而过
return 0;
}
扩充知识:
1.我们写出来的代码是文本代码,而点击编译执行的时候,编译器会把文本代码转化为二进制文件
也就是.exe(可执行程序),而要找到这个文件,只需要打开当前文件夹下的Debug文件夹
如图:
而双击.exe就会运行刚才我们所写的代码:
如果点击清理解决方案,那么观察文件夹 发现什么都没了!
2.
- Windows当中,双击程序本质就是运行程序。那么运行程序的本质又是什么呢?
运行程序的本质就是将程序加载到内存当中
- 任何程序在运行之前都必须要先加载到内存当中
夺命二连问:
① 为什么程序在运行之前必须先加载到内存当中?
CPU和内存交互比较快,与硬盘交互慢--- 快!
(冯诺依曼硬件体系结构规定的必须先加载到内存中)
② 程序在被加载到内存之前,他在哪?
程序没有被加载的时候,在硬盘当中
这里需要用画图说明一下:
冯诺依曼体系结构:
3.变量的定义和声明
3.1 什么是变量
变量就是在内存中开辟特定大小的空间,用来保存数据
所有的变量,本质都是在内存中的某个位置开辟空间的!
那么变量可以在硬盘中开辟吗?
不可以!因为变量是程序运行的时候进行开辟的,而程序运行的时候,已经被加载到内存中了
所以变量一定是在内存中的!
而为什么是某个位置,因为我们是不知道的,是由操作系统随机分配的一个位置
3.2 怎么定义变量
- 定义变量的时候初始化
char c='a';
double d=3.14;
这种定义方式是开辟空间的时候,所开辟的空间的里直接放入内容(变量与生俱来的)
- 定义好变量,然后再赋值
char c;
c='d';
这种定义方式是先开辟号一个空间 也就是变量c
然后 把d放到已经存在的空间里面
这两种定义方式没有本质的区别,这里只有在C语言的结构体部分才会有一些明显差别
3.3 为什么要定义变量
首先我们要知道,计算机是为了解决人类计算能力不足而产生的,也就是计算机是为了
进行计算的。
而计算就需要数据,但是计算过程中并不是所有的数据都立马需要被计算,有一些数据是需要先保存起来等待后续的处理的,所以利用变量是为了暂时保存数据
硬盘中的数据被加载到内存当中之后,是一次加载了很多数据,但是CPU一次只会处理一个数据!所以没有被CPU处理的数据就需要暂时存储起来,以便后续的处理
3.4 变量定义与声明的差别
定义:开辟空间
声明:更多的是一种告知,告知编译器某个变量是已经被定义好的,不用考虑它是不是没有被定义,等到后面链接的时候,把所有内容和我的变量关联起来就好了
注意:
- 定义只能有一次 (只能开辟一次空间)
- 声明可以有多次
举个例子:表白只能表白一次,表白成功之后,你可以给多个人声明一下这是我的女朋友😛
4.最宽宏大量的关键字---auto关键字
4.1 变量的分类
局部变量:包含在代码块中的变量叫做局部变量。局部变量具有临时性。进入代码块,自动形成局部变量,退出代码块自动释放。(有很多说函数中的变量是局部变量,其实是不准确的)
全局变量:在所有函数之外定义的变量(不初始化默认为0),叫做全局变量。全局变量具有全局性
代码块:用{ }括起来的区域叫做代码块
更深入的: 关于程序地址空间和函数栈帧问题 会在后面具体讨论!
4.2 变量的作用域
局部变量的作用域:本代码块中{ } ,出作用域就会未定义标识符!
全局变量的作用域:整个程序运行期间都有效
4.3 变量的生命周期
生命周期概念:指的是变量从定义到释放的时间范围(强调的是时间),而作用域是指某个变量从什么地方开始有效到什么地方开始无效,强调的是空间。所谓的释放,指的是曾经开辟的空间“被释放”
局部变量:进入代码块,形成局部变量(开辟空间),退出代码块,“释放”局部变量
全局变量:定义完成之后,程序运行的整个生命周期内,该变量一直有效
4.4 作用域VS生命周期
区分生命周期和作用域!完全不同的概念
举个例子:
一个人从出生到死亡 ---生命周期
一个人对某一片区域做出贡献,产生影响 ----作用域
如果全局变量和局部变量名字相同---(冲突)
那么以局部变量优先的原则
如下图:
总结:
4.5 auto相关
如何使用?
一般在代码块中定义的变量,即局部变量,默认都是auto修饰的,不过一般省略
默认所有的变量都是auto吗?NO ! 一般用来修饰局部变量
还有 一般我们提到的局部变量,自动变量,临时变量 都是一回事,我们统称为局部变量
auto 一般加在局部变量的前面,用来修饰局部变量,意味着这个变量只在该代码块内有效,出代码块自动销毁,不过auto关键字很老,现在默认省略
而auto修饰全局变量 会出错!(不能修饰全局变量)
5. 最快的关键字-register
其实 CPU主要是负责进行计算的硬件单元,但是为了方便运算,一般第一步需要先把数据从内存
中读取到CPU里面,那么也就需要CPU有一定的数据临时存储能力。注意:CPU并不是当前要计算了才把特定的数据读到CPU里,那样太慢了
所以现代CPU,都集成了一组叫做寄存器的硬件,用来做临时数据的保存
5.1存储金字塔
5.3 寄存器存在的本质
在硬件层面:提高计算机的运算效率。因为不需要从内存里读取数据了!
寄存器实际上就是一种缓存技术,利用Cache局部性原理,提前读取内存中的内容加载到寄存器当中,这样再访问数据的时候,会提升很大的速度
百度云盘之类的云盘 也应用了 缓存技术
5.4 register修饰变量
尽量将所修饰变量,放入CPU寄存器中,从而达到提高效率的目的
那么什么样的变量,可以采用register呢?
1.局部的(全局变量会导致CPU寄存器被场所及见占用)
2.不会被写入的(写入就需要写回内存,后续还要读取检测的话,register的意义在哪呢?)
3.高频被读取的(提高效率所在)、
4.如果要使用,不要大量使用,因为寄存器的数量有限
注意:被寄存器关键字修饰的变量 没有办法取地址(因为没有地址)
只有内存中才会有地址,寄存器是在寄存器中的,已经找到了还需要寻址吗?
#include<stdio.h>
int main()
{
register int a=0;
printf("&a=%p\n",&a);
//编译器报错: 1 error c2103:寄存器上的变量"&"
//注意:并不是所有的编译器会报错
a=10;//寄存器变量可以被写入!只是register就没有意义了
return 0;
}
还有一个问题:如果寄存器定义的变量很多 会怎么办?
这里register 只是尽量把变量放到寄存器中,所以如果定义太多,他不会给你放进去,
并且编译器也会检查这个变量值不值得放到寄存器里。