欢迎小伙伴的点评✨✨ 本篇章系列是对C语言的深度思考和总结、关于C语言内容会持续更新。
文章目录
- 前言
- 一、C运算符
- 1.1、算数运算符
- 1.2、关系运算符
- 1.3、逻辑运算符
- 1.4、位运算符
- 1.5、赋值运算符
- 1.6、条件运算、指针运算、字节运算
- 1.7、C中的运算优先级
- 二、不同类型数据间的混合运算
- 三、强制类型转换运算符
- 四、C语句
- 4.1、C语句的作用和分类
- 4.2、最基本的语句——赋值语句
- 4.3、赋值表达式和赋值语句
- 4.4、变量赋初值
前言
几乎每一个程序都需要进行运算,对数据进行加工处理,否则程序就没有意义了。要进行运算,就需规定可以使用的运算符。C语言的运算符范围很宽,把除了控制语句和输入输出以外几乎所有的基本操作都作为运算符处理,例如将赋值符“=”作为赋值运算符、方号作为下标运算符等。
一、C运算符
C语言提供了以下运算符:
(1)算术运算符 (+(加) -(减) *(乘) /(除) %(整除余0) ++(自增) --(自减) )
(2)关系运算符 ( >(大于) < (小于) == (相等) >=(大于或等于) <=(小于或等于) !=(不相等) )
(3)逻辑运算符 ( ! (非) &&(与) ||(或) )
(4)位运算符 ( <<(位左移) >>(位右移) ~(位取反) |(位或) &(位与) ^(异或))
(5)赋值运算符 ( =及其扩展赋值运算符)
(6)条件运算符 ( ?:(条件表达式))
(7)逗号运算符 ( , )
(8)指针运算符 和( * (指针) 和 &(取地址))
(9)字节运算符 ( (字节计算)sizeof() )
(10)强制类型转换运算符 ( (类型) )
(11)成员运算符 ( . ->)
(12)下标运算符 ( [ ] )
(13)其他( 如函数调用运算符() )
1.1、算数运算符
下表显示了 C 语言支持的所有算术运算符。假设变量 A 的值为 1,变量 B 的值为 2,则:
运算符 | 描述 | 实例 |
---|---|---|
+ | 把两个数相加 | A + B = 3 |
- | 从第一个操作数中减去第二个操作数 | A - B = -1 |
* | 把两个数相乘 | A * B = 2 |
/ | 分子除以分母 | A / B = 1/2 |
% | 把两个数相乘 | B % A = 0 |
+ + | 自增运算符,整数值增加1 | A++ = 2 |
- - | 自减运算符,整数值减少1 | A-- = 0 |
1.2、关系运算符
下表显示了 C 语言支持的所有关系运算符。假设变量 A 的值为 1,变量 B 的值为 2,则:
运算符 | 描述 | 实例 |
---|---|---|
> | 检查左操作数的值是否大于右操作数的值,如果是则条件为真 | ( A > B) 为假 |
< | 检查左操作数的值是否小于右操作数的值,如果是则条件为真 | ( A < B) 为真 |
== | 检查两个操作数的值是否相等,如果不相等则条件为真 | ( A == B) 为假 |
>= | 检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真 | ( A >= B) 为假 |
<= | 检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真 | ( A <= B ) 为真 |
!= | 检查两个操作数的值是否相等,如果不相等则条件为真 | ( A != B ) 为真 |
1.3、逻辑运算符
下表显示了 C 语言支持的所有关系逻辑运算符。假设变量 A 的值为 1,变量 B 的值为 0,则:
运算符 | 描述 | 实例 |
---|---|---|
&& | 称为逻辑与运算符。如果两个操作数都非零,则条件为真 | ( A && B) 为假 |
丨丨 | 称为逻辑或运算符。如果两个操作数中有任意一个非零,则条件为真 | ( A 丨丨 B) 为真 |
! | 称为逻辑非运算符。用来逆转操作数的逻辑状态。如果条件为真则逻辑非运算符将使其为假 | !( A && B) 为真 |
1.4、位运算符
位运算符作用于位,并逐位执行操作。假设如果 A = 60,且 B = 13,现在以二进制格式表示,它们如下所示:
A = 0011 1100
B = 0000 1101
A&B = 0000 1100
A|B = 0011 1101
A^B = 0011 0001
~A = 1100 0011
运算符 | 描述 | 实例 |
---|---|---|
<< | 二进制左移运算符。将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。 | A<<2 , 即1111 0000 |
>> | 二进制右移运算符。将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。 | A>>2 , 即0000 1111 |
~ | 取反运算符,按二进制位进行"取反"运算。 | ~A , 即1100 0011 |
丨 | 按位或运算符,按二进制位进行"或"运算。运算规则:0丨0=0; 0丨1=1; 1丨0=1; 1丨1=1; | ( A 丨 B) ,即0011 1101 |
& | 按位与操作,按二进制位进行"与"运算。运算规则:0&0=0; 0&1=0; 1&0=0; 1&1=1; | ( A & B) ,即0000 1100 |
^ | 异或运算符,按二进制位进行"异或"运算。运算规则:0 ^ 0=0; 0 ^ 1=1; 1 ^ 0=1; 1 ^ 1=0; | !( A ^ B) ,即0011 0001 |
1.5、赋值运算符
下表列出了 C 语言支持的赋值运算符:
运算符 | 描述 | 实例 |
---|---|---|
= | 简单的赋值运算符,把右边操作数的值赋给左边操作数 | C = A + B 将把 A + B 的值赋给 C |
+= | 加且赋值运算符,把右边操作数加上左边操作数的结果赋值给左边操作数 | C += A 相当于 C = C + A |
-= | 减且赋值运算符,把左边操作数减去右边操作数的结果赋值给左边操作数 | C -= A 相当于 C = C - A |
*= | 乘且赋值运算符,把右边操作数乘以左边操作数的结果赋值给左边操作数 | C*= A 相当于 C = C* A |
/= | 除且赋值运算符,把左边操作数除以右边操作数的结果赋值给左边操作数 | C /= A 相当于 C = C / A |
%= | 求模且赋值运算符,求两个操作数的模赋值给左边操作数 | C %= A 相当于 C = C % A |
<<= | 左移且赋值运算符 | C <<= 2 等同于 C = C << 2 |
>>= | 右移且赋值运算符 | C >>= 2 等同于 C = C >> 2 |
&= | 按位与且赋值运算符 | C &= 2 等同于 C = C & 2 |
^= | 按位异或且赋值运算符 | C ^= 2 等同于 C = C ^ 2 |
丨= | 按位或且赋值运算符 | C 丨= 2 等同于 C = C 丨 2 |
1.6、条件运算、指针运算、字节运算
下表列出了 C 语言支持的其他一些重要的运算符。
运算符 | 描述 | 实例 |
---|---|---|
sizeof | 返回变量的大小 | sizeof(a) 将返回 4,其中 a 是整数 |
& | 返回变量的地址 | &a; 将给出变量的实际地址 |
* | 指向一个变量 | *a; 将指向一个变量 |
?: | 条件表达式 | 如果条件为真 ? 则值为 X : 否则值为 Y |
1.7、C中的运算优先级
运算符的优先级确定表达式中项的组合。这会影响到一个表达式如何计算。某些运算符比其他运算符有更高的优先级,例如,乘除运算符具有比加减运算符更高的优先级。
算术运算符是自左至右(左结合性),赋值运算符是自右至左(右结合性)
下表将按运算符优先级从高到低列出各个运算符,具有较高优先级的运算符出现在表格的上面,具有较低优先级的运算符出现在表格的下面。在表达式中,较高优先级的运算符会优先被计算。
类别 | 运算符 | 结合性 |
---|---|---|
后缀 | () [] -> . ++ - - | 从左到右 |
一元 | + - ! ~ ++ - - (type)* & sizeof | 从右到左 |
乘除 | * / % | 从左到右 |
加减 | + - | 从左到右 |
移位 | << >> | 从左到右 |
关系 | < <= > >= | 从左到右 |
相等 | == != | 从左到右 |
位与 AND | & | 从左到右 |
位异或 XOR | ^ | 从左到右 |
位或 OR | 丨 | 从左到右 |
逻辑与 AND | && | 从左到右 |
逻辑或 OR | 丨丨 | 从左到右 |
条件 | ? : | 从右到左 |
赋值 | = += -= *= /= %=>>= <<= &= ^= | 从右到左 |
逗号 | , | 从左到右 |
二、不同类型数据间的混合运算
在程序中经常会遇到不同类型的数据进行运算。如果一个运算符两侧的数据类型不同,则先自动进行类型转换,使二者成为同一种类型,然后进行运算。整型、实型、字符型数据间可以进行混合运算。规律为:
(1)十、一、*、/运算的两个数中有一个数为 float 或 double 型,结果是 double 型,因为系统将所有 float 型数据都先转换为 double 型,然后进行运算。
(2)如果 int 型与 float 或 double型数据进行运算,先把 int 型和 float 型数据转换为double 型,然后进行运算,结果是 double 型。
(3)字符(char)型数据与整型数据进行运算,就是把字符的 ASCII 代码与整型数据进行运算。如:12+‘A’,由于字符A的ASCII代码是65,相当于12+65,等于77。如果字符型数据与实型数据进行运算,则将字符的 ASCII 代码转换为 double 型数据,然后进行运算。
三、强制类型转换运算符
可以利用强制类型转换运算符将一个表达式转换成所需类型。例如:
(double)a (将a转换成 double 型)
(int)(x+y)(将x+y的值转换成 int 型)
(float)(5%3)(将5%3的值转换成 float 型)
其一般形式为
(类型名)(表达式)
注意,表达式应该用括号括起来。如果写成
(int)x+y
则只将x转换成整型,然后与y相加。
需要说明的是,在强制类型转换时,得到一个所需类型的中间数据,而原来变量的类型未发生变化。例如:
a=(int)x
如果已定义x为 float 型变量, a为整型变量,进行强制类型运算(int)x后得到一个 int 类型的临时值,它的值等于x的整数部分,把它赋给a,注意x的值和类型都未变化,仍为 float 型。该临时值在赋值后就不再存在了。
从上可知,有两种类型转换。一种是在运算时不必用户干预,系统自动进行的类型转换,如3+6.5。另一种是强制类型转换。当自动类型转换不能实现目的时,可以用强制类型转换。如%运算符要求其两侧均为整型量,若x为 float 型,则x%3不合法,必须用(int)x%3。从附录C可以查到,强制类型转换运算优先于%运算,因此先进行(int)x 的运算,得到一个整型的中间变量,然后再对3求余。此外,在函数调用时,有时为了使实参与形参类型一致,可以用强制类型转换运算符得到一个所需类型的参数。
四、C语句
4.1、C语句的作用和分类
在前面的例子中可以看到:一个函数包含声明部分和执行部分,执行部分是由语句组成的,语句的作用是向计算机系统发出操作指令,要求执行相应的操作。一个C语句经过编译后产生若干条机器指令。声明部分不是语句,它不产生机器指令,只是对有关数据的声明。即一个C程序可以由若干个源程序文件(编译时以文件模块为单位)组成,一个源文件可以由若干个函数和预处理指令以及全局变量声明部分组成。一个函数由数据声明部分和执行语句组成。
C语句分为以下5类
(1)控制语句。控制语句用于完成一定的控制功能。C语言只有9种控制语句,它们的形式是:
① if( )…else… (条件语句)
② for( )… (循环语句)
③ while( )… (循环语句)
④ do…while( ) (循环语句)
⑤ continue (结束本次循环语句)
⑥ break (中止执行 switch 或循环语句)
⑦ switch (多分支选择语句)
⑧ return (从函数返回语句)
⑨ goto (转向语句,在结构化程序中基本不用goto语句)
上面9种语句表示形式中的( )表示括号中是一个“判别条件”,“…”表示内嵌的语句。
(2)函数调用语句。函数调用语句由一个函数调用加一个分号构成,例如:printf(" This is a Cstatement.");
其中 printf(“This is a C statement .”)是一个函数调用,加一个分号成为一个语句。
(3)表达式语句。表达式语句由一个表达式加一个分号构成,最典型的是由赋值表达式构成一个赋值语句。例如:
a=3是一个赋值表达式,而
a=3;是一个赋值语句。
可以看到,一个表达式的最后加一个分号就成了一个语句。一个语句必须在最后有一个分号,
分号是语句中不可缺少的组成部分,而不是 两个语句间的分隔符号。例如:
i=i+1 (是表达式,不是语句)
i=i+1;(是语句)
任何表达式都可以加上分号而成为语句,例如:
i++;
是一个语句,作用是使i值加1。
表达式能构成语句是C语言的一个重要特色。其实“函数调用语句”也属于表达式语句,因为函数调用(如 sin(x))也属于表达式的一种。只是为了便于理解和使用,才把“函数调用语句”和“表达式语句”分开来说明。
(4) 空语句。下面是一个空语句:
此语句只有一个分号,它什么也不做。那么它有什么用呢?可以用来作为流程的转向点(流程从程序其他地方转到此语句处),也可用来作为循环语句中的循环体(循环体是空语句,表示循环体什么也不做)。
(5)复合语句。可以用{ }把一些语句和声明括起来成为复合语句(又称语句块)。例如下面是一个复合语句:
{float pi=3.14159 , r=2.5 ,area; //定义变量area=pi*r*r;printf("area = %f" ,area);
}
可以在复合语句中包含声明部分(如上面的第2行),C 99允许将声明部分放在复合语句中的任何位置,但习惯上把它放在语句块开头位置。复合语句常用在 if 语句或循环中,此时程序需要连续执行一组语句。
注意:复合语句中最后一个语句末尾的分号不能忽略不写。
4.2、最基本的语句——赋值语句
在C程序中,最常用的语句是:赋值语句和输入输出语句。其中最基本的是赋值语句。程序中的计算功能大部分是由赋值语句实现的,几乎每一个有实用价值的程序都包括赋值语句。有的程序中的大部分语句都是赋值语句。
(1) 赋值运算符
赋值符号“=”就是赋值运算符,它的作用是将一个数据赋给一个变量。如a=3的作用是执行一次赋值操作(或称赋值运算)。把常量3赋给变量a。也可以将一个表达式的值赋给一个变量。
在赋值符=之前加上其他运算符,可以构成复合的运算符。
C语言采用这种复合运算符,一是为了简化程序,使程序精练,二是为了提高编译效率,能产生质量较高的目标代码。
(2)赋值表达式
赋值语句是在赋值表达式的末尾加一个分号构成的。
由赋值运算符将一个变量和一个表达式连接起来的式子称为“赋值表达式”。它的一般形式为
变量 赋值运算符 表达式
赋值表达式的作用是将一个表达式的值赋给一个变量,因此赋值表达式具有计算和赋值的双重功能。如a=3*5是一个赋值表达式。
对赋值表达式求解的过程是先求赋值运算符右侧的“表达式”的值,然后赋给赋值运算符左侧的变量。既然是一个表达式,就应该有一个值,表达式的值等于赋值后左侧变量的值。
例如,赋值表达式a=3 * 5,对表达式求解后,变量a的值和表达式的值都是15。
赋值运算符左侧应该是一个可修改值的“左值”(left value,简写为 lvalue)。
左值的意思是它可以出现在赋值运算符的左侧,它的值是可以改变的。并不是任何形式的数据都可以作为左值的,左值应当为存储空间并可以被赋值。
变量可以作为左值,而算术表达式a+b就不能作为左值,常量也不能作为左值,因为常量不能被赋值。能出现在赋值运算符右侧的表达式称为“右值”(right value,简写为 rvalue)。显然左值也可以出现在赋值运算符右侧,因而凡是左值都可以作为右值。
(3)赋值过程中的类型转换
如果赋值运算符两侧的类型一致,则直接进行赋值。如:
i=234 //设已定义i为整型变量
此时直接将整数234存入变量 i 的存储单元中。
如果赋值运算符两侧的类型不一致,但都是基本类型时,在赋值时要进行类型转换。类型转换是由系统自动进行的,转换的规则是:
(1)将浮点型数据(包括单、双精度)赋给整型变量时,先对浮点数取整,即舍弃小数部分,然后赋予整型变量。如果 i 为整型变量,执行“i=3.56;”的结果是使i的值为3,以整数形式存储在整型变量中。
(2)将整型数据赋给单、双精度变量时,数值不变,但以浮点数形式存储到变量中。如果有float 变量f,执行“f=23;”。先将整数23转换成实数23.0,再按指数形式存储在变量f 中。如将23赋给 double 型变量 d,即执行“d=23;”,则将整数23转换成双精度实数23.0,然后以双精度浮点数形式存储到变量d中。
(3)将一个 double 型数据赋给 float 变量时,先将双精度数转换为单精度,即只取6~7位有效数字,存储到 float 型变量的4个字节中。应注意双精度数值的大小不能超出 float 型变量的数值范围。
(4)字符型数据赋给整型变量时,将字符的 ASCII 代码赋给整型变量。如:
i='A' ; '//已定义i为整型变量
由于’A’字符的 ASCII 代码为65,因此赋值后 i 的值为65。
(5)将一个占字节多的整型数据赋给一个占字节少的整型变量或字符变量(例如把占4个字节的 int 型数据赋给占2个字节的 short 变量或占1个字节的 char 变量)时,只将其低字节原封不动地送到被赋值的变量(即发生“截断”)。
4.3、赋值表达式和赋值语句
在C 程序中,赋值语句是用得最多的语句。在C语句分类中,并没有看到赋值语句,实际上,C语言的赋值语句属于表达式语句,由一个赋值表达式加一个分号组成。其他一些高级语言(如BASIC,FORTRAN,COBOL,Pascal 等)有赋值语句,而无“赋值表达式”这一概念。这是C语言的一个特点,使之应用灵活方便。
前面已经提到,在一个表达式中可以包含另一个表达式。赋值表达式既然是表达式,那么它就可以出现在其他表达式之中。例如:
if((a=b)>0) max=a;
按一般理解,if后面的括号内应该是一个“条件”,例如可以是
if(a>0) max=a;
现在,在a的位置上换上一个赋值表达式 a=b,其作用是:先进行赋值运算(将b的值赋给a),然后判断a是否大于0,如大于0,执行 max=a。请注意,在 if语句中的a=b不是赋值语句,而是赋值表达式。如果写成
if((a=b;)>0) max=a; //“a=b;"是赋值语句
就错了。在 if 的条件中可以包含赋值表达式,但不能包含赋值语句。由此可以看到,C语言把赋值语句和赋值表达式区别开来,增加了表达式的种类,使表达式的应用几乎“无孔不入”,能实现其他语言中难以实现的功能。
注意:要区分赋值表达式和赋值语句。
赋值表达式的末尾没有分号,而赋值语句的末尾必须有分号。在一个表达式中可以包含一个或多个赋值表达式,但绝不能包含赋值语句。
4.4、变量赋初值
从前面的程序中可以看到:可以用赋值语句对变量赋值,也可以在定义变量时对变量赋以初值。这样可以使程序简练。如:
int a=3; //指定a为整型变量,初值为3
float f=3.56; //指定f为浮点型变量,初值为3.56
char c='a'; //指定c为字符变量,初值为'a'
也可以使被定义的变量的一部分赋初值。例如:
int a,b,c=5;
指定 a,b,c为整型变量,但只对c初始化,c的初值为5。
如果对几个变量赋予同一个初值,应写成
int a=3 ,b=3,c=3;
表示a,b,c的初始值都是3。不能写成
int a=b=c3;
一般变量初始化不是在编译阶段完成的(只有在静态存储变量和外部变量的初始化是在编译阶段完成的),而是在程序运行时执行本函数时赋予初值的,相当于执行一个赋值语句。
例如
int a=3;
相当于
int a; //指定a为整型变量
a = 3; //赋值语句,将3赋给a