变量
1. 变量是什么?
变量是内存中一块用来存放数据的空间。所以定义变量的本质是向内存申请一块空间用来存放数据。
2. 变量的初始化和赋值
#include<stdio.h>
int main()
{
int a = 10; //初始化:只能初始化一次,因为该空间只开辟一次。
a = 20; //赋值:可以多次重复赋值。
return 0;
}
3. 为什么要定义变量
计算机的出现的根本是为了解决人的计算能力不足,那么在计算机计算的时候就需要处理大量的数据。但是计算机也是一步一步计算数据的,那么数据的计算肯定有先后之分,并且为了保证在计算数据的时候能够方便的拿到数据,计算机会把数据放到内存中使用。总结:因为有数据需要暂时的被存储起来,所以我们需要用变量来保存它来等待计算机对它进行后续处理。
4. 变量的生命周期
- 生命周期概念:指该变量从定义到被释放的时间范围,所谓的释放,指的是内存中空间的“释放”。
- 局部变量:进入代码块,产生局部变量(会开辟空间),退出代码块,销毁局部变量(释放空间)。
- 全局变量:定义完成之后,程序运行的整个生命周期内,该变量都一直有效。
- 变量的作用域是“空间”上的概念,生命周期是变量的“时间”上的概念。
一、auto
- 使用:一般在代码块中定义的变量,即局部变量,默认都是auto修饰的,不过一般都是省略。
#include<stdio.h>
//auto int i = 100;
//全局变量,不能使用 auto 关键字
int main()
{
auto int a = 10;//局部变量,可以使用。
for (auto int i = 0; i < 10; i++)
{ // 临时变量,也可以使用。
;
}
return 0;
}
- 默认所有的变量都是auto修饰吗?
答:不是,一般只是用来修饰局部变量(局部变量、自动变量、临时变量都差不多,我们统称为“局部变量”) - 总结:auto 关键字太老了,现在基本不用,可以不写。
二、register
- 基本知识普及
- 计算机的存储结构是采用“存储分级”的形式来对我们的存储单元进行组织的。CPU是主要用来进行计算的硬件单元。为了方便运算,一般第一步需要先把数据从内存中读取到CPU内,那么这就要求CPU需要具有一定的数据临时存储能力,所以,在现代CPU内,都集成了一组叫做“寄存器”的硬件,用来做临时数据的保存 。
- CPU会在执行计算操作前预先把特定数据读取到CPU内,提高计算机运算效率。
暂时无法在飞书文档外展示此内容
- 作用
当你使用register来操作一个变量的时候,你只是在向编译器提出“建议”,把这个变量放到CPU的寄存区中,而编译器也尽量将变量放到CPU寄存区中,用来以提高计算效率 。注意,并不是用了register关键字,编译器就一定会帮你把你定义的变量放到CPU的寄存区中去,而是取决于编译器本身。另外,register并不会影响变量的生命周期 。 - 适用范围
- 局部的变量。(全局变量会导致CPU寄存器被长时间占用)
- 不会被写入的变量。(写入就意味着要写回内存,后序还要读取检测的话,register就没有意义了)
暂时无法在飞书文档外展示此内容 - 高频被读取的变量。(可以节省大量时间)
- 不能大量使用,寄存器的数量是有限的。
- 被rigister修饰的变量不能够取地址(此时的变量是存放在寄存区中的,而地址是找到的内存空间),但是可以修改变量的值。
#include<stdio.h>
int main()
{
register int a = 10;
//示例一
printf("&a = %p\n",&a);//err
//VS2022会报错:error C2103: 寄存器变量上的“&”
//当然,不是所有的编译器都会报错,这个后续再补充。
//示例二
a = 20;//ok
printf("%a = d\n",a);// a = 20;
return 0;
}
- 总结:早期的编译器需要人为指定register来进行手动优化,不过现在编译器很智能,能够进行比人更好的代码优化。所以,不用管该关键字 。
多文件
- main.c
#include<stdio.h>
extern int g_val;
//这是个声明,表明引用的是其他文件(test.c)的变量。
extern int g-val = 200;
//这种情况就不是声明,而是在定义一个整型全局变量g-val,并且赋初值为200。
//VS2022实测报警:或上一个增量链接没有生成它;正在执行完全链接test.obj : error LNK2005: _g_val 已经在 main.obj 中定义
//这表明程序在执行链接时出错。
//警告:fatal error LNK1169: 找到一个或多个多重定义的符号
//因为g_val这个变量在test.c和main.c两个文件内都定义了,所以是“重定义”
int main()
{
show();//使用其他文件内的函数
//在不进行声明的情况下也可以运行。但是在有些编译器上会报警告;
//用VS2022测试: warning C4013: “show”未定义;假设外部返回 int
return 0;
}
- test.c
#include<stdio.h>
//因为此文件中使用了printf函数,所以要包含对应的头文件
//函数,可以视作全局的
void show()
{
printf("Hello World!");
}
int g_val = 10;//全局变量
当我们写一个大的项目的时候,会用到许多自己写的函数,这个时候再使用这种extern??的方法未免太过麻烦,所以就诞生了“头文件”。
3. main.h
#pragma once
//头文件中应该放的内容:
//1. C头文件
//2. 所有变量的声明
//3. 所有函数的声明
//4. #define、类型typedef还有struct
extern int g_val; //变量声明必须带上extern
extern void show(); //函数声明建议带上extern
好,总结一下为什么要有头文件:
- 单纯的使用源文件来组织项目结构的时候,随着项目越来越大、越来越复杂的时候,维护成本就会越来越高。所以使用头文件来组织项目结构,就可以有效的减少大型项目的维护成本问题。
- 但是这也会造成另一个问题:头文件被重复包含的问题。要解决这个问题,方案一就是在头文件中写上:#pragma once就能避免头文件被多次包含的问题。