变量
变量(variable)可以理解成一块内存区域的名字。通过变量名,可以引用这块内存区域,获取里面存储的值。由于值可能发生变化,所以称为变量,否则就是常量了
变量名
变量名在 C 语言里面属于标识符(identifier),命名有严格的规范。
- 只能由字母(包括大写和小写)、数字和下划线(
_
)组成。 - 不能以数字开头。
- 长度不能超过63个字符。
变量的声明
C 语言的变量,必须先声明后使用。如果一个变量没有声明,就直接使用,会报错。
每个变量都有自己的类型(type)。声明变量时,必须把变量的类型告诉编译器。
int height;
上面代码声明了变量height
,并且指定类型为int
(整数)。
如果几个变量具有相同类型,可以在同一行声明。
int height, width;
// 等同于
int height;
int width;
注意,声明变量的语句必须以分号结尾。
变量的赋值
C 语言会在变量声明时,就为它分配内存空间,但是不会清除内存里面原来的值。这导致声明变量以后,变量会是一个随机的值。所以,变量一定要赋值以后才能使用。
赋值操作通过赋值运算符(=
)完成。
int num;
num = 42;
变量的值应该与类型一致,不应该赋予不是同一个类型的值,比如num
的类型是整数,就不应该赋值为小数。虽然 C 语言会自动转换类型,但是应该避免赋值运算符两侧的类型不一致。
变量的声明和赋值,也可以写在一行。
int num = 42;
C 语言有左值(left value)和右值(right value)的概念。左值是可以放在赋值运算符左边的值,一般是变量;右值是可以放在赋值运算符右边的值,一般是一个具体的值。这是为了强调有些值不能放在赋值运算符的左边,比如x = 1
是合法的表达式,但是1 = x
就会报错。
变量的作用域
作用域(scope)指的是变量生效的范围。C 语言的变量作用域主要有两种:文件作用域(file scope)和块作用域(block scope)。
文件作用域(file scope)指的是,在源码文件顶层声明的变量,从声明的位置到文件结束都有效。
int x = 1;
int main(void) {
printf("%i\n", x);
}
上面示例中,变量x
是在文件顶层声明的,从声明位置开始的整个当前文件都是它的作用域,可以在这个范围的任何地方读取这个变量,比如函数main()
内部就可以读取这个变量。
块作用域(block scope)指的是由大括号({}
)组成的代码块,它形成一个单独的作用域。凡是在块作用域里面声明的变量,只在当前代码块有效,代码块外部不可见。出了它的作用域对应的内存就会被清除。
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); // 出错
数据类型
C 语言的每一种数据,都是有类型(type)的,编译器必须知道数据的类型,才能操作数据。所谓“类型”,就是相似的数据所拥有的共同特征,那么一旦知道某个值的数据类型,就能知道该值的特征和操作方式。
基本数据类型有三种:字符(char)、整数(int)和浮点数(float)。复杂的类型都是基于它们构建的
字符类型
基础的字符类型已经讲过了,这里说点拓展的。
转义字符
比如我们想把单引号赋值给字符变量,因为字符本来就需要单引号括起来,如果直接char t = ”’;这样会出现歧义,所以这时候就需要转义字符
char t = '\'';
这种转义的写法,主要用来表示 ASCII 码定义的一些无法打印的控制字符,它们也属于字符类型的值。
\a
:警报,这会使得终端发出警报声或出现闪烁,或者两者同时发生。\b
:退格键,光标回退一个字符,但不删除字符。\f
:换页符,光标移到下一页。在现代系统上,这已经反映不出来了,行为改成类似于\v
。\n:换行符。
\r
:回车符,光标移到同一行的开头。\t
:制表符,光标移到下一个水平制表位,通常是下一个8的倍数。\v
:垂直分隔符,光标移到下一个垂直制表位,通常是下一行的同一列。\0
:null 字符,代表没有内容。注意,这个值不等于数字0。
如果想用print函数显示’\’斜杠,可以这样print(“\\”);
整数类型
基础的也讲过了,讲一下拓展。
整数变量的范围
不同计算机的int
类型的大小是不一样的。比较常见的是使用4个字节(32位)存储一个int
类型的值,但是2个字节(16位)或8个字节(64位)也有可能使用。它们可以表示的整数范围如下。
- 16位:-32,768 到 32,767。
- 32位:-2,147,483,648 到 2,147,483,647。
- 64位:-9,223,372,036,854,775,808 到 9,223,372,036,854,775,807。
signed,unsigned
C 语言使用signed
关键字,表示一个类型带有正负号,包含负值;使用unsigned
关键字,表示该类型不带有正负号,只能表示零和正整数。
对于int
类型,默认是带有正负号的,也就是说int
等同于signed int
。由于这是默认情况,关键字signed
一般都省略不写,但是写了也不算错。
signed int a;
// 等同于
int a;
int
类型也可以不带正负号,只表示非负整数。这时就必须使用关键字unsigned
声明变量。
unsigned int a;
整数变量声明为unsigned
的好处是,同样长度的内存能够表示的最大整数值,增大了一倍。比如,16位的signed int
最大值为32,767,而unsigned int
的最大值增大到了65,535。
字符类型char
也可以设置signed
和unsigned
。
signed char c; // 范围为 -128 到 127
unsigned char c; // 范围为 0 到 255
注意,C 语言规定char类型默认是否带有正负号,由当前系统决定。这就是说,char不等同于signed char,它有可能是signed char,也有可能是unsigned char。这一点与int不同,int就是等同于signed int。
整数的子类型
short int , long int , long long int。这些无非是拓展了int类型的表示范围。
整数的进制
C 语言的整数默认都是十进制数,如果要表示八进制数和十六进制数,必须使用专门的表示法。
八进制使用0
作为前缀
int a = 012; // 八进制,相当于十进制的10
十六进制使用0x
或0X
作为前缀,比如0xf
、0X10
。
int a = 0x1A2B; // 十六进制,相当于十进制的6699
有些编译器使用0b
前缀,表示二进制数,但不是标准。
int x = 0b101010;
浮点数类型
由于存在精度限制,浮点数只是一个近似值,它的计算是不精确的
有时候,32位浮点数提供的精度或者数值范围还不够,C 语言又提供了另外两种更大的浮点数类型。用法和int类似
double
:占用8个字节(64位),至少提供13位有效数字。long double
:通常占用16个字节。
C 语言允许使用科学计数法表示浮点数,使用字母e
来分隔小数部分和指数部分。
double x = 123.456e+3; // 123.456 x 10^3
// 等同于
double x = 123.456e3;
布尔类型
C 语言原来并没有为布尔值单独设置一个类型,而是使用整数0
表示伪,所有非零值表示真。
int x = 1;
if (x) {
printf("x is true!\n");
}
上面示例中,变量x
等于1
,C 语言就认为这个值代表真,从而会执行判断体内部的代码。