C语言—运算符、表达式和语句

基本运算符

C的基本运算符包括:+、-、*和/,C没有指数运算符,但是C的标准数学库中提供了一个pow()函数用于指数运算,例如pow(3.5,2.2)返回3.5的2.2次幂。
基本运算符这里讲一些注意点
这里先说几个术语:数据对象、左值、右值和运算符

  • 用于存储值的数据存储区域统称为 数据对象(data object)
  • 左值是C语言的术语,用于表示特定数据对象的名称或表达式。因此,对象指的是实际的数据存储,而左值是用于标识或定位存储位置的标签。对于早期C语言,提到左值意味着:
    1.它指定一个对象,可以引用内存中的地址。
    2.它可用在赋值运算符的左侧。
    但是后来,标准中新增了const限定符。用const创建的变量不可修改。因此const标识符满足以上的第一项而不满足第二项。为此,C标准新增了一个术语:可修改的左值(modifiable lvalue),用于标识可修改的对象
  • 右值指的是能赋值给可修改左值的量,且本身不是左值
除法运算符:/

C语言中,整数除法的小数部分会被舍弃,这一过程称为截断
混合整数和浮点数计算的结果是浮点数。计算机不能真正用浮点数处以整数,编译器会把两个对象转换成相同类型,例如printf("7./4 is %1.2f \n",7./4)中,在进行除法运算前,整数会被转换成浮点数`。

运算符优先级

优先级从高到低

运算符结合律
()从左往右
±(一元)从右往左
* /从左往右
± (二元)从左往右
=从右往左

考虑以下语句:
y = 6 * 12 + 5 * 20;
当不同运算符共享同一个运算对象时,优先级决定了求值顺序,即先进行两个乘法。但优先级并未规定先进行哪一个乘法,计算机根据不同的硬件来决定先计算前者还是后者,可能在一种硬件上采用某种方案的效率更高,而在另一种硬件上采用另一种方案效率更高。但无论采用哪种方案,表达式都是简化成 72 + 100,虽然这并不影响结果,但是读者可能误用乘法从左往右的结合律来解释这一点。 结合律只适用于同样优先级运算符共享同一运算对象的时候,例如表达式:12 / 3 * 2中,/ 和 * 的优先级相同,共享对象为3,因此结合律在这种情况起作用。

其它运算符

siezeof运算符和size_t类型

C头文件系统中使用typedef 把 size_t 作为 unsigned int 或 unsigned double 的别名。这样在使用size_t 类型时,编译器会根据不同的系统替换标准类型(有的系统sizeof()返回的是unsigned int类型,有的返回的是unsigned long 类型,引用size_t 统一返回值)。C99做了进一步调整,新增%zd 转换说明用于printf()显示size_t 类型的值。若系统不支持,可用%u 或 %lu替代 %zd。

求模运算符:%
  • 求模运算符只能用于整数,不能用于浮点数。
    求模运算符常用于控制程序流,例如,假设你正在设计一个账单预算系统,每3个月要加进一笔额外费用,这种情况可以在程序中对月份求模3,并检查结果是否为0。若为0,则加进额外费用。
    除法运算符得出的商的正负号和我们平常正常计算中的规则一致,而求模运算符得出的值的正负号与被除数一致。例如:
    11 % 5 得1 ;
    -11 % -5 得-1;
    -11% 5 得 -1
递增运算符:++
shoe = 3.0;
while (shoe < 18.5)
{
	foot = SCALE * size + ADJUST;
	printf("%10.1f  %20.2f inches\n",shoe,foot);
	++shoe;
}
//可将复合语句中的自增放到while条件语句中去,
//即while(++shoe<18.5),并改写shoe的初始值为2.0。

这么做的好处一是紧凑的代码能让程序更为简洁,可读性更高,更重要的是它把控制循环的两个过程集中在一个地方,这样就不容易忘记更新循环

递减运算符:- -
优先级

递增运算符和递减运算符都有很高的结合优先级,只有圆括号的优先级比它们高。因此,x * y++表示的是(x) * (y++),而不是(x * y)++,但是后者无效,因为递增和递减运算符只能影响一个变量(即只能影响一个可修改的左值),而组合x*y本身是不可修改的左值

不要自作聪明

建议不要一次性复杂地使用太多递增递减运算符。
例如在函数中:
printf("%10d,%10d\n",num,num*num++);
C语言中,编译器可以自行选择先对函数中的哪个参数求值,如果在函数参数表中使用递增递减运算符,可能造成不必要的麻烦,例如以上程序只能在某些系统中正常运行。该程序的问题是,当printf()获取待打印的值时,可能先对最后一个参数(num * num++)求值,这样在获取其它参数的值前就先递增了num,得到结果6,25;但是它也有可能从右往左执行,对最右边的num(++作用的num)使用5,然后递增num,对第2个num和最左边的num使用6,结果得到:6,30。
类似这样的语句也会导致一些麻烦:
ans = num/2 + 5*(1 + num ++);
编译器可能不会像你所想的先计算第1项(num/2)接着计算第2项(5 * (1 + num ++)),而是先计算第二项,递增num,然后再num/2中使用num递增后的新值。
遵循以下规则,可以避免类似的问题。

  • 如果一个变量出现在一个函数的多个参数中,不要对该变量使用递增或递减运算符。
  • 如果一个变量多次出现在一个表达式中,也不要对该变量使用递增或递减运算符。

表达式和语句

  • 每个表达式都有一个值
语句

语句是C程序的基本构建块。一条语句相当于一条完整的计算机指令,但并不是所有的指令都是语句。
例如: x = 6 + (y = 5)
该语句中子表达式 y = 5 是一条完整的指令,但它只是语句的一部分而不是一条语句。

副作用和序列点

副作用是对数据对象或文件的修改。如语句: states = 50; 它的副作用是将变量的值设置为50。副作用?这似乎更像是主要目的!但从C语言的角度看,主要目的是对表达式求值。跟赋值运算符一样,递增递减运算符也有副作用,但使用它们的主要目的就是使用其副作用。
序列点是程序的执行点,在该点上,所有的副作用都在进入下一之前发生。在C语言中,语句中的分号标记了一个序列点。意思就是,在一条语句中,赋值、自增、自减运算符对运算对象做的改变必须在程序执行下一条语句之前完成。

类型转换

1.当类型转换出现在表达式时,无论是unsigned int 还是 signed的 char 和 short 都会被自动转换成int ,必要时转换成unsigned int (如果 short 与 int的大小相同时,unsigned short 就比int 大,这种情况下 unsigned short被转换成unsigned int)。由于都是从较小类型转换成较大类型,所以这种转换被称为升级
2.涉及两种运算类型的运算,两个值会被分别转换成两种类型的更高级别。
3.类型的级别从高到低依次是: long double>double>float?unsigned long long>long long>unsigned long >long?unsigned int >int。
4.赋值表达式语句中,计算的最终结果会被转换成被赋值变量的类型。这个过程可能导致类型升级降级
5.当作为函数参数传递时,char和short被转换成int,float被转换成double。

类型升级通常不会有什么问题,但是类型降级会导致真正的麻烦。因为较低类型可能放不下较高类型的整个数字。例如一个8位的char变量存储101没问题,但是存不下22334.
如果待转换的值与目标类型不匹配怎么办?
1.目标类型是无符号整形,且待赋的值是整数时,额外的位会被忽略。例如,目标类型是8位的unsigned char,待赋的值是原始值求模256。
2.如果目标类型是一个有符号的整形,且待赋的值是整数,结果因实现而异。
3.如果目标类型是一个整型,且待赋的值是浮点数,该行为未定义。
强制类型转换符
格式:(目标类型)待转换的量

int mice;
mice = 1.6 + 1.7;
mice = (int)1.6 + (int)1.7;

第一行使用自动类型转换,首先1.6+1.7得3.3,然后为了匹配int类型的变量,3.3被类型转换截断为整数3.
第二行,1.6和1.7在相加之前都被转换成整数1,所以把1+1的和赋给变量mice。

带参数的函数

假设自定义一个函数 void pound(int n){…},在主函数前声明函数原型 void pound(int n),向函数传递一个char类型的‘!’,因为原型的存在,编译器会把该参数转换成int类型,在ASCII中‘!’的数值是33,在系统中,该参数从1字节的33变成4字节的33以满足函数的要求。同样地,向函数传递一个浮点数也会因为原型的存在而被转换成合适的类型。
而在ANSI C之前,C语言使用的是函数声明而不是函数原型,函数声明只指明了函数名和返回类型,没有指明参数类型。即使缺少函数声明,char和short类型也会被自动升级为int类型,但是float会被自动升级为double类型传输给函数,从而导致输出的内容不正确。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值