笔者语:由于本文知识点存在相互嵌套,所以当你遇到有不理解的地方先不用着急,文章的后面会有解释。
基本运算符
-
赋值运算符:=
int num; num = 1; //num赋值为1 num = 2; //num赋值为2
对于初学者,在阅读该系列前几篇文章时可能会对代码中“=”的使用产生疑惑。在我们比较熟悉的数学当中。“=”的含义是两边的数字相等。但在编程中(几乎所有编程语言),“=”的作用是将右边的值赋值给左边。或者说,在数学中,“=”代表的是一种结果,表示两数相等。而在编程中,“=”可以理解为一种运算,即“让”左边的值等于右边的值,这个运算过程称为“赋值”。在理解这个的前提下,我们就能明白,为什么在C语言中,下面的代码是错误的。
int n=1,m=2; n + m = 1 + 2 ;
在编译时你会得到这样的报错
即赋值操作的左边必须是变量
除了上面的简单赋值外,我们还可以多重赋值。从上面我 们可以知道,赋值运算符是右结合的,因此下面两 条语句是等价的。
a = b = c = 0;
a = (b = (c = 0));
(补充:右结合指表达式从右往左运算,左结合指从左往右运算。本文提到的运算符中,只有赋值运算符“=”和 作一元运算符时的“+、-”是右结合,其余均为左结合。)
四则运算:+ - * /
四则运算我们肯定很熟悉了,但要注意,在计算机中,我们使用(*)来代替(×),用(/)来代替(÷)。
这四个预算符的作用都是将运算符两边的量(被称为操作数)进行相应的操作。
依照概念来说,我们把这四个运算符以及上面提到的“=”这种,需要两个操作数(符号两边都存在符合要求的量)的运算符,称为二元运算符。
-
符号运算符:-和+
“+”和“-”除了进行上面的运算,还有其他用法。在这里,“+”和“-”分别起到了正号和负号的作用,用法也很简单,就是直接加在我们想要表明正负的量前面就行了(“+”作正号使用的情况比较小)。对应于上面说到的二元运算符,正负号这种只需要一个数的运算符,我们称之为一元运算符。
int a; a = 1; a = -a; //a = -1
其他运算符
-
sizeof运算符:
sizeof是C语言提供的专门用于计算指定数据类型字节数的运算符。具体的使用方法会在介绍基本数据类型时进行说明。我们现在只需要记住,sizeof是一个关键字,是一个运算符,与我们后面要讲的函数不是一类东西。
-
求余(取模)运算符:%:
求余运算符只能用于整数运算。可以得到运算符左侧的整数除以右侧整数的余数。
int num; num = 13 % 5; //13÷5余3,所以num = 3
-
复合的赋值运算符:
我们需要了解的复合赋值运算符共有5个,分别为“+=”、“-=”、“*=”、“/=”、“%=”,需要注意到的是,这5个符号分别看作整体,中间是没有空格的。具体使用方法如下所示。
以下两条语句是等价的
n *= m + 1; n = n * (m + 1);
即一般形式及其等价表示为
变量X 运算符 op= 表达式; <=> 变量X = 变量X 运算符op 表达式;
我们可以很容易发现,使用复合的赋值运算书写形式上更简洁,事实上这样的执行效率会更高。
-
递增递减运算符: ++ 和–
“++”与“–”的运算逻辑是一致的,因此在这里只做递增运算符“++”的介绍。
“++”是一元运算符,作用是使作用变量的值+1,总共有两种使用方式。
第一种是“++”放在作用变量的前面,称为前缀模式。第二种是放在作用变量的后面,称为后缀模式。
这两种模式的区别在于进行运算的时间不同,我们通过下面的例子进行理解。
n1 = n2 = 1; m = n1++; //这里先进行赋值操作,即m = 1;再进行递增操作,即n1 = 2 m = ++n2; //这里先进行递增操作,即n2 = 2;再进行赋值操作,即m = 2
但我们单独运行n++和++n两个语句时得到的结果是一样的,都是n的值+1,此时我们可以把它们等价于n += 1,这时的运行效率是要比n = n + 1要更高一点。
注:类似于 (x * y)++ 这样的用法是无效的。通过定义我们可以知道,递增运算符只能改变一个变量(即一个可以修改的值),但在这里x * y是一个确定的结果,是不能修改的。我们可以将x * y的值赋值给一个变量再进行递增操作。
运算符优先级
但很多时候我们一条表达式会使用到多个运算符,这又应该怎么计算呢?这就涉及到一个概念,叫做运算符的优先级。我们通过数学的学习可以知道,在四则运算中,先算乘除,再算加减,有括号的先算括号内。这个原则在C语言中同样适用。
运算符 | 优先级(数值越大越高) |
---|---|
() | 5 |
++ 、– | 4 |
+、-(一元) | 3 |
*、/、% | 2 |
+、-(二元) | 1 |
= | 0 |
注意:不要混淆优先级和求值顺序。
m = 2;
n = 3;
num = (m + n++) * 6; //num = (2 + 3) * 6
根据优先级,先进行括号内运算,且n++的优先级高于作二元运算符时的“+”,但后缀模式下,n的值在运算完成后再递增,因此得到上面的式子。所以,根据优先级可以判断何时使用运算符,而递增运算符的性质决定了何时对n进行递增运算。
-
一点建议:
尽管复合赋值运算符和递增递减运算符十分方便,形式也非常简洁,但并不建议任何时候无脑使用。
a += a -= a * a++;
比如这种语句,可读性非常差,不仅他人阅读起来非常费劲,代码的编写者也很容易思绪混乱导致出错。本着降低错误率、提高代码可读性的观念,普遍不建议使用一句复杂的计算语句,而是分成几条语句进行计算。
常用数学函数
(下面函数在使用前应当在代码开始包含头文件<math.h>)
#include<math.h>
除了上面提到的基本的运算符,C语言的标准库还提供了很多函数用于更复杂的计算。
abs() | 求绝对值 |
---|---|
acos() | 求反余弦 |
asin() | 求反正弦 |
atan() | 求反正切 |
atan2() | 求反正切,按符号判定象限 |
ceil() | 求不小于某值的最小整数 (求上界) |
cos() | 求余弦 |
cosh() | 求双曲余弦 |
div() | 求商和余数 |
exp() | 求e的幂 |
fabs() | 求浮点数的绝对值 |
floor() | 求不大于某值的最大整数 (求下界) |
fmod() | 求模数 |
frexp() | 求数的科学表示法形式 |
labs() | 求长整型数的绝对值 |
ldexp() | 以科学计数法计算 |
ldiv() | 以长整型返回商和余数 |
log() | 自然对数 |
log10() | 以10为底的自然对数 |
modf() | 将一个数分解成整数和小数部分 |
pow() | 求幂 |
sin() | 求正弦 |
sinh() | 求双曲正弦 |
sqrt() | 求平方根 |
tan() | 求正切 |
tanh() | 求双曲正切 |
表达式和语句:
关于表达式和语句在本系列的第一篇文章中有所涉及,在这里进行更系统的说明。
-
表达式:
表达式由运算符和运算对象组成。最简单的表达式就是一个单独的运算对象。表达式有一个十分重要的特点,就是一定具有一个值,我们可以在此基础上建立更复杂的表达式。例子如下:
表达式 值 -4 + 6 2 c = 3 + 8 11 6 + (c = 3 + 8)(合法但不建议使用) 11 5 > 3 1 (注:最后这个5>3的值为1的具体情况会在后面的循环结构中的关系运算符中讲到)
有了这个概念,我们就更好理解为什么n + m = 1 + 2 在C语言中是不合法的了。我们只需要记住,表达式可以看作一个整体,这个整体是有一个值的。
-
语句:
语句是C语言程序的基本单元,一条语句就相当于一条完整的计算机指令。在C语言中,大部分语句都是以分号结尾的,遇到不以分号结尾的我都会特殊说明。
n = 4 //表达式 n = 4; //语句
在编写C语言代码时,通常空格的有无以及数量是没有限制的,但需要满足没有歧义这一前提。
n = m; //这两个语句是等价的的 n=m;
num = 1; //这两个语句是不等价的,下面的语句是非法的的 n u m = 1;
同时,C语言代码中的所有字符(除了字符串的内容为外,这个在讲到字符串时会说明),都必须是英文字符。比如中文输入法打出来的分号或双引号会由于编译器无法检测到该符号而导致程序编译失败,这是非常容易出现错误的地方。当然,在很多编译器上,中英文符号会给予不同的显示以便于我们发现错误。
的
n=m;```c num = 1; //这两个语句是不等价的,下面的语句是非法的的 n u m = 1;
同时,C语言代码中的所有字符(除了字符串的内容为外,这个在讲到字符串时会说明),都必须是英文字符。比如中文输入法打出来的分号或双引号会由于编译器无法检测到该符号添加链接描述而导致程序编译失败,这是非常容易出现错误的地方。当然,在很多编译器上,中英文符号会给予不同的显示以便于我们发现错误。
(注:未经允许不得转载)
我理解的C语言 #6:选择控制结构与goto语句
我理解的C语言 #5:循环控制结构
我理解的C语言 #4:格式化输入与输出
我理解的C语言 #3:基本数据类型与类型转换
我理解的C语言 #1:常量与变量以及命名
我理解的C语言 #0:第一个程序——hello world