一、变量的作用域
1.局部变量
1> 定义:在函数内部定义的变量,称为局部变量。形式参数也属于局部变量。
2> 作用域:局部变量只在定义它的函数内部有效,即局部变量只有在定义它的函数内部使用,其它函数不能使用它。
3> 附上一个例子:
float(int a)
{int b ,c;
} //b,c在函数float有效
int main()
{int m,n;
return 0;
}m,n在main函数有效
2.全局变量
1>定义:在所有函数外部定义的变量,称为全局变量。
2>作用域:全局变量的作用范围是从定义变量的位置开始到源程序结束,即全局变量可以被在其定义位置之后的其它函数所共享。
3> 附上一个例子
int p = 1,q = 5;//p,q全局变量,整个程序都有效
float(int a)
{int b ,c;
}//b,c只在float函数中有效
int main()
{int m,n;
rerurn 0;
}m,n在main函数有效
二、局部变量的存储类型
变量的存储类型就是指变量存储在什么地方。有3个地方可以用于存储变量:普通内存、运行时堆栈、硬件寄存器。变量的存储类型决定了变量何时创建、何时销毁以及它的值能保持多久,也就是决定了变量的生命周期。
语言根据变量的存储类型的不同,可以把变量分为:自动变量、静态变量、寄存器变量。
1.自动变量(auto变量)
1> 定义:自动变量是存储在堆栈中的。
2> 哪些是自动变量:被关键字auto修饰的局部变量都是自动变量,但是极少使用这个关键字,基本上是废的,因为所有的局部变量在默认情况下都是自动变量。
3> 生命周期:在程序执行到声明自动变量的代码块(函数)时,自动变量才被创建;当自动变量所在的代码块(函数)执行完毕后,这些自动变量就会自行销毁。如果一个函数被重复调用,这些自动变量每次都会重新创建。
2.静态变量(static变量)
1> 定义:静态变量是存储在静态内存中的,也就是不属于堆栈。
2> 哪些是静态变量:
所有的全局变量都是静态变量
被关键字static修饰的局部变量也是静态变量
3> 生命周期:静态变量在程序运行之前创建,在程序的整个运行期间始终存在,直到程序结束。
3.寄存器变量(registe变量)
1> 定义:存储在硬件寄存器中的变量,称为寄存器变量。寄存器变量比存储在内存中的变量访问效率更高(默认情况下,自动变量和静态变量都是放在内存中的)
2> 哪些变量是寄存器变量:
被关键字register修饰的自动变量都是寄存器变量
只有自动变量才可以是寄存器变量,全局变量和静态局部变量不行
寄存器变量只限于int、char和指针类型变量使用
3> 生命周期:因为寄存器变量本身就是自动变量,所以函数中的寄存器变量在调用该函数时占用寄存器中存放的值,当函数结束时释放寄存器,变量消失。
4> 使用注意:
由于计算机中寄存器数目有限,不能使用太多的寄存器变量。如果寄存器使用饱和时,程序将寄存器变量自动转换为自动变量处理
为了提高运算速度,一般会将一些频繁使用的自动变量定义为寄存器变量,这样程序尽可能地为它分配寄存器存放,而不用内存
三、全局变量的存储类别
1> 在一个文件内扩展外部变量的作用域
如果外部变量不在文件的开头定义,其有效的作用范围只限于定义处到文件结束
关键字extern对变量作“外部变量声明”,表示把该外部变量的作用域扩展到此位置
2> 将外部变量的作用域扩展到其它文件
3> 将外部变量的作用域限制在本文件中
例子:
file.c file2.c
static int A; extern A;
int main() void fun(int n)
{ {
…… A = A*n // 出错
} }
这种加上static声明,只能用于本文件的外部变量称为静态外部变量
四、结构体
1.格式
struct 结构体名
{
成员列表
};
2.定义结构体变量的3种方式
1> 先定义类型,再定义变量(分开定义)
struct Student
{
int age;
};
struct Student stu;
2> 定义类型的同时定义变量
struct Student
{
int age;
} stu;
struct Student stu2;
3> 定义类型的同时定义变量(省略了类型名称)
struct
{
int age;
} st;
3.结构体类型的作用域
1> 定义在函数外面:全局有效(从定义类型的那行开始,一直到文件结尾)
2> 定义在函数(代码块)内部:局部有效(从定义类型的那行开始,一直到代码块结束)
4.结构体注意点
1> 不允许对结构体本身递归定义
2> 结构体内可以包含别的结构体
3> 定义结构体类型,只是说明了该类型的组成情况,并没有给它分配存储空间,就像系统不为int类型本身分配空间一样。只有当定义属于结构体类型的变量时,系统才会分配存储空间给该变量
4> 结构体变量占用的内存空间是其成员所占内存之和,而且各成员在内存中按定义的顺序依次排列
5.结构体和数组
数组:只能由多个相同类型的数据构成
结构体:可以由多个不同类型的数据构成
6.指向结构体的指针
1> 每个结构体变量都有自己的存储空间和地址,因此指针也可以指向结构体变量
2> 结构体指针变量的定义形式:struct 结构体名称 *指针变量名
3> 有了指向结构体的指针,那么就有3种访问结构体成员的方式
定义方式:
结构体变量名.成员名
(*指针变量名).成员名
指针变量名->成员名
五、枚举类型
声明形式:
enum [枚举名]{枚举元素列表};
例子:
enum Sex { Man, Woman, Unkown};
结构体的本质是int类型,第一个成员为0,第二个为1,依次递加
六、预处理指令
1.综述
1> 所有的预处理指令都是以#开头
2> 预处理指令分3种: 宏定义,条件编译,文件包含
3> 预处理指令在代码翻译成0和1之前执行
4> 预处理的位置是随便写的
5> 预处理指令的作用域:从编写指令的那一行开始,一直到文件结尾,可以用#undef取消
5> 宏名一般用大写或者以k开头,变量名一般用小写
2.宏定义
1> 不带参数的宏定义
一般形式:
#define 宏名 字符串
小例子:
#define ABC 10
特例:
有时候右边的字符串也可以省略,比如:
#define ABC
作用:
它的作用是在编译预处理时,将源程序中所有"宏名"替换成右边的"字符串",常用来定义常量。
使用习惯与注意:
宏名一般用大写字母,以便与变量名区别开来,但用小写也没有语法错误
对程序中用双引号扩起来的字符串内的字符,不进行宏的替换操作。
在编译预处理用字符串替换宏名时,不作语法检查,只是简单的字符串替换。只有在编译的时候才对已经展开宏名的源程序进行语法检查
宏名的有效范围是从定义位置到文件结束。如果需要终止宏定义的作用域,可以用#undef命令
定义一个宏时可以引用已经定义的宏名
2> 带参数的宏定义
一般形式
#define 宏名(参数列表) 字符串
作用
在编译预处理时,将源程序中所有宏名替换成字符串,并且将字符串中的参数 用宏名右边参数列表中的参数替换
使用注意
宏名和参数列表之间不能有空格,否则空格后面的所有字符串都作为替换的字符串
带参数的宏在展开时,只作简单的字符和参数的替换,不进行任何计算操作。所以在定义宏时,一般用一个小括号括住字符串的参数。
计算结果最好也用括号括起来
3> 宏定义与函数的区别
从整个使用过程可以发现,带参数的宏定义,在源程序中出现的形式与函数很像。但是两者是有本质区别的:
宏定义不涉及存储空间的分配、参数类型匹配、参数传递、返回值问题
函数调用在程序运行时执行,而宏替换只在编译预处理阶段进行。所以带参数的宏比函数具有更高的执行效率
2.条件编译
利用条件编译防止头文件多次包含
如:
#ifndef LISI_H
#define LISI_H
int sum(int a, int b);
#endif
3.文件包含
文件包含就是copy包含声明的头文件,用#include定义
七、typedef
关于typedef声明
1> 简单地用一个新的类型名代替原有的类型名
typedef int Integer;//指定Integer为类型名,作用域int相同
typedef float Real;//指定Real为类型名,作用域float相同
2> 命名一个简单地类型名代替复杂的类型表示方法
typedef struct
{int month;
int day;
int year;
}Date;//类型名Date代表上面的一个结构体类型
Date birthday;//定义结构体变量birthday
终于总结完了,C语言,我们永别勒~ (~ o ~)~~