操作符

38 阅读12分钟

操作符

1.操作符分类

算术操作符

移位---

位---

赋值---

单目---

关系---

逻辑---

条件---

逗号表达式

下标引用,函数调用和结构成员

2.算术操作符

/ % + - *

(1)除了%操作符之外,其他的几个操作符可以作用于整数和浮点数。

(2)对于/操作符,如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。

(3)%操作符的两个操作符必须为整数。返回的是整除之后的余数

3.移位操作符

移动的是二进制位(整数在内存中存储的是补码)

<< 左移操作符

右移操作符 >>

移位操作符的操作数只能是整数,不能是浮点数!!!

3.1左移操作符

移位规则:

左边抛弃,右边补0

左移位符的直观图.png

#include<stdio.h>
int main()
{
	int a = 7;
	int b = a << 1;
	printf("a=%d\n", a);
	printf("b=%d\n", b);
	return 0;
}

左移操作符有*2的效果

左移位符 b=14.png

#include<stdio.h>
int main()
{
	int a = -7;
	int b = a << 1;
	printf("a=%d\n", a);
	printf("b=%d\n", b);
	return 0;
}

b=-14结果.png

b=-14的直观图.png

3.2 右移操作符

算术移位:右边丢弃,左边补原符号位

逻辑移位:右边丢弃,左边补0

对于移位运算符,不要移动负数位,这个是标准未定义的

int b = a>>-1;

VS编译器(大部分)采用的算术右移

正数测不出来是哪种移位方式

#include<stdio.h>
int main()
{
	int a = 7;
	int b = a >> 1;
	printf("a=%d\n", a);
	printf("b=%d\n", b);
	return 0;
}

b=3的结果.png

b=3的直观图.png

负数:

#include<stdio.h>
int main()
{
	int a = -7;
	int b = a >> 1;
	printf("a=%d\n", a);
	printf("b=%d\n", b);
	return 0;
}

b=-4的结果.png

b=-4的直观图.png

4. 位操作符

&//按(2进制)位与
|//按位或
^//按位异或

它们的操作数必须是整数

#include<stdio.h>
int main()
{
	int a = 3;
	int b = -5;
	int c = a & b;
    //00000000000000000000000000000011  3的补码
    //10000000000000000000000000000101   -5的原码
    //11111111111111111111111111111010   -5的反码
    //11111111111111111111111111111011    -5的补码
    
    
    //00000000000000000000000000000011  3的补码
    //11111111111111111111111111111011    -5的补码
    //00000000000000000000000000000011   正数的原码,反码,补码相同
    //只要有0,按位与的结果就是0,两个都为1,按位与的结果才为1
    //%d 意味着打印一个有符号的整数(原码)
	printf("c=%d\n", c);
	return 0;
}

按位与结果.png

#include<stdio.h>
int main()
{
	int a = 3;
	int b = -5;
	int c = a | b;
    //00000000000000000000000000000011  3的补码
    //10000000000000000000000000000101   -5的原码
    //11111111111111111111111111111010   -5的反码
    //11111111111111111111111111111011    -5的补码
    
    
    //00000000000000000000000000000011  3的补码
    //11111111111111111111111111111011    -5的补码
    //11111111111111111111111111111011 补码
    //11111111111111111111111111111010 反码
    //10000000000000000000000000000101 原码
    //-5
    //只要有1,按位或的结果都是1,两个都为0,按位或的结果才是0
    //%d 意味着打印一个有符号的整数(原码)
	printf("c=%d\n", c);
	return 0;
}

按位或的结果.png

#include<stdio.h>
int main()
{
	int a = 3;
	int b = -5;
	int c = a ^ b;
    //00000000000000000000000000000011  3的补码
    //10000000000000000000000000000101   -5的原码
    //11111111111111111111111111111010   -5的反码
    //11111111111111111111111111111011    -5的补码
    
    
    //00000000000000000000000000000011  3的补码
    //11111111111111111111111111111011    -5的补码
    //11111111111111111111111111111000 补码
    //11111111111111111111111111110111 反码
    //10000000000000000000000000001000 原码
    //-8
    //相同为0,相异为1
    //%d 意味着打印一个有符号的整数(原码)
	printf("c=%d\n", c);
	return 0;
}

