第一章 操作符分类:
- 算术操作符
- 移位操作符
- 位操作符
- 赋值操作符
- 单目操作符
- 关系操作符
- 逻辑操作符
- 条件操作符
- 逗号表达式
- 下标引用、函数调用和结构成员
算数操作符:
- +
- -
- *
- /
- %
/:除法。得到的是商。例如:5 / 2=2.
- 如果两个操作数都为整数,执行整数除法,而只有浮点数执行的就是浮点数除法。
%:模。得到的是余数。例如:5 % 2 = 1
- 两个操作数必须为整数,返回的是整除后的余数。
移位操作符:
<< 左移操作符
移动规则:左边丢弃,右边补0
>> 右移操作符
- 算术右移:右边丢弃,左边补原符号位。
int a = -6; //10000110
int b = a >> 1; //移动的是二进制位
printf("%d",b); //10000011 -3
- 逻辑右移:右边丢弃,左边补0
注:不要移动负数位,是一种未定义行为。
位操作符:
- & 按位与:有0为0
- | 按位或:有1为1
- ^ 按位异或:相同为0,不同为1
注:都是根据二进制位进行操作。它们的操作数必须是整数。
练习1:不创建临时变量,交换两个数字。
//不创建临时变量,交换两个数字
int a = 3; //011
int b = 5; //101
printf("before: a = %d,b = %d\n",a,b);
// //加减法
// a = a + b;
// b = a - b;
// a = a - b;
//异或
a = a ^ b;
b = a ^ b;
a = a ^ b;
printf("after: a = %d,b = %d\n",a,b);
练习2:求一个整数存储在内存中的二进制中1的个数
//方法2:
//求一个整数存储在内存中的二进制中1的个数
int num = 0;
int count = 0;
scanf("%d",&num);
int i = 0;
for ( i = 0; i < 32; i++)
{
if(1 == ((num >> i) & 1))
count ++;
}
//方法1:
//存在问题:负数的时候就不能正确计算。
// while(num)
// {
// if(num % 2 == 1)
// count ++;
// num = num / 2;
// }
赋值操作符:
= :可以把得到的不满意的值重新赋值。
复合操作符:
- += 例如:a = a + 2 与 a += 2 等价,以下类似
- -=
- *=
- /=
- %=
- >>=
- <<=
- &=
- |=
- ^=
单目操作符:只有一个操作数
- !取反
- - 负值
- + 正值
- & 取地址
- sizeof 操作数的类型长度(以字节为单位)
short s = 0;
int a = 10;
printf("%d\n",sizeof(s = a + 5)); //不真实参加赋值
printf("%d\n",s); //0
- ~ 二进制按位取反
int a = 0;
//按二进制位取反
//00000000000000000000000000000000 -- 原码、反码、补码
//11111111111111111111111111111111 -- 按位取反 -- 补码
//11111111111111111111111111111110 -- 反码
//10000000000000000000000000000001 -- 原码
// -1
printf("%d\n",~a); //-1
int a = 11;
//00000000 00000000 00000000 00001011
//00000000 00000000 00000000 00000100 或 -- 1<<2
//00000000 00000000 00000000 00001111
a = a |(1<<2);
printf("%d\n",a); //15
//00000000 00000000 00000000 00001111
//11111111 11111111 11111111 11111011 与 -- ~(1<<2)
//00000000 00000000 00000000 00001011
a = a &(~(1<<2));
printf("%d\n",a); //11
- -- 前置、后置--
- ++ 前置、后置++
- * 间接访问操作符(解引用操作符)
- (类型) 强制类型转换
关系操作符:
- >
- >=
- <
- <=
- !=
- == 相等
逻辑操作符:按照布尔值进行操作。
- && 逻辑与:有一项表达式为0,剩下的表达式不计算,最终结果就是0
- || 逻辑或:有一项表达式为1,剩下的表达式不计算,最终结果就是1
int i = 0, a = 0, b = 2, c = 3, d = 4;
i = a++ && ++b && d++;
printf("a = %d, b = %d, c = %d, d = %d\n",a,b,c,d);
i = a++ || ++b || d++;
printf("a = %d, b = %d, c = %d, d = %d\n",a,b,c,d);
条件操作符(三目操作符):
exp1 ? exp2 : exp3
如果exp1成立则执行exp2,如果exp1不成立则执行exp3.
逗号表达式:
exp1,exp2,exp3......expn
逗号表达式,从左往右依次执行,最后一个表达式的结果就是整个表达式的结果。
练习:函数expt((v1,v2),(v3,v4),v5,v6)在调用时的参数个数为几个?
解析:有四个。(v1,v2)用到的就是逗号表达式的作用,表示v2。
下标引用、函数调用和结构成员:
1.[ ]下标引用操作符
操作数:一个数组名 + 一个索引值,例如:arr[10]
2.()函数调用操作符
操作数:函数名(参数1,参数2, ...)
3. 访问一个结构的成员
结构体变量.成员名
结构体指针->成员名
#include<stdio.h>
struct stu
{
char name[20];
int age;
char id[20];
};
int main()
{
int a = 10;
//使用struct stu这个类型创建了一个学生对象s1,并初始化。
struct stu s1 = {"张三",2,"1941520320"};
struct stu* ps = &s1;
printf("%s\n",(*ps).name);
printf("%d\n",(*ps).age);
printf("%s\n",(*ps).id);
printf("%s\n",ps->name);
printf("%d\n",ps->age);
printf("%s\n",ps->id);
printf("%s\n",s1.name);
printf("%d\n",s1.age);
printf("%s\n",s1.id);
return 0;
}
第二章 表达式求值
- 表达式求值的顺序一部分是由操作符的优先级和结合性决定的。
- 有些表达式的操作数在求值的过程中可能需要转换为其他类型。
1. 隐式类型转换:表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换成为整型提升。
如以下例子。b和c的值要提升为普通整型,再进行计算。
如何进行整型提升呢?按照变量的数据类型的符号位来提升。
char a = 3;
//00000000000000000000000000000011
//00000011 - a
char b = 127;
//00000000000000000000000001111111
//01111111 - b
char c = a + b;
//整型提升
//00000000000000000000000000000011 - a
//00000000000000000000000001111111 - b
//00000000000000000000000010000010
//10000010 - c
printf("%d\n",c);
//%d就是要打印整型,所以c进行整型提升
//11111111111111111111111110000010 - 补码
//11111111111111111111111110000001 - 反码
//10000000000000000000000001111110 - 原码 -126
2. 算术转换:如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算术转换。
- long double
- double
- float
- unsigned long int
- long int
- unsigned int
- int
如果某个操作数的类型在上面这个列表中排名较低,那么要首先转换成另外一个操作数的类型后执行运算。