“C和指针” 学习笔记 --章节五

操作符和表达式
‘+’ ‘-’ * / %
除了 % 运算符,其余四个都是既适用于整型又适用于浮点型,% 返回的是余数而不是商。
左移位运算符 << 右移位运算符 >>
在左移位,值最左面的几位被丢弃,右面多出来的几个空位由 0 补齐
右移位分为两种情况
1、逻辑移位 左侧多出来的空位补 0
2、算术移位 左侧多出来的空位补 0 还是补 1 由符号位决定;
标准说明,无符号值执行的所有移位操作都是逻辑移位,但对于有符号值,到底采用逻辑移位还是算术移位取决于编译器。
a << -5 这种移位的行为是未定义的,它是由编译器决定的;

位操作符
& | ^
与 或 异或

赋值运算符
赋值是表达式的一种,所以只要允许出现表达式的地方都允许赋值,
赋值操作符把右操作数的值存储于左操作数的指定位置。但赋值也是一个表达式,表达式就具有一个值—赋值表达式的值就是左操作数的新值;如下语句所示
a = x = y + 3;
在这条语句中,认为 a 和 x 被赋予相同值的说法是不正确的;
如果 x 是一个字符变量,那么 y + 3 就会被截去一段,那么 a 得到的值就是被截断后的值;
char ch;
while((ch = getchar()) != EOF) …
EOF 需要的位比字符型提供的位更多,getchar 返回一个整型值,整型值截断后保存在 char 中,然后这个 char 被提升为整型再与 EOF 比较,这不出错才奇了怪了……

复合赋值符
目前为止介绍的所有型号都有一种复合的形式
+= -= *= /= %=

= <<= |= &= ^=
以 += 为例
a += expression 读作把 expression 加到 a; 功能实际上相当于下面的表达式
a = a + (expression);
注意括号,必须要确保表达式在执行加法运算之前已经被完整求值;

单目运算符
! ++ – - & sizeof ~ + * 类型
! 对操作数执行逻辑反,原值为假,那么在取反之后,值为真;
~ 对整型操作数进行按位取反
‘-’ 产生操作数的负值,+ 没有任何作用
& 获得操作数的地址
‘*’ 间接访问操作符,访问指针所指向的值
sizeof 判断操作数类型长度,以字节为单位; 操作数既可以是表达式也可以是两边加上括号的类型名,当然在表达式操作数两边都加上括号也是合理的 比如
sizeof(int) sizeof x sizeof(x)
当 sizeof 的操作数是个数组名时,他将返回数组的长度,以字节为单位;

#include <stdio.h>

int main () {
    long long  i = 0;
    int j = 2;
    int x = sizeof (i = j + 1);
    printf("x = %d i = %d\n", x, i);
}

// 打印结果
// x = 8 i = 0

实际上就是 sizeof 括号内的表达式没有执行赋值操作,同时 sizeof 实际上计算的是等号左侧的 i 的长度。
(类型运算符) 被称为强制类型转换,显式把表达式的值转为另外的类型,例 为了获得整型变量 a 对应的浮点数值 可以这样写
(float)a
强制类型转换具有相当高的优先级,如果说把强制类型转换放在一个表达式前面他只会改变表达式第一个项目的类型,如果要对整个表达式进行类型转换,必须要把整个表达式用括号括起来;

++ 和 – 自增和自减运算符;这两个操作符均有两种不同的变型,前缀和后缀形式;这两个操作符操作的是变量(实际上是左值)而不是表达式,这点要注意!!
使用规则很好记,在操作数之间的操作符在变量值被使用之前增加他的值,在操作数之后的操作符在变量值被使用之后才增加他的值;

#include <stdio.h>

int main () {
    int x = 1, z = 0;
    z = x++;
    printf("z = %d\n", z);
    z = 0;
    x = 1;
    z = ++x;
    printf("z = %d\n", z);

    return 0;
}

// result
// z = 1
// z = 2

实际上来说
a++ 操作符的返回结果是变量值的拷贝; 所以 a++ = 10,这种操作是相当错误的;
int a = 10;
a++ = 10;
这种写法基本等价于
int a = 10;
10 = 10;
a = a + 1;

关系操作符

= < <= != ==
如果复合表达式,返回 1;如果不符合表达式,返回 0; 这里的 1 和 0 是整数,而不是 bool
if( exprssion==0 ) 等价于 if( !exprssion ) 使用这种形式的 if 语句需要注意的是,由于 ! 优先级很高,表达式内如果包含了其他操作符,最好把表达式放在一对括号内。