按位异或结果.png

//3^3=0  ->a^a=0
//011
//011
//000


//0^5=5  ->0^a=a
//000
//101
//101

//3^3^5=5
//3^5^3=5
//异或操作符支持交换律
//011
//101
//110
//011
//101
//该算法可以避免溢出
#include <stdio.h>
int main()
{
	int a = 3;
	int b = 5;
	printf("交换前:a=%d b=%d\n", a, b);
	a = a ^ b;//a=3^5
	b = a ^ b;//3^5^5 --->b=3
	a = a ^ b;//3^5^3 --->a=5
	printf("交换后:a=%d b=%d\n", a, b);


	return 0;

}

互换数值法3(按位异或).png

5.赋值操作符

int weight = 120//体重  初始化
weight = 89//不满意就赋值
a = x = y+1;//连续赋值(不推荐)
x = y+1;
a = x;//这样的写法更加清晰爽朗且易于调试
    

复合赋值符

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

6.单目操作符

!   逻辑反操作符   真变为假 假变为真  0表示假,非0表示真
-   负值
+    正值
    & 取地址
    sizeof 操作数的类型长度(以字节为单位)
    sizeof 是操作符,不是函数,后面的()可以省略,如:sizeof a  但sizeof int 的写法是错误的
   // strlen是库函数,用来求字符串长度
    ~ 对一个数的二进制按位取反
    -- 前置,后置--
    ++ 前置,后置++
    * 间接访问该操作符(解引用操作符)
    (类型) 强制类型转换
#include<stdio.h>
int main()
{
    int flag = 3;
    //flag 为真,进入if
    if (flag)
    {}
    //flag为假,进入if
    if(!flag)
    {}
    
    
    
    int a=-10;
    int b=+a;//-10
    int b=-a;//10
    
    
    &a;//取出a在内存中的起始地址  a占4个字节,取出的是第一个字节的地址
   int* p = &a;//p就是指针变量
       
       
    int a=10int n=sizeof(a);//计算的是a所占内存的大小,单位是字节
       int n=sizeofint);
     //计算的是变量/类型所创建的变量
       //~是按(二进制)位取反  将0变为1,将1变为0
     int a=0;
    //00000000000000000000000000000000
    //11111111111111111111111111111111 ~a补码
    //11111111111111111111111111111110 ~a反码
    //10000000000000000000000000000001 ~a原码
    //-1
    printf("%d\n",~a);
    
    
    int a = 3;
    int b = ++a;//先++,后使用
    printf("%d\n",a);//4
    printf("%d\n",b);//4
    
    
    
    int a = 3;
    int b = a++;//先使用,后++
    printf("%d\n",a);//4
    printf("%d\n",b);//3
        return 0;
}
#include<stdio.h>
int main()
{
	int a = 10;
	printf("%d\n", a--);//先使用,只会打印出10
	printf("%d\n", a);//后--,打印出9
	return 0;
}

a--的结果.png

#include<stdio.h>
int main()
{
	int a = 10;
	int* p = &a;
	*p = 20;//指向的是p所存放地址的对象  即a,将a的值改为20
	printf("%d\n", a);
	return 0;
}

解引用结果.png

#include<stdio.h>
int main()
{
	int a = (int)3.14;
	printf("%d\n", a);
	return 0;
}

类型转换结果1.png

7.关系操作符

==

int main()
{
    if("abc"=="abcdef")//这样是在比较两个字符串的首字符的地址
    {
        
    }
    //两个字符串比较相等应该使用strcmp(库函数)
    return 0;
}

8.逻辑操作符

&&  逻辑与
||  逻辑或    

只关注真假

#include<stdio.h>
int main()
{
	int a = 3;
	int b = 5;
	int c = a && b;//并且   如果表示真,结果为1   表示假,为0
	int c = a || b;//或者  只要有一个为真,结果为1
	printf("%d\n", c);
	//if (a && b)
	//{

	//}

	return 0;
}

