变量的属性
- 存储位置:变量使用的是哪一段内存
- 作用域:变量的使用范围
- 生命周期:创建时间~销毁时间
注意:作用域和生命周期要特别注意区分概念。
变量的作用域
全局变量:定义在函数外。
-
存储位置:未初始化的存储在BSS段,被初始化的存储在DATA段
-
作用域:整个程序。比如在a.c定义了全局变量,在b.c中也可以使用。
-
生命周期:main函数执行前就被定义出,程序运行结束后再释放。
注意:在程序中,局部变量和全局变量的名称可以相同,如果两个名字相同,会优先使用局部变量值,全局变量暂不可被引用。在同一个函数中不能定义具有相同名字的局部变量。
局部变量:定义在函数内。
- 存储位置:栈
- 作用域:所在的函数内
- 生命周期:从定义语句开始~函数执行结束。函数每调用一次就进行一遍定义、释放。
块变量:定义在if,for,while等语句块中。
- 存储位置:栈
- 作用域:所在语句块的大括号内。
- 生命周期:从定义语句开始~函数执行结束(注意不是对应的块结束时)。
#include<stdio.h> void func(void) { int* p = NULL; for(int i =0; i<10;i++) p = &i; //printf("%d",i); //超出了i的作用域,无法访问 printf("%d",*p); //依然可以通过指针访问i地址的数据 } int main() { func(); }
初始化局部变量和全局变量
当局部变量被定义时,系统不会对其初始化,您必须自行对其初始化。定义全局变量时,系统会自动对其初始化,如下所示:
数据类型 | 初始化默认值 |
---|---|
int | 0 |
char | ‘\0’ |
float | 0 |
double | 0 |
pointer | NULL |
修饰变量的关键字
auto:
用来定义申请内存、自动释放内存的变量(局部、块变量,默认被auto修饰)。申请和释放的都是访问权限,不是数据。
注意:auto只能用在函数内,即 auto 只能修饰局部变量。全局变量和static变量不能被auto修饰。因为它们的内存段不同。
const:
是对变量提供一种保护机制,但它不一定是常量。
const变量一旦初始化后,就不能再显式地修改它的值。但是可以通过指针来修改。
const修饰被初始化过的全局变量才是真正的常量,一旦修改就出现段错误,因为编译器把这种全局变量的存储位置划分进了内存的代码段。
#include<stdio.h>
const int num2; //未初始化的全局变量
const int num3 =13; //已初始化的全局变量
int main()
{
const int num1 = 2; //局部变量
// num2 = 10; //无法显式地修改const变量(const的作用就在于此)
int* p1 = &num1;
*p1 = 10; //但是可以通过指针间接地修改
printf("num1 = %d\n",num1);
int* p2 = &num2;
*p2 = 11; //未初始化的全局变量也可通过指针修改
printf("num2 = %d\n",num2);
int* p3 = &num3;
// 已初始化的全局变量是真正意义上的常量。在编译时就存在于内存的代码段,只有只读权限,不允许任何形式的修改、赋值。
// *p3 = 12;
// num3 =12;
}
注意:const int func(void); const 修饰的是函数的返回值
static:
存储类指示编译器在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。
-
全局变量:限制全局变量的作用域,只能在当前文件内使用。
-
块、局部变量:改变存储位置,由栈改为BSS段或DATA段(由是否初始化决定),但作用域没有变。只执行初始化一次,并且会延长生命周期,直到程序运行结束以后才释放。。
#include<stdio.h> void func() { for(int i =0;i<5;i++) { static int num = 0; //只初始化一次 num++; printf("%d ",num); } } int main() { func(); } /* out: 1 2 3 4 5 */
-
修饰函数:
注意:static int func(void); static 修饰的是函数(由普通函数变为内部函数),只能在当前文件内调用。
register:
计算机的存储介质(从慢到快):硬盘 -> 固态硬盘 -> 内存 -> 缓存 -> 寄存器
register关键字申请把变量的存储位置更改为寄存器。仅仅是申请,因为寄存器数量有限或者硬件不同,不一定成功。
适用场景:如果要写一个死循环,其中有一个变量要频繁计算,那么就应该修饰为register,比如计数器。可以大大提高运算效率。
注意:寄存器变量不能取地址符‘&’(因为它没有内存位置)。
volatile:
(不稳定的,易变的):修饰线程间共享的变量,或者硬件编程时使用。
如果一个变量的值没有显式的修改,编译器不会再次从内存中读取它的数值,而是直接使用上次的读取结果(优化过程)。
volatile的功能就是告诉告诉编译器变量的值随时可能发生变化,不要优化它的取值过程(每次用到变量时都去内存中获取它的值)
int num = 10;
num == num; //永远为真。
volatile int num1 = 20;
num1 == num1; //不一定为真,多线程或者硬件改变时。
extern:
多文件中共享全局变量时使用。谁使用谁声明。可以这么理解,extern 是用来在另一个文件中声明一个全局变量或函数。
定义的含义:创建变量名,分配存储空间
声明的含义:创建变量名
a.c中定义一个全局变量,b.c中如果想使用,共享一个变量。