一、前言
生活中的有些值是不变的(比如:圆周率,身份证号码,血型等等),有些值是可变的(比如:年龄,体重,薪资等等)。
不变的值,C语言中用常量的概念来表示;变的值,C语言中用变量来表示。
二、变量
1.变量的概念
int main()
{
short age = 10; // 创建一个名为age的变量,为它开辟2byte(short)的空间,初始化为10
printf("%d\n", age); // 打印结果为10
age = 11; // age是变量,可以改变,二次赋值为11
printf("%d\n", age); // 打印结果为11
return 0;
}
通过上面的代码我们可以看出,age的值可以改变,所以age是变量。
2.定义变量的方法
数据类型 变量名 = 初始值
int age =150;
float weight=45.5f;
char ch='w';
3.变量的分类
- 局部变量 定义在代码块{}内的变量
- 全局变量 定义在代码块{}外的变量
int g = 100; // 全局变量
void test()
{
int b = 1000; // 局部变量
}
int main()
{
int a = 10; // 局部变量
return 0;
}
注:局部变量不一定在main函数内部,只要是在代码块{}内部的变量都是局部变量。
①全局变量和局部变量名字相同会报错吗?
int a = 100; // 全局变量
int main()
{
int a = 30; // 局部变量
printf("%d\n", a); // 局部变量优先,若没有局部变量,再使用全局变量
return 0;
}
当你调试上面代码时,会发现它的运行结果是30,不会报错。
由此我们可以得出一个结论,全局变量和局部变量名字可以相同,当名字相同时,局部变量优先,但不建议这样写,会引起bug。
int a = 100; // 全局变量
void test() // test函数内部的a使用的是全局变量
{
printf("%d\n", a); // 打印全局变量a - 100
}
int main()
{
int a = 10; // 局部变量
printf("%d\n", a); // 局部优先a - 10
a = 20; // 局部变量优先,修改的是局部变量的值,全局变量的值未改变
test();
return 0;
}
int a = 100;
void test()
{
printf("%d\n", a);
}
int main()
{
a = 20; // 修改的是全局变量
test();
return 0;
}
由此可见局部变量a和全局变量a完全是两个变量,没有任何联系,不会出现修改局部变量导致全局变量的现象。
4.变量的使用
我们以加法为例,读者自行理解
int main()
{
// 2个整数相加
int num1 = 0;
int num2 = 0;
int sum = 0;
printf("输入两个操作数:>");
// 输入
scanf("%d%d", &num1, &num2);
// 相加
sum = num1 + num2;
// 输出
printf("sum = %d\n", sum);
return 0;
}
代码书写常见问题
①scanf书写格式问题
在这里scanf和printf函数的格式不一样,语法规定需要用到取地址符&,意味着将你输入的值存放到该地址处,并且输入的时候用空格隔开以区分两个值。
但如果你的scanf函数是下面写法:
scanf("%d,%d", &a, &b);
你输入值的时候就不是空格隔开,而是逗号隔开,并且注意中英文逗号。
有的同学需要输入三个值才能进行相加,但是运算时第三个数没用,这种问题的出现可能是在scanf的%d后面加了\n导致的。注意scanf后面不能加\n。
scanf("%d%d\n", &a, &b); // err
②scanf报错问题
有些同学和笔者一样使用的是VS2019编译器,写scanf函数时会报错,这并不是代码问题。
第一种解决方法:把所有的scanf用scanf_s代替
scanf是C语言提供的,scanf_s不是标准C语言提供的,是VS编译器提供的(居然认为scanf不安全,不理解啊哈哈)。但scanf_s尽量不要使用,移植性不好,写的代码在其他编译器运行不了,我们需要考虑代码的跨平台性、可移植性。
第二种解决方法(推荐):在源文件第一行加上#define _CRT_SECURE_NO_WARNINGS
如果觉得每次都要加这一行太麻烦,我们可以选择一劳永逸的办法:找到VS安装路径,在VS安装文件夹里搜索找到newc++file.cpp这个文件,用记事本打开(需要管理员权限)加上#define _CRT_SECURE_NO_WARNINGS这句话,这样每次新建源文件都会出现这行代码。
5.变量的作用域和生命周期
①作用域
作用域,程序设计概念,通常来说,一段程序代码中所用到的名字并不总是有效/可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。
- 局部变量的作用域是变量所在的局部范围。(代码块内部)
- 全局变量的作用域是整个工程。(整个工程跨文件使用变量时需要借助extern关键字来进行声明)
局部变量例子:
// 例子1
int main()
{
{
int a = 10; // 在局部变量的代码块外,超出作用域无法使用
}
printf("a=%d\n", a); // 该打印函数不会打印a,因为已经出了a的作用域
return 0;
}
// 例子2
int main()
{
{
int a = 10; // 在局部变量的代码块内,在作用域内可以使用
printf("a=%d\n", a); // 该打印函数能够打印a,因为在a的作用域里面
}
return 0;
}
// 例子3
int main()
{
int b = 20;
{
int a = 10;
printf("a=%d\n", a); // 打印10
printf("b=%d\n", b); // 打印20
}
printf("b=%d\n", b); // 打印20
return 0;
}
通过上面例子可以看出局部变量的作用域是代码块包含的所在局部范围。
全局变量例子:
// 例子1
int a = 100; // 全程变量的作用域为整个工程,在整个工程内部都可以使用
void test()
{
printf("test: a=%d\n",a); // 打印全局变量a - 100
}
int main()
{
test();
printf("main: a=%d\n", a); // 打印全局变量a - 100
return 0;
}
// 例子2
// 相同工程不同源文件下使用全局变量
// test1.c
int g_val = 2021; // 全程变量的作用域为整个工程,在整个工程内部都可以使用
// test2.c
// 声明外部变量,extern关键字
extern int g_val; // 不声明会报错,只要声明其他源文件定义的全局变量就可以正常跨文件使用了
int main()
{
printf("%d", g_val); // 整个工程内跨文件,全局变量可以使用
return 0;
}
通过上面例子我们可以看到全局变量在整个工程里面任意地方都可以使用。
②生命周期
变量的生命周期指的是变量的创建到变量的销毁之间的一个时间段。
- 局部变量的生命周期是:进入作用域生命周期开始,出作用域生命周期结束。
- 全局变量的生命周期是:整个程序的生命周期。
局部变量例子:
// 例子1
int main()
{
{ // 进入局部变量作用域,局部变量生命周期开始
int a = 10;
printf("a=%d\n", a);
} // 出局部变量作用域,局部变量生命周期结束
printf("a=%d\n",a); // 此时局部变量的生命周期结束,无法使用
return 0;
}
由此可见,局部变量的生命周期是进入局部变量所在的范围生命周期开始,出局部变量所在的范围生命周期结束。
全局变量例子:
// 例子1
// 相同工程不同源文件下使用全局变量
// test1.c
int g_val = 2021;
// test2.c
// 声明外部变量,extern关键字
extern int g_val; // 不声明会报错,只要声明其他源文件定义的全局变量就可以正常跨文件使用了
int main()
{
printf("%d", g_val); // 全局变量的生命周期为main函数的生命周期,main函数的生命周期为程序的生命周期,故全局变量的生命周期为整个程序的生命周期
return 0;
}
一个程序是怎么执行的呢?从main函数第一行(程序的入口)开始执行,一直执行到main函数的最后一行结束。所以我们可以认为程序运行的生命周期就是main函数的生命周期,而main函数里面任意地方全局变量都可以使用,所以简单来想,全局变量的生命周期就是程序的生命周期。
三、常量
C语言常量与变量定义形式有所差异,常分为以下四种,我们将逐一介绍:
- 字面常量
- const修饰的常变量
- #define定义的标识符常量
- 枚举常量
1.字面常量
所谓字面常量,就是字面意义上的常量,比如说3.14、1000,很显然,这些数值不能被修改,不可能写成3.14=1000。
int main()
{
// 字面常量
3.14; // 浮点型字面常量
100; // 整型字面常量
}
2.const修饰的常变量
int main()
{
int a = 100; // 局部变量
a = 200;
printf("%d\n", a);
return 0;
}
这里a的值首先被初始化,将常量100赋值给变量a,再给a二次赋值为200。
但是如果在a的定义前面加上const关键字呢?结果将大不相同。
int main()
{
// 一个变量不能被改变我们就说具有常属性 - 不能改变的属性
const int a = 100; // const修饰的常变量
a = 200; // 常变量的值不能被修改,具有了常属性
printf("%d\n", a);
return 0;
}
这样写代码会报错(左值指定const对象),因为const所修饰的变量不能再被修改,即具有了常属性,但是它的本质仍然为变量,故而我们将变量a称为const修饰的常变量。
①本质是变量
在这里我们通过数组的定义来进行解释,首先我们所需要知道的是数组的定义中[ ]内必须是常量表达式。
int main()
{
int arr[10] = { 0 }; // 数组
return 0;
}
由于上面代码[ ]内为10,是常量,所以代码正常运行。
int main()
{
int n = 10;
int arr[n] = { 0 }; // 数组定义时[]内必须是常量表达式
return 0;
}
由于上面代码[ ]内为n,是变量,即可以改变的量,不是常量表达式,因此代码报错。
如果在n的定义前面加上const呢?变成常变量还能否正常运行了呢?
int main()
{
const int n = 10; // const修饰的常变量
int arr[n] = { 0 }; // n不是常量,本质是变量,只是具有了常属性,不能被修改而已
return 0;
}
很显然,加上const后仍然会报错,说明n仍然不是常量,这是因为const修饰的常变量虽然具有了常属性,不可被修改,但它的本质仍然是变量,不是常量表达式,因此数组定义是错误的。
const修饰的常变量虽然具有了常属性,其定义的值不可被修改,但它的本质仍然是变量。
3.#define定义的标识符常量
#define MAX 100 // #define定义的标识符常量
int main()
{
int a = MAX;
printf("%d\n", a);
return 0;
}
这里我们define定义的MAX是常量,它的值在整个工程里就是100,MAX值不能被修改。
#define PI 3.14
int main()
{
PI = 200; // error - PI是常量,其值不能被修改
return 0;
}
上面写法是错误的,因为#define定义的PI就是常量,不能被修改,所以后面将PI改为200是错误的写法。
①本质是常量
在这里我们依然使用数组的定义来证明
#define MAX 100
int main()
{
int arr[MAX] = { 0 }; // MAX是常量
return 0;
}
这种写法是对的,因为#define定义的标识符常量是常量表达式,可以正确用在数组的[ ]中。
4.枚举常量
枚举的意思就是一一列举,我们生活中有很多事物可以一一列举出来,比如说性别有男、女、保密三种类型;三原色又可以列举为红,黄,蓝。在C语言中,我们可以用枚举类型来定义枚举常量,这里就使用了枚举关键字enum(enumerate)。
//枚举类型
enum SEX
{
MALE, //0
FEMALE, //1
SECRET //2
};
enum COLOR
{
RED, //0
YELLOW = 97, //97
BLUE //98
};
以上就是我们定义的枚举类型,枚举类型里面的可能取值默认第一个值为0,以此类推向下自增1(0,1,2,……),但中间可以赋初值,若中间有赋值,那么后面紧跟着自增1。
下面代码为枚举常量的例子
enum Gender // 枚举类型
{
// 枚举常量 —— 枚举的可能取值
MALE,
FEMALE = 5,
SECRET
};
int main()
{
// 枚举类型里面的可能取值默认第一个值为0,以此类推向下自增1,中间可以赋初值
printf("%d\n", MALE);
printf("%d\n", FEMALE);
printf("%d\n", SECRET);
enum Gender s = MALE;
return 0;
}
显然上面的输出结果为0 5 6。
①本质是常量
enum Gender // 枚举类型
{
// 枚举常量 —— 枚举的可能取值
MALE,
FEMALE = 5,
SECRET
};
int main()
{
FEMALE = 3; // err - 枚举常量的值不能被修改
return 0;
}
这种写法是错误的,因为enum类型包含的枚举常量是不能被修改的,但通过枚举类型所创建的枚举变量是可以被修改的。