174 lines
5.8 KiB
Markdown
174 lines
5.8 KiB
Markdown
---
|
||
id: 变量
|
||
title: 变量
|
||
sidebar_position: 3
|
||
data: 2022年3月30日
|
||
---
|
||
|
||
变量(variable)可以理解成一块内存区域的名字。通过变量名,可以引用这块内存区域,获取里面存储的值。由于值可能发生变化,所以称为变量,否则就是常量了。
|
||
|
||
## 变量名
|
||
|
||
变量名在 C 语言里面属于标识符(identifier),命名有严格的规范。
|
||
|
||
- 只能由字母(包括大写和小写)、数字和下划线(`_`)组成。
|
||
- 不能以数字开头。
|
||
- 长度不能超过63个字符。
|
||
|
||
下面是一些无效变量名的例子。
|
||
|
||
```c
|
||
$zj
|
||
j**p
|
||
2cat
|
||
Hot-tab
|
||
tax rate
|
||
don't
|
||
```
|
||
|
||
上面示例中,每一行的变量名都是无效的。
|
||
|
||
变量名区分大小写,`star`、`Star`、`STAR`都是不同的变量。
|
||
|
||
并非所有的词都能用作变量名,有些词在 C 语言里面有特殊含义(比如`int`),另一些词是命令(比如`continue`),它们都称为关键字,不能用作变量名。另外,C 语言还保留了一些词,供未来使用,这些保留字也不能用作变量名。下面就是 C 语言主要的关键字和保留字。
|
||
|
||
> auto, break, case, char, const, continue, default, do, double, else, enum, extern, float, for, goto, if, inline, int, long, register, restrict, return, short, signed, sizeof, static, struct, switch, typedef, union, unsigned, void, volatile, while
|
||
|
||
另外,两个下划线开头的变量名,以及一个下划线 + 大写英文字母开头的变量名,都是系统保留的,自己不应该起这样的变量名。
|
||
|
||
## 变量的声明
|
||
|
||
C 语言的变量,必须先声明后使用。如果一个变量没有声明,就直接使用,会报错。
|
||
|
||
每个变量都有自己的类型(type)。声明变量时,必须把变量的类型告诉编译器。
|
||
|
||
```c
|
||
int height;
|
||
```
|
||
|
||
上面代码声明了变量`height`,并且指定类型为`int`(整数)。
|
||
|
||
如果几个变量具有相同类型,可以在同一行声明。
|
||
|
||
```c
|
||
int height, width;
|
||
|
||
// 等同于
|
||
int height;
|
||
int width;
|
||
```
|
||
|
||
注意,声明变量的语句必须以分号结尾。
|
||
|
||
一旦声明,变量的类型就不能在运行时修改。
|
||
|
||
## 变量的赋值
|
||
|
||
C 语言会在变量声明时,就为它分配内存空间,但是不会清除内存里面原来的值。这导致声明变量以后,变量会是一个随机的值。所以,变量一定要赋值以后才能使用。
|
||
|
||
赋值操作通过赋值运算符(`=`)完成。
|
||
|
||
```c
|
||
int num;
|
||
num = 42;
|
||
```
|
||
|
||
上面示例中,第一行声明了一个整数变量`num`,第二行给这个变量赋值。
|
||
|
||
变量的值应该与类型一致,不应该赋予不是同一个类型的值,比如`num`的类型是整数,就不应该赋值为小数。虽然 C 语言会自动转换类型,但是应该避免赋值运算符两侧的类型不一致。
|
||
|
||
变量的声明和赋值,也可以写在一行。
|
||
|
||
```c
|
||
int num = 42;
|
||
```
|
||
|
||
多个相同类型变量的赋值,可以写在同一行。
|
||
|
||
```c
|
||
int x = 1, y = 2;
|
||
```
|
||
|
||
注意,赋值表达式有返回值,等于等号右边的值。
|
||
|
||
```c
|
||
int x, y;
|
||
|
||
x = 1;
|
||
y = (x = 2 * x);
|
||
```
|
||
|
||
上面代码中,变量`y`的值就是赋值表达式(`x = 2 * x`)的返回值`2`。
|
||
|
||
由于赋值表达式有返回值,所以 C 语言可以写出多重赋值表达式。
|
||
|
||
```c
|
||
int x, y, z, m, n;
|
||
|
||
x = y = z = m = n = 3;
|
||
```
|
||
|
||
上面的代码是合法代码,一次为多个变量赋值。赋值运算符是从右到左执行,所以先为`n`赋值,然后依次为`m`、`z`、`y`和`x`赋值。
|
||
|
||
C 语言有左值(left value)和右值(right value)的概念。左值是可以放在赋值运算符左边的值,一般是变量;右值是可以放在赋值运算符右边的值,一般是一个具体的值。这是为了强调有些值不能放在赋值运算符的左边,比如`x = 1`是合法的表达式,但是`1 = x`就会报错。
|
||
|
||
## 变量的作用域
|
||
|
||
作用域(scope)指的是变量生效的范围。C 语言的变量作用域主要有两种:文件作用域(file scope)和块作用域(block scope)。
|
||
|
||
文件作用域(file scope)指的是,在源码文件顶层声明的变量,从声明的位置到文件结束都有效。
|
||
|
||
```c
|
||
int x = 1;
|
||
|
||
int main(void) {
|
||
printf("%i\n", x);
|
||
}
|
||
```
|
||
|
||
上面示例中,变量`x`是在文件顶层声明的,从声明位置开始的整个当前文件都是它的作用域,可以在这个范围的任何地方读取这个变量,比如函数`main()`内部就可以读取这个变量。
|
||
|
||
块作用域(block scope)指的是由大括号(`{}`)组成的代码块,它形成一个单独的作用域。凡是在块作用域里面声明的变量,只在当前代码块有效,代码块外部不可见。
|
||
|
||
```c
|
||
int a = 12;
|
||
|
||
if (a == 12) {
|
||
int b = 99;
|
||
printf("%d %d\n", a, b); // 12 99
|
||
}
|
||
|
||
printf("%d\n", a); // 12
|
||
printf("%d\n", b); // 出错
|
||
```
|
||
|
||
上面例子中,变量`b`是在`if`代码块里面声明的,所以对于大括号外面的代码,这个变量是不存在的。
|
||
|
||
代码块可以嵌套,即代码块内部还有代码块,这时就形成了多层的块作用域。它的规则是:内层代码块可以使用外层声明的变量,但外层不可以使用内层声明的变量。如果内层的变量与外层同名,那么会在当前作用域覆盖外层变量。
|
||
|
||
```c
|
||
{
|
||
int i = 10;
|
||
|
||
{
|
||
int i = 20;
|
||
printf("%d\n", i); // 20
|
||
}
|
||
|
||
printf("%d\n", i); // 10
|
||
}
|
||
```
|
||
|
||
上面示例中,内层和外层都有一个变量`i`,每个作用域都会优先使用当前作用域声明的`i`。
|
||
|
||
最常见的块作用域就是函数,函数内部声明的变量,对于函数外部是不可见的。`for`循环也是一个块作用域,循环变量只对循环体内部可见,外部是不可见的。
|
||
|
||
```c
|
||
for (int i = 0; i < 10; i++)
|
||
printf("%d\n", i);
|
||
|
||
printf("%d\n", i); // 出错
|
||
```
|
||
|
||
上面示例中,`for`循环省略了大括号,但依然是一个块作用域,在外部读取循环变量`i`,编译器就会报错。
|