2308-操作符

发布时间 2023-10-06 17:33:58作者: 积分三换

1.算术操作符:

+ - * / %

1. 除了% 操作符之外,其他的几个操作符可以作用于整数和浮点数。
2. 对于/ 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。
3. % 操作符的两个操作数必须为整数。返回的是整除之后的余数。

 

2.移位操作符

<< 左移操作符:

左边抛弃、右边补0


>> 右移操作符:

1. 逻辑移位
左边用0填充,右边丢弃
2. 算术移位(一般采用)
左边用原该值的符号位填充,右边丢弃

 

3. 位操作符

& //按位与 (0&1=0)
| //按位或 (0|1=1)
^ //按位异或 (对应二进制位相同为0,相异为1)   


注:他们的操作数必须是整数。

例:交换a和b的值,不引入第三个变量

 1 #include <stdio.h>//交换a和b的值
 2 int main()
 3 {
 4     int a = 3;
 5     int b = 5;
 6     a = a ^ b;
 7     b = a ^ b;//b=a^b^b=a
 8     a = a ^ b;//a=(a^b)^(a^b^b)=a^b^a=a^a^b=b,满足交换律
 9     printf("a = %d, b = %d", a, b);
10     return 0;
11 }

 

4. 赋值操作符

+=
-=
*=
/=
%=
>>=
<<=
&=
|=
^=

5. 单目操作符

单目操作符不改变数值

!   逻辑反操作
-   负值
+   正值
&   取地址
sizeof   操作数的类型长度(以字节为单位)
~   对一个数的二进制按位取反
--   分为前置、后置
++    分为前置、后置
*   间接访问操作符(解引用操作符)
(类型)   强制类型转换

#include <stdio.h>
int main()
{
    int a = 13;
    ////把a的二进制中的第5位置成1
    a = a | (1 << 4);//0|1 = 1
    printf("a = %d\n",a);//29
    //a =        00...001101
    //1<<4 =    00...010000
    //a|(1<<4)= 00...011101
    
    
    //把a的二进制中的第5位置成0
    a = a & ~(1 << 4);//0&1 = 0, ~为单目操作符:按位取反
    printf("a = %d\n", a);//13
    //a =            00...001101
    //1<<4 =        00...010000
    //~(1<<4) =        11...101111
    //a&~(1 << 4) = 00...001101
    return 0;
}

++和--的前置与后置:

#include <stdio.h>
int main()
{
    int a = 10; 
    printf("%d\n", a--);//10,a--为先使用后做减法
    printf("%d\n", a);//9
    return 0;
}

 sizeof:

 1 #include <stdio.h>
 2 void test1(int arr[])
 3 {
 4     printf("%d\n", sizeof(arr));//(5)
 5 }
 6 void test2(char ch[])
 7 {
 8     printf("%d\n", sizeof(ch));//(6)
 9 }
10 int main()
11 {
12     short s = 5;
13     int a = 10;
14     printf("%d\n", sizeof(s = a + 2));//(1)
15     printf("%d\n", s);//(2)
16 
17     int arr[10] = { 0 };
18     char ch[10] = { 0 };
19     printf("%d\n", sizeof(arr));//(3)
20     printf("%d\n", sizeof(ch));//(4)
21     test1(arr);
22     test2(ch);
23     return 0;
24 }

(1) 值为2,即short的尺寸。

(2) 值为s原本的值5,因为sizeof不改变值

(3)和(4)分别为40和10

(5)和(6)都为数组第一个元素的地址大小,即指针大小,为4或8

 

6. 关系操作符

>
>=
<
<=
!=   用于测试“不相等”
==     用于测试“相等”

 

7.逻辑操作符

&&   逻辑与
||      逻辑或

1 #include <stdio.h>
2 int main()
3 {
4 int i = 0,a=0,b=2,c =3,d=4;
5 i = a++ && ++b && d++; //注释掉此行,结果为多少?
6 i = a++||++b||d++; //注释掉此行,结果又为多少?
7 printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
8 return 0;
9 }

注释掉第5行,在逻辑或中,a++判定为0,++b判定为3,即停止第6行的运算。在逻辑或运算中,有1即停止运算输出为1,此时打印结果为1 3 3 4,因为d没有运算

注释掉第6行,在逻辑与中,a++判定为0,即停止第5行的运算。在逻辑与运算中,有0即停止运算输出为0,此时打印结果为1 2 3 4, bcd都没有参与运算

 

8.条件操作符

exp1 ? exp2 : exp3

exp1为真,则只计算exp2;若exp1为假,则只计算exp3

 

9.逗号表达式

exp1, exp2, ... ,expN

从左到右依次运算,但是整个表达式的结果是最后一个表达式的结果

 

10. 下标引用、函数调用和结构成员

1. [ ] 下标引用操作符

操作数:一个数组名 + 一个索引值

2. ( ) 函数调用操作符

接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。

 

 1 #include <stdio.h>
 2 void test1()
 3 {
 4 printf("hehe\n");
 5 }
 6 void test2(const char *str)
 7 {
 8 printf("%s\n", str);
 9 }
10 int main()
11 {
12 test1(); //()作为函数调用操作符。
13 test2("hello bit.");//()作为函数调用操作符。
14 return 0;
15 }

3. 访问一个结构的成员

.   结构体 . 成员名
->   结构体指针 -> 成员名

 1 #include <stdio.h>
 2 struct Stu
 3 {
 4     char name[10];
 5     int age;
 6     char sex[5];
 7     double score;
 8 };
 9 
10 void set_age1(struct Stu stu)
11 {
12     stu.age = 18;
13 }
14 void set_age2(struct Stu* pStu)
15 {
16     pStu->age = 18;//结构成员访问
17 }
18 int main()
19 {
20     struct Stu stu;
21     struct Stu* pStu = &stu;//结构成员访问
22     stu.age = 20;//结构成员访问
23     set_age1(stu);
24     pStu->age = 20;//结构成员访问
25     set_age2(pStu);
26     return 0;
27 }

 

11. 表达式求值

隐式类型转换:C的整型算术运算总是至少以缺省整型类型的精度来进行的。

为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。

整型提升的意义:

表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度般就是int的字节长度,同时也是CPU的通用寄存器的长度。
因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。
通用CPU (general-purpose CPU) 是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算。

 

 算术转换:

如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算术转换。

如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算。

 

 操作符的属性:

复杂表达式的求值有三个影响的因素。
1. 操作符的优先级
2. 操作符的结合性
3. 是否控制求值顺序。
两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。

总结:我们写出的表达式如果不能通过操作符的属性确定唯一的计算路径,那这个表达式就是存在问题的