《c语言修炼内功之第二种境界(看代码就是内存)之关键字系列一》
- 前言:这个系列内容我会深入讲解一下c语言中的重点内容,会把每一个知识点讲的更加底层些,会增强大家的c语言内功,从内存维度看代码你会有不同的理解。
- 第一节会比较简单,博主为以后复习专用,大家可以用来巩固基础进行学习
目录
从编译和内存角度理解程序的运行
- 我们以第一个c语言程序为例进行讲解
#include<stdio.h> #include<windows.h> int main() { printf("Hello,Worldn"); system("pause");//在vs2013中由于没有自动停屏(除了自己设置的情况下),我们用system("pause")来进行停屏 return 0; }
我们理解一下这段代码的编译。
首先我们通过文本代码通过编译链接运行出来的可执行程序,那这个可执行程序是什么呢?
这个可执行程序其实就是我们常说的二进制文件,而这个文件又叫做可执行文件(.exe文件),这个文件我们没编译一段代码都会生成这个可执行文件,而当我们清理解决方案的时候这个.exe文件就会被删除。
那接着我们怎样执行这个可执行文件的,一种是使用快捷键ctrl+f5,一种是双击鼠标,
那在windows系统中这种双击鼠标的本质是什么呢?
其实这种双击鼠标的实质就是在运行程序的时候,将程序加载到内存当中去。那我们启动程序的本质是什么呢?本质就是将数据加载到内存中,能够让计算机运行。而我们在执行程序之前这些数据放在哪里呢?由于这些程序都是数据,我们一般存储这些数据都放在硬盘中去,所以这些执行程序就是在硬盘中。好,最后一个问题,我们在加载程序时为什么要加载到内存中去?要说到这里就不得不提一下冯诺依曼体系(当然由于博主水平有限,只能讲的浅一些)
首先冯诺依曼体系有五个部件分别是存储器,运算器和控制器(这里统称cpu),输入设备,输出设备。
经过这个理解我们把数据加载到内存中才能更快的显示出数据。
变量的定义和声明
-
什么是变量
变量我们以前的理解是就是可以改变的量,而我们现在认识就不一样了,我们现在用内存的角度可以理解是在内存中的某个位置开辟的空间就是变量。
-
怎么样定义变量
我们一般定义变量,就是类型+变量名=默认值进行定义。
这里会有初始化和赋值的概念
这里的初始化内存为我们开辟空间,而这个空间就对应我们存放的数据。
赋值就是那个空间已经被开辟,我们把那个空间的数据要改成你要修改的值。
举个生活中的例子:比如智商高的人和智商普通的人最总都会变成很厉害的人
初始化就是这个人一出生智商就很高这就是与生俱来。(与生俱来就在那块空间里)
赋值就是一个智商一般的人通过自己不断的努力成为厉害的人。(通过被修改被存放在那块空间里)。
-
为什么要定义变量呢?
由于计算机要进行计算,但是由于数据太多,得一个个进行处理,所以这些数据就会被暂时存放,等待被执行和计算,所以我们需要变量进行暂时存放数据 。
-
变量定义的本质
- 程序运行,需要加载到内存中
- 程序计算,需要使用变量
- 定义变量的本质:在内存中开辟一块空间,用来保存数据。(为何一定是内存:因为定义变量,也是程序逻辑的一部分,程序已经被加载到内存)
-
变量声明的本质
这里我们也应该学过函数的声明,就是声明有这个函数的存在,而这个函数的具体功能就是函数的定义了。
关键字auto
其实提到auto那就跟局部变量有关,我们接下来过一下局部变量,全局变量,生命周期及作用域的概念
局部变量是什么呢?先了解啥是代码块也就是我们经常写代码时候的{}花括号。
我们将在代码块内部的变量叫做局部变量。全局变量相反就是定义在代码块外部的变量,也就有全局性,全局都可以使用。
#include <stdio.h> #include <windows.h> int g_x = 100; //全局变量 int main() { int x = 10; //局部变量,main函数也是函数,也有代码块{} printf("x:%dn", x); system("pause"); return 0; }
作用域概念:指的是该变量的可以被正常访问的代码区域。int main() { int x = 10; if (x == 10){ int y = 20; printf("局部: x: %d, y: %dn", x, y);//y只能在本代码块内有效 } printf("局部: x: %d, y: %dn", x, y); //报错,y不能被访问 system("pause"); return 0; }
所以局部变量只在自己的代码块内部有效,而全局变量时全局有效。
#include <stdio.h> #include <windows.h> int g_x = 100; //全局变量 int main() { int g_x = 10; //局部变量,与全局同名 printf("g_x:%dn", g_x);//输出的是局部,也就是局部和全部同名的时候,优先局部。所以,强烈不建议这样用。 system("pause"); return 0; }
总结:当我们局部变量和全局变量同名时候先输出局部变量
生命周期概念:指的是该变量从定义到被释放的时间范围,所谓的释放,指的是曾经开辟的空间
”
被释放
“局部变量: 进入代码块,形成局部变量
[
开辟空间
]
,退出代码块,
"
释放
"
局部变量全局变量
:
定义完成之后,程序运行的整个生命周期内,该变量一直都有效
- 作用域指的是范围而生命周期指的是时间维度
- 局部变量的作用域在函数内部和内码快内部,而全局变量是在全局范围内,作用域是进入代码块和出代码块的范围,而生命周期是指在进入代码块变量的创建到出代码块变量被销毁的时间段。
auto使用:一般在代码块中定义的变量,即局部变量,默认都是
auto
修饰的,不过一般省略默认的所有变量都是
auto
吗?不是,一般用来修饰局部变量中断一下:后面我们所到的,局部变量,自动变量,临时变量,都是一回事。我们统称局部变量
#include <stdio.h>
#include <windows.h>
int main()
{
for (int i = 0; i < 10; i++) {
printf("i=%dn", i);
if (1) {
auto int j = 0; //自动变量
printf("before: j=%dn", j);
j += 1;
printf("after : j=%dn", j);
}
}system("pause");
return 0;
}
大家可以看一下运行结果·这段代码想告诉我们的是关于局部变量的生命周期和作用域
可知我们这个j定义在if的代码块内部,出了这个代码块就会被销毁所以一直是1,2交替打印,而i一直在for循环代码块内部没有出for循环的代码块因此不销毁。
-
register关键字
CPU
主要是负责进行计算的硬件单元,但是为了方便运算,一般第一步需要先把数据从内存读取到
CPU
内,那
CPU
具有一定的数据临时存储能力。注意:
CPU
并不是当前要计算了,才把特定数据读到
CPU
里面,那样
CPU
内,都集成了一组叫做寄存器的硬件,用来做临时数据的保存。
register的关键字作用就是将变量优先放入寄存器优先进行计算,但至于有没有放进去这个是未知的,这里的register只是个建议优先进行计算和处理。
这个图就是cpu内部图,但具体的细节这里就不再讲(太偏硬件了,博主学了继续补充)。
修饰变量
将所修饰变量,放入
CPU
寄存区中,从而达到提高效率的目的
register可以存放的变量1.
局部的
(
全局会导致
CPU
寄存器被长时间占用2.
不会被写入的
(
写入就需要写回内存,后续还要读取检测的话,
register
的意义在哪呢?
)3.
高频被读取的
(
提高效率所在
)4.
如果要使用,请不要大量使用,因为寄存器数量有限
还有一点需要注意
int main()
{
register int a = 0;
printf("&a = %pn", &a); //编译器报错:错误 1 error C2103: 寄存器变量上的“&”
//注意,这里不是所有的编译器都报错,目前我们的vs是报错的。
system("pause");
return 0;
}
这里利用register关键字修饰变量不能取地址,因为计算机已经默认他已经在寄存器中。
寄存器变量能够被写入但不能被取地址,编译器必须考虑寄存器被优化,到寄存器,在内存中是直接开辟空间的,所以不能取地址。(就是已经认为在寄存器中,所以内存以为他开辟好空间)。