运算符
使用运算符表示算术运算
基本算术运算 = + - * /
数据对象:存储值的数据存储区,即一块内存;
标识对象的方法:变量名、指定数组的元素,结构的成员,指针表达式,即指定具体某一块内存;
运算对象:“项”,是运算符操作的对象
有几个运算对象参与就叫几元运算符
优先级
先算括号内,递增或递减,再算乘除,再算加减,
1、优先级高的先算
2、共享同一运算对象的相同优先级的运算符,编译器会自行选择求值顺序,有结合律(从左往右或从右往左)决定求值顺序
一个变量(出现在一个函数的多个参数中)或者(多次出现在一个表达式中),不要对该变量使用递增或递减运算符
算术运算符 +/- > 关系运算符 > 赋值运算符=
结合律从左往右
eg:
x>y+2 相当于 x>(y+2)
x=y>2 相当于 x=(y>2)
x!=y==z 相当于 (x!=y)==z
!与递增运算符优先级相同,只比圆括号优先级低,比乘法运算符高
&&优先级大于||,比关系运算符优先级低,比赋值运算符高
// 12*6+5*20、*和+共享6和5,根据优先级先算12*6 和 5*20
// 12/3*2,/和*共享3,根据结合律先算/
// y = 6 * 12 + 5 * 20; 规定先算*再算加,并未规定两个乘法哪个先算,结合律只针对共享同一运算对象的运算符
int top, score;
top = score = -(2 + 5) * 6 + (4 + 3 * (2 + 3)); // 圆括号,乘除,加减
printf("top = %d,score = %d\n", top, score);
表达式和语句
表达式
运算对象(变量、常量、变量常量的组合)+运算符
每个表达式都有一个值
根据(运算符优先级和结合律)规定顺序执行操作获取值
赋值表达式的值为右值
关系表达式的值为0,1
语句
C程序的基本构建块,大部分以分号结尾,可以改变值或调用参数。
一条语句相当于一条完整的计算机指令;
一条完整的指令可能只是语句的一部分。
表达式;即为表达式语句
;即为空语句
赋值表达式语句:为变量分配一个值,变量名+赋值运算符+表达式+分号
函数表达式语句:引起函数调用
赋值和函数调用都是表达式语句
声明:创建名称和类型,分配内存位置,不属于表达式语句,去掉分号既不是表达式也没有值
复合语句:需要用花括号括起来的一条或多条语句,称为块,整个复合语句被视为一条语句
主要目的:对表达式求值
副作用:对数据对象或文件的修改。例:给变量赋值,递增递减,printf显示的信息
序列点:程序执行的点,在该点上所有的副作用都在进入下一步之前发生、(分号、任何一个完整表达式的结束)、有助于分析后缀递增何时发生
完整表达式:不是子表达式
int guests = 0;
while (guests++ < 10)
printf("%d \n", guests);
/*guests++ < 10即是一个完整表达式,结束就是一个序列点,在此序列点处,会在执行printf之前发生副作用执行递增表达式,后缀会在完成比较之后才进行递增*/
int x, y = 0;
y = (4 + x++) + (6 + x++);
/*完整表达式是整个语句,分号标记,所以C只能保证执行下一条语句前递增x两次,并未指明先递增还是先求值*/
/*程序段 1*/
int index = 0, sam;
while (index++ < 10)
sam = 10 * index + 2; /* 没有花括号时,循环中只有一条语句,
从while行运行至下一个分号,返回循环测试处。
缩进并不影响编译,只是方便阅读*/
printf("sam = %d\n", sam); // 循环之后只调一次
/*程序段 2*/
index = 0;
while (index++ < 10)
{ /* 突出'块'。花括号,分号,while结构才会影响编译
while (index++ < 10) { 突出 '块附属于循环',缩进不影响编译
*/
sam = 10 * index + 2;
printf("sam = %d\n", sam); // 两句都属于while循环的一部分
}
= 赋值运算符
赋值表达式语句:把值储存到内存位置上
cvb=2002; //把等号右边的值2002赋给左边的变量cvb指向的内存,赋值行为从右往左进行
i=i+1; //找出变量i的值,把该值加1,然后把新值赋值变量i
左值:标识对象的标签,指定一个对象,引用内存地址,可用在=左侧,const变量不可修改;
可修改左值:标识可修改的对象
等号左边必须引用一个存储位置,必须是可修改的左值(变量名、指针);
右值:能赋值给可修改左值的量,且本身不是左值,(常量,变量、可求值的表达式,不可修改的左值);
int ex;
int why;
int zee; // 以上均是可修改左值
const int TWO = 2; // TWO为不可修改左值,此为赋值,除了赋值只可放在赋值表达式的右侧
why = 42; // 42为右值(不可引用内存位置,不可赋值)
zee = why;
ex = TWO * (why + zee);
/* why和zee属于可修改左值,都标识了可赋值的数值对象,
表达式(why + zee)属于右值(不可表示特定内存位置,不能赋值)*/
int jane, cheeta, tarzan;
cheeta = tarzan = jane = 68; // 可以三重赋值,从左往右
printf(" cheeta tarzan jane\n");
printf("First round score %4d %8d %8d\n", cheeta, tarzan, jane);
其他赋值运算符
+= -= *= /= %= |= >>= <<=与=优先级相同
a += b; // a=a+b
a -= b; // a=a-b
a *= b; // a=a*b
a /= b; // a=a/b
a %= b; // a=a%b
a |= b; // a=a|b
a ^= b; // a=a^b
a >>= b; // a=a>>b
a <<= b; // a=a<<b
x*= 3*y +12 //相当于 x=x*(3*y+12)
算术运算符
+
加法运算符:二元运算符,需要两个运算对象:
相加的值是变量/常量
计算机会查看加法运算符左右的变量,相加之后即为表达式结果
一元+运算符:不会改变运算对象的值或符号,不建议使用
-
减法运算符:二元运算符,需要两个运算对象:
减号左侧的值减去右侧的值
一元-运算符:表明或改变一个值的代数符号,只需要一个运算对象
*
可以用乘法代替平方
#define SQUARES 64 // 棋盘中的方格数
const double CROP = 2E16; // 世界小麦年产谷粒数
double current, total;
int count = 1;
printf("square grains total ");
printf("fraction of \n");
printf(" added grains ");
printf("world total\n");
total = current = 1.0; /*从 1颗谷粒开始*/
printf("%4d %13.2e %12.2e %12.2e\n", count, current, total, total / CROP);
while (count < SQUARES)
{
count = count + 1;
current = 2.0 * current; /*下一个方格谷粒翻倍 */
total = total + current; /*更新总数*/
printf("%4d %13.2e %12.2e %12.2e\n", count, current, total, total / CROP);
}
printf("That's all.\n");
/
左侧是被除数,右侧是除数,
浮点数除法结果是小数
整数除法结果是整数,小数部分会被丢弃,不会四舍五入,过程称为(趋零)截断。
混合整数和浮点数计算结果是浮点数,编译器会把两个运算对象转换成相同的类型
printf("integer division: 5/4 is %d \n", 5 / 4); // 小数直接丢
printf("integer division: 6/3 is %d \n", 6 / 3);
printf("integer division: 7/4 is %d \n", 7 / 4);
printf("integer division: -19/5 is %d \n", -19 / 5);//-3.8,小数部分直接丢 -3
printf("floating division: 7./4. is %1.2f \n", 7. / 4.);
printf("mixed division: 7./4 is %1.2f \n", 7. / 4); // 会把两个运算对象转换成相同的类型。整数转换为浮点数
标准数学库 pow() 可用于指数运算
%
只能用于整数运算,左侧整数除以右侧整数的余数
负数取模:a%b=a-(a/b)*b
eg -11%5= -11-(-11/5)*5= -1
// min_sec.c -- 把秒数转换成分和秒
#define SEC_PER_MIN 60 // 1分钟60秒
int sec, min, left;
printf("Convert seconds to minutes and seconds!\n");
printf("Enter the number of seconds (<=0 to quit):\n");
scanf("%d", &sec); // 读取秒数
while (sec > 0) // 输入0或负值时停止循环、计数器超出给定的大小时停止循环
{
min = sec / SEC_PER_MIN; // 截断分钟数
left = sec % SEC_PER_MIN; // 剩下的秒数
printf("%d seconds is %d minutes,%d seconds,\n",sec, min, left);
printf("Enter next value (<=0 to quit):\n");
scanf("%d", &sec); // 每次循环都会修改被测试的变量值
}
printf("Done!\n");
++
运算对象递增1
前缀模式++a:先加后用
后缀模式a++:先用后加
只能影响一个变量,影响一个可修改的左值
int ultra = 0, super = 0;
while (super < 5)
{
super++;
++ultra; // 两者无区别
printf("super = %d,ultra = %d \n", super, ultra);
}
double shoe, foot;
shoe = 2.0;//后面要递增,先减1
while (++shoe < 18.5)
/*shoe的值会先加1,然后在做比较,如果成立,执行一次,
**更简洁,(判断是否继续循环)和(改变待测试的元素)放在一起避免忘记更新导致无限循环
** 循环范围 3-18
shoe++ < 18.5 先比较再递增,循环范围3-19*/
{
foot = SCALE * shoe + ADJUST;
printf("%10.1f %20.2f inches\n", shoe, foot);
}
int a = 1, b = 1;
int a_post, pre_b;
a_post = a++; // 后缀递增:先赋值再递增 q=2*a++;先q=2*a,在a++
pre_b = ++b; // 前缀递增:先递增再复制
printf("a a_post b pre_b \n");
printf("%1d %5d %5d %5d\n", a, a_post, b, pre_b);
int y, n, nextnum;
y = 2;
n = 3;
nextnum = (y + n++) * 6; // nextnum = (2 + 3)*6 = 5*6 = 30,只有在使用之后n才会递增为4
printf("%1d %5d %5d\n", y, n, nextnum);
- -
运算对象递减1
前缀模式–a:先减后用
后缀模式a–:先用后减
只能影响一个变量,影响一个可修改的左值
一个变量出现在一个函数的多个参数中或多次出现在一个表达式中不要对该变量使用递增或递减
#define MAX 100
int count = MAX + 1;
while (--count > 0) //范围100-1
{
printf("%d bottles of spring water on the wall,"
"%d bottles of spring water!\n",count,count);
printf("Take one down and pass it around,\n");
printf("%d bottles of spring water!\n\n", count - 1);
}
//一些错误的用法
//print("%10d %10d \n",num,num*num++);函数可能会先对最后一个参数求值
//ans = num/2+5*(1+num++);编译器可能会先计算5*(1+num++)
//y=n++ + n++;对y求值时,编译器可能使用n的旧值2或3次
关系运算符
关系表达式中间的运算符,比较左右两侧的值
关系为真/假,关系表达式值为1/0
可以比较字符,使用ASCII,while(ch!=‘$’)
不可以比较字符串
比较浮点数时尽量使用<和>,浮点数的舍入误差可能导致在逻辑上应该相等的两个数不相等
fabs()返回浮点数的绝对值
0为假,所有的非0都为真
#include <math.h>
const double ANSWER = 3.14159;
double response;
printf("What is the value of pi?\n");
scanf("%lf", &response);
while (fabs(response - ANSWER) > 0.0001)
{ // 只有当用户输入的值和正确值之间相差0.0001时才结束循环停止输入,浮点数比较不能直接用==,只能用差值<0.0001
printf("Try again!\n");
scanf("%lf", &response);
}
printf("Close enough!\n");
int true_val, false_val;
true_val = (10 > 2); // 关系为真的值为1,所有非零值都视为真,只有0是假
false_val = (10 == 2); // 关系为假的值为0
printf("true = %d;false = %d \n", true_val, false_val);
int n = 3;
while (n)
printf("%2d is true\n", n--);
printf("%2d is false\n", n); // 只有0是假
n = -3;
while (n)
printf("%2d is true\n", n++);
printf("%2d is false\n", n);
/*while(n)当n=-3时也为真
while(goats)相当于while(goats!=0)只有在goats为0时为假
while(status=1)把1赋给status,赋值表达式的值为左值,为1,相当于死循环
while(5==canoes)便于编译器检查错误,检查值是否为5
while(5= canoes)语法错误
*/
_Bool类型
_Bool类型的变量只能存储1或0
其他非零值赋给_Bool会被设置为1
#include<stdbool.h>
bool作为_Bool的别名,true=1,false=0
long num;
long sum = 0L;
_Bool input_is_good;
printf("Please enter an integer to be summed ");
printf("(q to quit):");
input_is_good = (scanf("%ld", &num) == 1);
while (input_is_good)
{
sum = sum + num;
printf("Please enter next integer (q to quit):");
input_is_good = (scanf("%ld", num) == 1);
// 从优先级考虑scanf()不需要用括号括起来,为了提高代码可读性
printf("Those integers sum to %ld.\n", sum);
}