逻辑操作符
&& ||
使用这两个运算符有一个非常有趣的规则叫做短路求值
对于 a && b 如果 a操作数为假,那么就不在执行 b
对于 a || b 如果 a操作数为真,那么就不在执行 b

条件运算符
expression1? exprssion2:expression3;
如果 expression1 为真(非0值),那么将会执行 expression2 ,否则执行 expression3;

逗号运算符
逗号运算符将多个表达式分割开来,这些表达式自左至右逐个求值,最后的表达式的值就是整个逗号表达式的值

while( x<10 )
	b += x,
	x += 1;

逗号运算符把两条语句整合成一个语句,从而避免了在两端加上括号;

下标引用、函数调用和结构成员
下标引用操作符是一对方括号,他具有两个操作数:一个数组名和一个索引值。下标引用并不局限于数组,C 的下标值总是从 0 开始,除了优先级不同外,下标引用操作和间接访问表达式是等价的
arr[10] 等价于
*(arr + (10))
函数调用操作符
他能接受一个或多个操作数,第一个参数就是要调用的函数名,剩余的操作数就是传递给函数的参数,把函数调用以操作符的方式实现意味着表达式可以代替常量作为函数名
. 和 -> 用于访问结构成员;如果 s 是一个结构变量,那么 s.a 就是访问s结构体中的 a 成员;如果 s 是一个指向某个结构体的指针,那么 s->a 就是访问s结构体中的 a 成员;

左值和右值
左值是指可以出现在赋值符号左面的东西,右值是指可以出现在赋值符号右面的东西;例如:
a = b + 15;
a 是一个左值,他标识了一个可以存储结果的地点,b + 15是一个右值,因为他只指定了一个值,如果我们反过来写
b + 15 = a;
那么这条语句是非法的;为什么会这样?
当 b + 15 被计算时,他的结果必然保存在机器的某个地方,但是程序员是不能预测出他出现在哪里的,也无法保证这个值下一次还会存储在这个地方,因此,这个表达式不是一个左值;同样的理由,字面值常量也不是一个左值。
看起来是变量可以作为左值而表达式不能被当作左值,这个推断实际上是相当不准确的,比如
a[b + 1] = 15;
左侧是一个表达式,但是他可以标定一个指定的位置,所以他是一个左值;
一个表达式、一个变量可以让程序员确定唯一位置,并且该位置的内容可以修改,那么他就可以作为左值;

表达式求值
表达式求值由操作符的优先级、结合性和类型转换共同决定;
隐式类型转还
C 整型算数运算至少以缺省整型的精度来进行,为了获得这个精度,字符型和短整型在使用之前会转换为普通整型,这种转换称为整型提升;
char a, b, c;
c = a + b;
a 和 b 被提升为整型,然后再进行加法运算,加法运算的结果被截短再储存于 c 中。隐式转换对这个例子的运算结果没有任何影响。但在如下案例中,他的结果就不在相同
a = (~a ^ b << 1) >> 1;
由于存在求补和左移操作,8 位精度是完全不够的,标准要求进行整型求值,所以虽然结果与不进行隐式转换时不同,但最终结果并不存在歧义性。

算数转换
如果某个操作符的不同操作数属于不同类型,那么其中一个操作数必须要转换为另一个操作数类型,否则操作就无法进行。转换优先级如下:
long double
double
float
unsigned long int
unsigned int
int

// 如下代码就存在问题
// a、b 进行整型运算,如果在16位的机器上,运算结果就会溢出,解决办法就是把其中一个操作数转换为长整型
// long c = (long int)a * b;
int a = 50, b = 100;
long c = a * b;

类型转换会带来精度损失
1、整型转换为float时,float 仅仅要求 6 位数字的精度,所以一个超过 6 位数字的整型赋值给 float 时,其结果只能是该整型值的近似值;
2、当 float 转换为整型值时,小数部分被舍弃,如果浮点型数值过于庞大,无法容纳在整型值中,那么他的结果也是未定义的

操作符属性
表达式中的优先级只决定表达式各个组成部分在求值过程中如何进行聚组
a * b + c * d + e * f;
常规意义上执行顺序是这样的
a * b
c * d
e * f
a * b + c * d
a * b + c * d + e * f
但实际上他可能会这样执行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值