一.操作符的分类
- 算术操作符: + - * / %
- 移位操作符:<< >>
- 位操作符: & | ^
- 赋值操作符: = += -= *= /= %= >= &= |= ^=
- 单⽬操作符: ! ++ -- & * + - ~ sizeof ( 类型 )
- 关系操作符: > >= < <= == !=
- 逻辑操作符:&& || ! ++ -- & * + - ~ sizeof ( 类型 )
- 条件操作符:? :
- 逗号表达式:,
- 下标引用:[ ]
- 函数调用:()
- 结构成员访问:. - >
二.二进制和进制转换
计算机能够识别的是2进制信息,数据在内存中存出的时候,也是以2进制的形式存储的
2进制转10进制:

10进制转换为2进制

2进制转化为8进制
8进制的数字每⼀位是0~7的,0~7的数字,各⾃写成2进制,最多有3个2进制位就⾜够了,⽐如7的二进制是111,所以在2进制转8进制数的时候,从2进制序列中右边低位开始向左每3个2进制位会换算一个8进制位,剩余不够3个2进制位的直接换算。 如:2进制的01101011,换成8进制:0153,0开头的数字,会被当做8进制。
例:

2进制转换为16进制
16进制的数字每⼀位是0~9,a~f的,0~9,a~f的数字,各⾃写成2进制,最多有4个2进制位就足够了, 比如f的二进制是1111,所以在2进制转16进制数的时候,从2进制序列中右边低位开始向左每4个2进 制位会换算⼀个16进制位,剩余不够4个二进制位的直接换算。 如:2进制的01101011,换成16进制:0x6b,16进制表示的时候前⾯加0x
例:

三.原码,反码,补码
原码::直接将数值按照正负数的形式翻译成⼆进制得到的就是原码
反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。
补码:反码+1就得到补码。补码得到原码也是可以使⽤:取反,+1的操作。
正整数的原,反,补码都相同。
负整数的三种表示方法各不相同
**整数(有符号整数,无符号整数)**的2进制表示方法有三种,即原码、反码和补码
有符号整数 的三种表示方法均有符号位和数值位两部分,2进制序列中,最高位的1位是被当做符号 位,剩余的都是数值位。
符号位都是0表示正,1表示负。

无符号整数的三种 2 进制表⽰相同,没有符号位,每⼀位都是数值位。

对于整形来说:数据存放内存中其实存放的是补码。
四.移位操作符
移位操作符只能对整数进行移位,移动的是二进制位(存储在内存中的补码)
<<左移操作符
移位规则:左边抛弃,右边补零
cs
int main()
{
int a=6;
int b=(a<<1)
printf("%d\n",b);
return 0;
}
最后a不没有变,结果是b变了,以为之后为12
>>右移操作符
移位规则:首先右移运算分两种(具体哪种取决于编译器):
1.逻辑右移:左边⽤0填充,右边丢弃
2.算术右移:左边⽤原该值的符号位填充,右边丢弃
对于移位运算符,不要移动负数位!
五.位操作符:& | ^ ~
&:按位与
|:按位或
^:按位异或(对应的二进制位,相同为0,相异为1)
零和任何数异或还是任何数,异或支持交换律
~:按位取反(对应的二进制位,原来是0变成1,原来是1变成0)
以上操作符的操作数必须是整数
这里举两个例子应用一下
- 编写代码将13二进制序列的第5位修改为1,然后再改回0
cs
int main()
{
int a = 13;
a |= (1 << (5 - 1));
printf("%d\n", a);//29
/*
13=00001101
1=00010000
按位或00011101
*/
a &= ~(1 << (5 - 1));
/*
29=00011101
11101111
按位与00001101
*/
return 0;
}
- 不创建临时变量(第三个变量),实现两个整数的交换
cs
int main()
{
//0与任何数按位异或都为任何数
int a=10;
int b=20;
a=a^b;//a'=a^b
b=a^b;//b=a'^b=a^b^b=a
a=a^b;//a=a^b^a=b
printf("a=%d b=%d\n",a,b);
return 0;
}
六.单目操作符
! ++ -- & * + - ~ sizeof ( 类型 )
七.逗号表达式
cs
exp1,exp2,exp3,...expN
逗号表达式,从左向右依次执行,整个表达式的结果是最后一个表达式的结果
八.下标访问[ ],函数调用()
1.[ ]下标引用操作符
操作数:一个数组名+一个索引值(下标)
cs
int arr[10];
arr[9]=10;//使用下标引用操作符
[ ]的两个操作是arr和9
2.函数调用操作符()
接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数
九.结构成员访问操作符
在学习这个操作符之前,我们得先知道一个概念:结构体
结构是⼀些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量,如: 标量、数组、指针,甚至是其他结构体
1.结构体的声明和结构体变量的创建和初始化
cs
struct tag//(自定义的一个名字)
{
member -list;//里面可以有一个或多个成员
}variable-list;//(可有可无)
//例:描述一个学生
struct Stu
{
char name[20];
int age;
char id[11];
float score;
}s6,s7,s8;//声明的同时顺便创建了几个变量,可有可无,这是全局变量
struct Stu s4;//全局变量
int main()
{
int num=0;
struct Stu s1;//结构体变量的创建,s1,s2,s3都是局部变量
struct Stu s2={"zhangsan",20,"20240809",85.5f};//初始化用大括号
struct Stu s3={.age=18,.name="wangcai",.score=95.5f,.id="20240801"};//想指定初始化那个这里就用到了结构访问操作符.去初始化
return 0;
}
2.结构体的嵌套
cs
struct Peo
{
char name[30];
int age;
char tele[12];
};
struct Ebook//实现一个通讯录,这个结构体就包含了另一个结构体
{
struct Peo data[100];
int count;
};
int main()
{
struct Peo p1={"zhangsan",20,"12372324"};
struct Ebook eb={{{"wangwu",19,"19378634"},{"cuihua",18,"28646375"}},0};
printf("%s\n",p1.name);//访问结构体变量打印姓名(.结构成员访问操作符)
printf("%s\n",p1.age);
printf("%s\n",eb.data[1].name);//打印翠花的名字
return 0;
}
3,结构体成员的直接访问操作符.
使用方式:结构体变量.成员名
具体使用示例见上面代码
4.结构体成员的间接访问操作符->
使用方式:结构体指针->成员名
具体使用实例待后面学完指针详细说明,结构体也会在后面详细学习
十,操作符的优先级和结合性
1.优先级:
如果一个表达式包含很多操作符,我们需要知道那个操作符优先,优先级高的先进行计算
2.结合性:
如果两个运算符优先级相同,这时没法确定先算哪个就看结合性。根据运算是左结合还是右结合,决定执行顺序。
具体可参考下表

