目录
1.算数操作符
+ - * / %
加 减 乘 除 取余
ps: %取余操作符的两个操作数必须为整数。
2.移位操作符
对操作数(整数)的二进制位进行操作,内存中存的是补码,也就是对补码进行操作
对于负整数而言: 原码:直接根据数值写出来的二进制序列就是原码
反码:原码符号位不变,其他位按位取反就是反码
补码:反码加 1 即补码
对于正整数而言:原码、反码、补码相同
左移操作符 <<
左边丢弃,右边补零
右移操作符 >>
1.逻辑移位
左边补零,右边丢弃
2.算术移位
左边用原该值的符号位填充,右边丢弃。
3.位操作符
3.1 按位与操作符 & (按二进制位与)
例
int main()
{
int a = 3;
int b = 5;
int c = a & b;
printf("%d", c);
return 0;
}
运算逻辑: a 00000000000000000000000000000011
b 00000000000000000000000000000101
c 00000000000000000000000000000001
a b 相对应的位置有一个为 0(假),则c 的相对应的位为 0(假) 有1假为假
a b 相对应的位置都为 1 (真)时, 则c 的相对应的位为 1 (真) 有2真才为真
3.2 按位(二进制位)或操作符 |
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
//按位或
int main()
{
int a = 3;
int b = 5;
int c = a | b;
printf("%d", c);
return 0;
}
运算逻辑
有真(1)即为真(1),两假(0)才为假(0) 1真为真,2假才为假
3.3 按(二进制位)位异或 ^
例
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
//按位异或
int main()
{
int a = 3;
int b = 5;
int c = a ^ b;
printf("%d", c);
return 0;
}
运算逻辑
相对应 bit 位相异即为真(1),相同即为假(0) 相异为真,相同则假
ps:a^a=0; a^0=a
4.赋值操作符
赋值 = (判断符 ==)
复合赋值操作符
+=
-=
*=
/=
%=
>>=
<<=
&=
|=
^=
例:a+=10
a=a+10;
5.单目操作符(只有一个操作数)
5.1 !逻辑反操作 a=1为真 !a即为假(!a=0)
5.2 - 负值
5.3 + 正值
5.4 & 取地址
与5.9 解引用操作符一起讲
5.5 sizeof 操作数的类型长度(以字节为单位)
例:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
int a = 10;//占用4个字节空间
int arr[10] = {0};//占用4*10 个字节空间 初始化数组需 要 大括号 {0}
printf("%d\n",sizeof(arr));//sizeof里面可放数组名
printf("%d\n",sizeof(int [10]));//也可以放函数类型
printf("%d\n", sizeof a);//计算 a 所占空间大小可以不用带括号,带也无妨,证明sizeof是一个操作
//符并不是一个函数
printf("%d\n", sizeof(int));//
short s = 5;//占用2个字节空间
printf("%d\n", sizeof(s = a + 2));//sizeof计算的仍是 s 这个短整型所占的空间大小
printf("%d\n", s);//sizeof函数里面的表达式是不参与运算的,所以并不会影响 s 的值
return 0;
}
5.6 ~ 对一个数的二进制按位取反
5.7 -- 前置、后置--
--使用方法和++一致
5.8 ++ 前置、后置++
5.9 * 间接引用操作符(解引用操作符)
5.10 (类型) 强制类型转换
6.关系操作符 > >= < <= != ==
可以用来判断变量之间的关系
ps: 1. 比较两个字符串是否相等,不能使用 ==
2. = 赋值 == 判断是否相等
7.逻辑操作符
7.1 && 逻辑与
有一个为假都为假
两个为真才为真
有一个条件为假后面的都不用算了
7.2 || 逻辑或
有一个条件为真后面的就不用算了
与逻辑与相对
应该为真即为真,两个为假才为假
8.条件操作符
(三木操作符)
exp1 ? exp2 :exp3
例:
9.逗号表达式
10.下标引用、函数调用和结构成员
10.1 下标引用
10.2 函数调用
10.3 结构成员
通过解引用操作符访问指针打印
或者
使用 -> 结构体 -> 成员名
11.表达式求值
表达式求值的顺序一般是由操作符的优先性和结合性决定的。
同样,有些表达式的操作数在求值过程中可能需要转换为其他类型。
11.1 隐式类型转换
C语言中的整型算术运算总是至少以缺省整型类型的精度来进行的
为了获得这个精度,表达式中的字符和短整型操作数在使用之前转换为普通整型,这种转换称为 整型提升
ps:当操作数长度大于或等于普通整型长度时,便无须进行整型提升。
【整型提升的意义】:
表达式的整型运算要在cpu的相应运算器件内执行,cpu内整型运算器(ALU)的操作数的字节长度一般是 int 的字节长度,同时也是cpu的通用寄存器的长度。
因此即使两个 char 类型的相加,在cpu执行时实际上也要转换为cpu内整型操作数的标准长度。
通用cpu(general-purpose CPU)是难以直接实现两个8比特字节的直接相加运算(虽然机器指令中可能有这种字节相加的指令)。所以,表达式中各种可能小于 int 长度的整型值,都必须先转换为 int 换 unsigned int ,然后才能送入 cpu 去执行运算。
例:
又例:
再例
11.2 算术转换
如果某个操作符的各个操作数属于不同类型,那么除非其中一个操作数转换为另一个操作数的类型,否则操作无法进行。下面的层次体系称为寻常算术转换。
long double |
double |
float |
unsigned long int |
long int |
unsigned int |
int |
如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算。(向精度更高的去转换)(int >> long int)
警告: 但是算术转换要合理,要不然会有一些潜在的问题。
12.3 操作符的属性
复杂表达式的求值有三个影响的因素。
1. 操作符的优先级
例:* / 优先于 + -
2. 操作符的结合性
3. 是否控制求值顺序。
两个相邻的操作符先执行哪个?取决于他们的优先级。
如果两者的优先级相同,取决于他们的结合性。
C语言操作符优先级
C语言操作符优先级
优先级 | 运算符 | 含 义 | 要求运算 对象的个数 | 结合方向 |
1 | () [] -> . | 圆括号 下标运算符 指向结构体成员运算符 结构体成员运算符 | 自左至右 | |
2 | ! | 逻辑非运算符 | 1 (单目运算符) | 自右至左 |
~ | 按位取反运算符 | |||
++ | 自增运算符 | |||
-- | 自减运算符 | |||
- | 负号运算符 | |||
(类型) | 类型转换运算符 | |||
* | 指针运算符 | |||
& | 地址与运算符 | |||
sizeof | 长度运算符 | |||
3 | * / % | 乘法运算符 除法运算符 求余运算符 | 2 (双目运算符) | 自左至右 |
4 | + - | 加法运算符 减法运算符 | 2 (双目运算符) | 自左至右 |
5 | << >> | 左移运算符 右移运算符 | 2 (双目运算符) | |
6 | < <= > >= | 关系运算符 | 2 (双目运算符) | 自左至右 |
7 | == != | 等于运算符 不等于运算符 | 2 (双目运算符) | 自左至右 |
8 | & | 按位与运算符 | 2 (双目运算符) | 自左至右 |
9 | ^ | 按位异或运算符 | 2 (双目运算符) | 自左至右 |
10 | | | 按位或运算符 | 2 (双目运算符) | 自左至右 |
11 | && | 逻辑与运算符 | 2 (双目运算符) | 自左至右 |
12 | || | 逻辑或运算符 | 2 (双目运算符) | 自左至右 |
13 | ? : | 条件运算符 | 3 (三目运算) | 自右至左 |
14 | = += -= *= /= %= >>= <<= &= ^= |= | 赋值运算符 | 2 | 自右至左 |
15 | , | 逗号运算符 | 自左至右 |
一些问题表达式
注释:代码1在计算的时候,由于*比+的优先级高,只能保证,*的计算是比+早,但是优先级并 不能决定第三个*比第一个+早执行。
注释:同上,操作符的优先级只能决定自减--的运算在+的运算的前面,但是我们并没有办法得 知,+操作符的左操作数的获取在右操作数之前还是之后求值,所以结果是不可预测的,是有歧义 的。
《c和指针》作者测试 多个编译器测试结果
值 编译器
—128 Tandy 6000 Xenix 3.2
—95 Think C 5.02(Macintosh)
—86 IBM PowerPC AIX 3.2.5
—85 Sun Sparc cc(K&C编译器)
—63 gcc,HP_UX 9.0,Power C 2.0.0
4 Sun Sparc acc(K&C编译器)
21 Turbo C/C++ 4.5
22 FreeBSD 2.1 R
30 Dec Alpha OSF1 2.0
36 Dec VAX/VMS
42 Microsoft C 5.1
纳尼?
这个代码有没有实际的问题?
有问题!
虽然在大多数的编译器上求得结果都是相同的。 但是上述代码 answer = fun() - fun() * fun(); 中我们只能通过操作符的优先级得知:先算乘法, 再算减法。 函数的调用先后顺序无法通过操作符的优先级确定。
总结:我们写出的表达式如果不能通过操作符的属性确定唯一的计算路径,那这个表达式就是存在问题的。