&& 左边为假,右边就不计算了

|| 左边为真,右边就不计算了

9.条件操作符

三目操作符

(表达式1)?(表达式2):(表达式3)

(a>5)?(b=3):(b=-3)
b=(a>5?3:-3);

10.逗号表达式

就是用逗号隔开的多个表达式

从左至右依次执行,整个表达式的结果是最后一个表达式的结果

int main()
{
    int a = 1;
    int b = 2;
    int c = (a>b,a = b+10,a,b = a+1);
    printf("c=%d",c);
    return 0;
}

逗号表达式结果.png

11.下标引用操作符,函数调用和结构成员

(1)下标引用操作符

[]

int arr[10]={0};
arr[7]=8;//--->*(arr+7) arr是数组首元素的地址  arr+7就是跳过7个元素,指向了第8个元素  *(arr+7)就是第八个元素
//arr[7]-->*(arr+7)-->*(7+arr)-->7[arr]
7[arr]=9;    

(2)函数调用

int Add(int x,int y)
{
    return x + y;
}
int main()
{
    int a = 10;
    int b = 20;
    //函数调用
    int c = Add(a,b);//()就是函数调用操作符
    return 0;
}

(3)结构成员

. 结构体.成员名

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

struct Stu
{
    char name[20];
    int age;
    double score;
}
void set_stu(struct Stu ss)
{
   strcpy( ss.name,"zhangsan" );
        ss.age=20;
    ss.score=100.0;
}
viod print_stu(struct Stu ss)
{
    printf("%s %d %lf",ss.name,ss.age,ss.score);
}
int main()
{
    struct Stu s ={0};
    set_stu(s);
    print_stu();
    return 0;
}
    
struct Stu
{
    char name[20];
    int age;
    double score;
}
viod set_stu(struct Stu* ps)
{
    //strcpy((*ps).name,"zhangsan");
    //(*ps).age=20;
    //(*ps).score=100.0;
    
    strcpy(ps->name,"zhangsan");
    ps->age=20;
    ps->score=100.0;
    
}
void set_stu(struct Stu* ps)
{
   strcpy( ps->name,"zhangsan" );
        ps->age=20;
    ps->score=100.0;
}
viod print_stu(struct Stu* ps)
{
    printf("%s %d %lf",ps->name,ps->age,ps->score);
}
int main()
{
    struct Stu s ={0};
    set_stu(&s);
    print_stu(&s);
    return 0;
}

结构体指针->成员

结构体对象.成员

结构成员直观图.png

12.表达式求值

表达式求值的顺序一部分是由操作符的优先级和结合性决定

同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型

12.1隐式类型转换

c的整型算术运算总是至少以缺省整型类型的精度来进行的

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

无符号数整型提升时高位补0

整型提升的意义.png

#include<stdio.h>
int main()
{
    char a = 5;
    //00000000000000000000000000000101
    //00000101   a
    char b = 126;
    //00000000000000000000000001111110
    //01111110
    char c = a + b;
    //00000000000000000000000000000101   a
    //00000000000000000000000001111110  b
    //00000000000000000000000010000011
    //10000011  c
    //11111111111111111111111110000011 补码
    //11111111111111111111111110000010 反码
    //10000000000000000000000001111101 原码
    
    printf("%d\n",c);//-125
    return 0;
}
#include<stdio.h>
int main()
{
	char c = 1;
	printf("%u\n", sizeof(c));
	printf("%u\n", sizeof(+c));//+c 也是一个表达式,会进行整型提升
	printf("%u\n", sizeof(-c));
	return 0;
}

+c -c结果.png

12.2算术转换

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

向上(大的类型)转换

long double
    double
    float
    unsigned long int
    long int
    unsigned int
    int

12.3操作符的属性

复杂表达式的求值有三个影响的因素

1.操作符的优先级(相邻

2.操作符的结合性

3.是否控制求值顺序(&& || ?: (,,)是,其他都为否)

问题表达式

a*b+c*d+e*f
c + -- c

问题表达式直观图.png

问题表达式2直观图.png