十一,表达式求值
1.整型提升
cs
int main()
{
int a=100;
long c=100l;//编译器会默认100是整型,除非你在后面加个l,它会转变为long型
long long b=100ll;//同理
float f=3.14f;//这里规定会被默认为double类型,所以我们再后面加个f就会转换为float类型
return 0;
}
C语言中整型算术运算总是至少以缺省(默认)整型类型的精度来进行的。 为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。
如何整型提升?
(1). 有符号整数提升是按照变量的数据类型的符号位来提升的
(2).无符号整数提升,高位补0
cpp
int main()
{
//char是有符号的,signed char
char a=20;//截断后存进了a,因为a一个字符只占了八个字节,存不下那么多所以要截断
//000000000000000000010100
//只有00010100存进了a
char b=120;//发生截断
//000000000000000001111000
//01111000存进了b
char c=a+b;
//整型提升
//a-00010100-提升000000000000000000010100
//b-01111000-提升000000000000000001111000
// 相加所得的是补码0000000000000000010001100
//存进c里的为100001100
printf("%d",c);
//c提升后为111111111111111110001100-补码
//打印的是原码-100000000000000001110100-转化为十进制就是-116
return 0;
}
以上代码中b和a的值被提升为普通整型,然后再执行加法运算。 加法运算完成之后,结果将被截断,然后再存储于c中
打印结果就为-116
2.算术转换
如果某个操作符的各个操作数属于不同的类型,则除非其中⼀个操作数的转换为另⼀个操作数的类 型,否则操作就不能进行。这个我们不需要过多操心,编译器会自动转化

3.问题运算代码举例
(1)
cs
c + --c
这个代码运算的问题是,前一个c的开始取值不知道,因为后一个--c先进行计算,不确定前一个c是初定义的值还是减减后的值
(2)
cs
a*b + c*d + e*f
不确定是以下哪种顺序,因为以下都符合正确的优先级和结合性

(3)
cs
int main()
{
int answer;
answer=fun()-fun()*fun();
printf("%d\n",answer);
return 0;
}
这个我们只能通过优先级知道先算乘法,再算减法,而上面三个fun函数调用先后顺序无法通过操作符的优先级确定,不一样的先后顺序就导致了结果的不同
所以我们尽量不要写那样运算符冗杂混乱的代码,不确定优先级的操作符可以加以括号进行辅助。