2. 变量和数据类型
变量
什么是变量?顾名思义,变量是一个可以被改变的量。在计算机中,变量是用来存储数据的容器。变量可以存储各种类型的数据,如整数、浮点数、字符串等。
在 C 语言中,变量可以被声明,你需要在声明时指定变量的类型和名称。
此外,你可以使用 赋值符号(=)
来给变量赋值。
int age = 10;
float number = 3.14159;
char letter = 'A';
提示
赋值符号(=)是 C 语言中的赋值运算符,用于将右侧的值赋给左侧的变量。
赋值符号的左侧只能是指定的变量,右侧可以是任何结果满足被赋值变量类型的表达式。
如果许多个变量具有相同的类型,你可以使用逗号分隔符来声明多个变量:
int a, b, c;
变量的作用域
变量的作用域表示该变量在程序中可以被正确访问的范围。
对于于全局变量,它们在整个程序中都可以被访问。全局变量通常在函数外部声明:
// 这里不可以访问 PI
int PI = 3.14159; // 全局变量
// 这里可以访问 PI
int main() {
// 这里可以访问 PI
}
提示
为保证全局变量的安全性,一般将全局变量声明为常量或静态变量。
常量和静态变量的含义会在稍后提到
而对于大括号 {}
内部声明的变量,它们只能在当前大括号内被访问。这种变量被称为局部变量:
局部变量一旦离开其声明的大括号,就会立刻被销毁,此后再也无法访问。
此外,变量需要先声明才能够被使用
int main() {
// 这里也访问不到 a
int a = 10; // 局部变量
// 这里可以访问到 a
}
// 这里访问不到 a
常量和静态变量
常量(const)和静态变量(static)是 C 语言中的两种特殊变量。
常量是一种不能被修改的变量。在声明常量时,需要使用 const
关键字。常量通常用于表示不会改变的值,如圆周率、自然对数的底数等。
const float PI = 3.14159;
静态变量是一种在整个程序运行期间都存在的变量。在声明静态变量时,需要使用 static
关键字。静态变量通常用于表示需要在函数调用之间保持状态的变量,如计数器、累加器等。
static int counter = 0;
静态变量的特点在于,即使离开变量所在的作用域,静态变量不会被销毁,并仍然保持其值不变:
int counter() {
static int count = 0;
count = count + 1;
return count;
}
例如,每次调用上面的 counter()
函数,count
的值都会增加 1,而不是每次都返回 1。
变量的命名规则
在 C 语言中,变量命名我们一般遵循以下规则
- 对于一般的局部变量和函数,变量名一般使用小写字母,单词之间使用下划线
_
分隔,即snake_case
命名法 - 也可以使用匈牙利命名法,即变量名以类型前缀开头,之后名称每个单词首字母大写,如
int iAge
,float fpNumber
,但个人觉得这种命名方式非常丑而且没有必要。 - 对于常量,变量名一般使用大写字母,单词之间使用下划线
_
分隔,即UPPER_CASE
命名法 - 其它的命名规则会在说到对应的语法时提到
数据类型
在 C 语言中,数据类型用于表示变量可以存储的数据类型。C 语言中的数据类型包括以下几种:
- 整型(
short
、int
、long long
)(对应类型分signed
和unsigned
) - 浮点型(
float
、double
、long double
) - 字符型(
char
) - 布尔型(
bool
)(C 语言需要包含头文件stdbool.h
) - 其它类型(进度未解锁)
整型
short
short
是一种较小的整数类型,占用 2 个字节(16 位)的内存空间。
既然它占用了二进制上 16 位的空间,那么它最大能是多少呢?
首先,当我们定义整型类型时,它默认是 signed
整型类型,即代表它有符号,因此,还需要牺牲一位二进制位表示符号的正负
因此,short
实际上只拥有 15 位来表示数值,而 15 位二进制数能够表示的最大值是 32767
,最小值是 -32768
而如果你不需要符号,你可以在定义 short
类型时,使用 unsigned
关键字来表示无符号整型,这样,short
就拥有 16 位来表示数值,因此,它的最大值是 65535
,最小值是 0
:
unsigned short a = 65535;
之后的整型类型只在存储空间上有区别,之后的就不详细讲解了
提示
为啥最小值可以比最大值多 1?
因为 short
的二进制表示中,最高位是符号位,当符号位为 1 时,表示负数,当符号位为 0 时,表示正数。
因此,当符号位为 1 时,表示负数,而负数的表示方式是补码表示法,即最高位为 1(表示为负数),其余位取反,然后加 1。
那 -0
这个数怎么办呢?在这个时候,符号位就会被直接解释为负数,因此,-0
就被解释为 -32768
。
这也是为什么,当你取 32767 + 1
时,得到的却是 -32768
了
为什么负数要使用补码表示法?
减法需要退位,但计算机的设计是每位分开计算的,只能用 1 位的临时寄存器来记录进位,退位操作根本无法实现
因此,出现了补码表示法,它是来想办法将减法转换为加法的
补码怎么想出来的?
如果说一个数减去 x
和一个数加上 y
相等,那么是不是可以得到,y = -x
?
那不对啊,在二进制上,没有办法表示 -x
啊?
有的,兄弟,有的,回想起我们小学学过的整数除法,是不是有 商余
的概念?
举个例子让你回想起来:
4 ÷ 3 = 1 …… 1
7 ÷ 3 = 2 …… 1
那这样的话,4 和 7 在同时整除三之后,是不是相等的?
同理,对于 short
类型,能用来表示数字的只有 16 位,当我们得到的结果超过 16 位时,多出来的那一位就会被丢弃,因此,两个数字相加,实际上得到的结果是除以 的余数
那么我们回到除 3
的情景,如果我现在要得到 4 - 2
在整除 3 之后得到的结果,那显然它是 2
那有没有另外一个数字,能让 4
加上它再整除 3
之后得到的结果还是 2
呢?
答案自然是有的,我们找到最小的一个正整数,也就是 1
那么在同时除以三的基础上,加上 1
就相当于将原来的数字减去 2
,因此,1
在这里就可以看作是 -2
的补码
再仔细一瞧,您猜怎么着?1 + 2
正好就是 3
,也就是原本要被整除的那个数
那同理推断,一个负整数 short
的变量,加上它的补码,得到的结果就是原本要被整除的那个数
所以补码的值就很好计算了,只需要将原本的数字取反,然后加 1
就可以得到补码了
int
int
是一种标准的整数类型,占用 4 个字节(32 位)的内存空间。
通常情况下,int
的最大值是 2147483647
(),最小值是 -2147483648
。
提示
int
的大小范围建议牢记,一些题目可能会在数据范围上作文章,大多围绕 int
的范围来出题,因此要特别小心超出 int
范围的情况
long long
long long
是一种非常大的整数类型,占用 8 个字节(64 位)的内存空间。
浮点型
float
float
是一种单精度浮点数类型,占用 4 个字节(32 位)的内存空间。
double
double
是一种双精度浮点数类型,占用 8 个字节(64 位)的内存空间。
long double
long double
是一种扩展精度浮点数类型,占用 10/16 个字节(80 / 128 位)的内存空间。具体占用大小因平台而异
如果直接写小数运算,那么 C 语言默认使用的是 double
类型,一般来说,double
的大小也够用,一般不会用到 long double
类型
浮点型变量的存储格式采取 IEEE 754 格式,因为展开讲起来有些麻烦,所以就不展开了,一般来说考前看一眼就够用,一般也用不上这么精细
字符型
char
char
是一种字符类型,占用 1 个字节(8 位)的内存空间。
char
类型的变量可以存储一个字符,如字母、数字、符号等。
字符需要用单引号 '
括起来,如 'A'
、'1'
、'!'
等。
char letter = 'A';
char
类型的变量实际上存储的是字符的 ASCII
码值,因此,char
类型的变量也可以存储整数,并参与整数运算:
int distance = 'a' - 'A';
在 C 语言中,char
默认是 signed
格式,这会导致它浪费了 128 个值,而 128 ~ 255
实际上是存在对应字符的,因此,在使用 char
输出 ASCII
码大于 127
的字符时,务必注意 char
此时是 unsigned
还是 signed
格式
布尔型
bool
bool
是一种二元类型,理论上应该只需要占用 1 个二进制位,但很遗憾,因为地址空间的申请必须按照字节大小对齐,因此,bool
类型实际上占用 1 个字节(8 位)的内存空间。
bool
类型的变量只能存储 true
或 false
两个值。
因此,实际上 true = 1
,false = 0
bool flag = true;
输入输出
在 C 语言中,输入输出是使用 scanf
和 printf
函数来实现的,当然,也有 putchar
,getchar
这种更加原始的方法。
要使用它们,需要先包含头文件 stdio.h
scanf
scanf
函数用于从标准输入读取数据,并将其存储到指定的变量中。
scanf
函数的语法如下:
int scanf(const char *format, ...);
提示
format
的类型目前尚未解锁,稍安勿躁哦~
其中,format
是一个字符串,用于指定输入数据的格式,...
表示可以接受任意数量的参数,这些参数是用于存储输入数据的变量。
例如,以下代码从标准输入读取一个整数和一个浮点数,并将其存储到 a
和 b
变量中:
int a;
float b;
scanf("%d%f", &a, &b);
其中,%d
表示读取一个整数,%f
表示读取一个浮点数,&a
和 &b
表示将读取的数据存储到 a
和 b
变量中。
这里的 &
表示取地址,含义是,将变量所存储的位置告诉 scanf
函数,这样 scanf
函数才能将读取的数据存储到这个位置。
提示
虽然 scanf
可以指定输入的格式,但实际上当你不指定格式,只是一味的写 %?
时,scanf
还是会自动忽略两个输入之间的空白符,因此,反而不必担心输入的格式问题
提示
%
号表示法
%
号表示法用于指定输入输出的格式。例如,%d
表示输入输出一个整数,%f
表示输入输出一个浮点数,%c
表示输入输出一个字符,%s
表示输入输出一个字符串。
其它的一些用法会在之后提到,或可自行查阅
printf
printf
函数用于将数据输出到标准输出。
printf
函数的语法如下:
printf(const char *format, ...);
其中,format
是一个字符串,用于指定输出数据的格式,...
表示可以接受任意数量的参数,这些参数是要输出的数据。
例如,以下代码将整数 a
和浮点数 b
输出到标准输出:
int a = 10;
float b = 3.14;
printf("%d %f\n", a, b);
\n
表示换行符,它们俩合在一起表示一个字符
提示
\
转义符号
\
是转义符号,用于表示特殊的字符。例如,\n
表示换行符,\t
表示制表符,\"
表示双引号,\'
表示单引号。
在字符串中,如果需要输出反斜杠,需要使用两个反斜杠 \\
来表示一个反斜杠
提示
printf
的输出格式还能有更多花样,这里挑几个比较好用的:
- "%?d":输出一个整数,其中
?
表示输出的位数,如果输出的位数不足,则会在前面补空格,如果超出位数,则照常输出不会有变化 - "%?f":和上一个同理,但是是浮点数
- "%x":输出一个整数,以十六进制的形式输出,也可以用来输入
- "%X":和上一个同理,但是是十六进制的大写形式
- "%.?d":输出一个整数,其中
?
表示输出的位数,如果输出的位数不足,则会在前面补零,如果超出位数,则不会起作用 - "%.?f":输出一个浮点数,其中
?
表示保留小数的位数,如果输出的位数不足,则会在后面补零,如果超出位数,则会将超出的部分截掉 - "%?.?d":相当于之前提到的两种方法的结合
- "%?.?f":同理
getchar
getchar
函数用于从标准输入严格读取下一个字符,这意味着读取的字符也包含换行符、空格等。
getchar
函数的语法如下:
int getchar();
getchar
函数返回读取的字符的 ASCII
码值
例如,以下代码从标准输入读取一个字符,并将其存储到 c
变量中:
char c;
c = getchar();
putchar
putchar
函数用于将一个字符输出到标准输出。
putchar
函数的语法如下:
int putchar(int c);
putchar
函数接受一个整数参数 c
,表示要输出的字符的 ASCII
码值。
例如,以下代码将字符 c
输出到标准输出:
char c = 'A';
putchar(c);
运算符
算术运算符
算术运算符用于执行基本的算术运算,包括加法、减法、乘法、除法和取模运算。
+
:加法运算符,用于将两个操作数相加。-
:减法运算符,用于将一个操作数减去另一个操作数。*
:乘法运算符,用于将两个操作数相乘。/
:除法运算符,用于将一个操作数除以另一个操作数。对于整数除法,结果会舍去小数部分。%
:取模运算符,用于计算两个操作数的余数。++
:自增运算符,用于将操作数加 1。--
:自减运算符,用于将操作数减 1。
提示
++
和 --
这两个运算符有说法,它们可以放在操作数的前面,也可以放在操作数的后面,放在前面表示先加 1 或减 1,再进行运算,放在后面表示先进行运算,再加 1 或减 1
例如:
int a = 1;
int b = ++a; // a = 2, b = 2
int c = a++; // c = 2, a = 3
位运算符
位运算符用于对整数进行位操作。
&
:按位与运算符,用于将两个操作数的对应位进行与运算。|
:按位或运算符,用于将两个操作数的对应位进行或运算。^
:按位异或运算符,用于将两个操作数的对应位进行异或运算。~
:按位取反运算符,用于将操作数的所有位进行取反运算。<<
:左移运算符,用于将操作数的所有位向左移动指定的位数。>>
:右移运算符,用于将操作数的所有位向右移动指定的位数。
注意
位运算符的优先级较低,因此在使用时需要注意运算符的优先级,建议多使用括号来明确运算顺序。
提示
位运算的速度比算术运算快很多,因此,在一些可以用位运算符替代算术运算的地方,可以试试使用位运算符!
特殊的加减方式
对于变量,除了 x = x + 1
这种赋值方法,你也可以使用类似 x += 1
这种赋值方法,即将运算符提前,这样的写法对于大部分的二目运算符都可以使用。这样写的好处是代码更加简洁,但请确定好运算符之间的运算顺序,否则可能会出现意想不到的结果