对于入门者来说,今天的内容可能有点小难,但大家别着急,慢慢看,慢慢来,仔细想想,动手敲敲就会发现其实并不难,对编程来说,一切的一切都不如实践。
Oh!突然发现我在这篇里面创建的文件是progect,我在写的时候都写成了project,请大家自行进行文本替换(hhhhh)
目录
1.关键字
1.1Typedef(类型重命名)
直接上图:
上图中,使用 typedef 将整型int重命名为A,即A就是int; A b 即int B
嗯,就这么简单,我们就讲这些,嘿嘿,直接进入下一个(有难度哦)
1.2Static
这是一个重点关键字(重点中的重点)
Static有三种作用
1.21修饰局部变量
可以发现,打印的是10个1,而不是1~10,正如我们在前面所说,test()里的i是局部变量,只在它所在的第一个{}内有效,出去后就会被销毁,即超出生命周期,原本i占用的内存被释放,第二次循环进入后又会重新进行i的初始化等功能,因此每次i都是1.
在这个图里面,显而易见发现输出结果为1~10,而与上个图,唯一的区别就是在int前加了static进行修饰,然后发现这个程序里面,i并不会被销毁,即计算机在i在跳出{}后仍然保存了它的数据,没有进行数据释放,因此在进入第二次循环时,i=1,而不像第一个程序中i还要重新开辟空间,也正是由于i已经有数据(说明已经进行过初始化),所以跳过后面的初始化(不懂为什么跳过可以先看下面的补充—初始化与赋值),直接执行i++,因此打印1~10
总结:
static 修饰局部变量,扩大了局部变量的生命周期,但并没有改变作用域(即i数据在每次调用之后不会被销毁,但仍然不能在i的{}外使用{}里的i)
补充:初始化与赋值:
初始化是与生俱来的,就像我们一出生就有了性别,且不会过一段时间再拥有一个性别,我们有且只有一个性别。i在一创建时便有了数据0,在该数据未回收删除时,再次遇到i的初始化会自动跳过,即初始化只能进行一次。
赋值就像我们存储的知识,会在生活中不断地进行删减变化,i在初始化之后,可以进行无数次赋值,而每一次赋值不过是调用i的空间,将要赋的值存入i的空间(对原值进行覆盖替换)。
1.22修饰全局变量和函数
引入:多文件操作
在具体分析这个之前,我们先了解一下多个源文件的操作场景
上图程序共建立了三个文件:一个头文件(project.h),两个源文件(project.c和main.c)
说明:
A:当头文件建立好后,会默认有一行代码 #pragma once,这行代码是为了防止头文件被重复引用,即每个源文件引用一次头文件,会影响程序运行效率,大大加大代码量(暂时知道它的功能即可)
B:当引用头文件时,把include后的<>换成 ” ”即可,” ”里面填头文件的名字(注意后缀也要写),在头文件里引用了include,声明了函数(注意中-函数的声明和定义),因此每个源文件只需要引用该头文件即可。
C:在main.c 中,我们调用了两个函数来实现加法和减法功能,我们先假设这两个功能已经实现,然后把main.c里面的逻辑写清楚(两个变量的设置和最后的打印)
D:现在我们已经写好main.c了,但我们调用的函数还没有定义,我们在另一个源文件里(project.c)对这两个函数的功能进行定义。
E:此时其实就可以运行程序了,但做人要正直,代码要正规,严格来说,我们仍然需要对函数进行声明,来告诉计算机我已经定义他了,不信你去找它的定义,我们把声明写到头文件(project.h)里面,并将该头文件引用到其他源文件,如此,程序完成。
F:那么我们为什么要如此“繁杂”的写这样两个小功能呢,为什么要用多个文件呢。实际上,一堆代码臃肿的挤在一个源文件里是及其不方便的,每一次维护都要付出成倍的精力,因此使用多文件这对后期维护和项目的多人开发有着重要意义,通常称为工程化处理。如若检测时发现加法出现问题,我们只需要去对加法函数的定义进行更改即可。
注意:
A: 只能有一个main函数,我们写到main.c里面,且头文件里不能写定义,只用来写声明和文件引用。
B:定义和声明
定义要开辟空间,即为原先并不存在的东西开辟一块内存空间将其存储,因此对于同一个函数来说,只能进行一次定义(如同变量的初始化,只能进行一次),而声明即是告诉计算机我已经对该函数进行了定义,它是存在的,因此可以多次声明(你可以告诉很多人你有某班花是你的女朋友(声明),却不能每天跟你女朋友说一遍做我女朋友吧(重新定义吗?),这不合适吧...)
C:函数的定义,声明和调用,
函数的调用:
函数的定义:
函数的声明:
实际函数声明甚至不需要写extern(用于声明的关键字),因为函数的声明相对于函数的定义来说,明显的少了函数体,因此计算机可以轻松的识别出函数的声明和定义,但养成好习惯十分重要,推荐写上。
D:变量的声明和定义
我们都知道int x=3是对x的定义,但 int y是什么?是对y的定义?为什么不是对y的声明? 因此,对于变量来说,其定义和生命并不像函数的定义和声明那么好区分,所以如果我们要对变量进行声明,要加上extern。
补充:关于变量的重定义
对于同一个名字的变量,只能存在于不同的作用域中,否则视为重定义进行报错
第二个图中花括号隔开了两个x的定义,不属于重定义
哇塞,我们终于讲完全局变量和函数的引子了,那么让我们回归正题,static对全局变量和函数的修饰作用。
不改变刚刚三个文件内的任何代码,仅仅在函数的定义前面加上static来进行修饰,我们发现编译器进行了报错(找不到两个函数的定义),前面的多文件操作中我们已经发现,函数默认是可以进行跨文件调用的,而现在却找不到函数定义,说明static缩减了函数的作用域(限制函数使其只能在该文件内部使用)。
在project.c中对x进行定义,在.h中声明后发现可以在main.c中运行(注意声明,否则很可能编译器在本源文件中找不到x的定义),因此全局变量默认也是可以进行跨文件调用的
用static对x的定义进行修饰,发现无法找到x的定义了,因此static修饰全局变量同函数作用相同,即改变全局变量的作用域(限制使其只能在本文件内调用)
总结:
Static修饰局部变量:扩大生命周期 ,不改变作用域
Static修饰全局变量:缩减作用域(只能在本文件内使用)
Static修饰函数:缩减作用域(只能在本文件内使用)
Static可以很好的减少调用成本,提高程序执行效率(比如你写了100和函数封装按照功能类似性放在不同的源文件下,却只想调用一个功能,此时就需要static对其他函数进行限制)
2.宏和常量
在c中define是用来定义宏的,而宏的作用我们暂时只讲它的文本替换
宏常量-简单地文本替换
该图中,进行宏定义(这个是宏常量)后,后面遇到的所有x都会被5进行文本替换,因此printf里的x实际上就是5;
带参数的宏
注意:
a:宏定义可是没有分号的噢
b:宏的文本替换发生在编译过程(编译就是编译器把一堆代码根据一定的语法、规则,转化成计算机可以识别的机器语言(二进制)),而数据的运算则是则编译之后运行时才发生的,即先替换之后才进行运行,因此是10*3+5,而不是10*8.
3.关键字的作用
3.1内存解释
如int、long、short、double等内置数据类型关键字,来解释所定义变量的类型,内存申请大小,使用规则。
3.2编译建议
如static就是建议把该变量或函数静态化处理(就是上面讲的对作用域和生命周期的改变);
Const就是建议把该变量定义为常量。
hhhhh,小伙伴们有没有认真的看到这里呀,初始c语言马上就要讲完了哦,加油!