操作符详解

目录

操作符分类

A.算术操作符

B.移位操作符

C.位操作符

1.按位与

2.按位或

3.按位异或:

D.赋值操作符

E.单目操作符

F.关系操作符

G.逻辑操作符

H.条件操作符(三目操作符)

I.逗号表达式

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

1.下标引用操作符

2.函数调用操作符

3.结构成员访问操作符


操作符分类

A.算术操作符

+ - * / %

/ 取整操作符,举例

1.0 / 2 = 0.5

1.0 / 2.0 = 0.5

% 取模操作符,得到的是计算后的余数

取模操作符的两端必须是整数

B.移位操作符

>>右移操作符

<<左移操作符

注:移位操作符的操作数只能是正整数

移位操作符移动的是二进制位

整数的二进制表示有三种,分别为原码,反码,补码

正的整数的原码,反码,补码相同

负的整数的原码,反码,补码,需要计算

举例

十进制的123为什么是123呢

因为123的个位的权重是10º,所以就是3*10º,十位的权重是10¹,所以就是2*10¹,百位的权重是10²,所以就是1*10²,相加就是123

二进制的7(111)也是相同的道理

分别是1*2²,1*2¹,1*2º,相加得7,

因为7是正数所以它的二进制补码最高位是0,

7是一个整数,站四个字节,共32bit,则

7的二进制序列是00000000 00000000 00000000 00000111 - 原码

00000000 00000000 00000000 00000111 - 反码

00000000 00000000 00000000 00000111 - 补码

-7的二进制序列是10000000 00000000 00000000 00000111 - 原码

11111111 11111111 11111111 11111000 - 反码(原码的符号位不变,其他位按位取反)

11111111 11111111 11111111 11111001 - 补码(在反码的基础上,把反码+1,就是补码)

整数在内存中的存放形式是补码

移位操作符在内存中移动的是补码

#define _CRT_SECURE_NO_WARNINRS
#include <stdio.h>

int main()
{
    int a = 7;
    int b = a << 1;
    //左移操作符

    printf("a = %d\n",a);
    printf("b = %d",b);    

    return 0;
}

7的补码,左移一位,在后面补0,00000000 00000000 00000000 00001110

也就是1*2³,1*2²,1*2¹,0*2º,相加得14

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main()
{
    int a = -7;
    int b = a << 1;

    printf("%d\n",a);
    printf("%d\n",b);

    return 0;
}

-7的补码,左移一位,补0,得到-7的补码,然后-7的补码-1,按位取反,符号位不变,得到-7的原码10000000 00000000 00000000 00001110,得-14

结论:左移有*2的特点

右移操作符

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

逻辑移位:右边丢弃一位,左边补0(首位数字,0代表正数,1代表负数)

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main()
{
    int a = 7;
    
    int b = a >> 1;

    printf("%d\n", a);
    printf("%d\n", b);

    return 0;
}

正数看不出使用了哪种移位,因为首位都是0

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main()
{
    int a = -7;
    int b = a >> 1;

    printf("%d\n",a);
    printf("%d\n",b);

    return 0;
}

-7的补码是11111111 11111111 11111111 11111001,如果是

逻辑移位编码变成01111111 11111111 11111111 11111100,由于首位是0为正数,所以它的原,反,补码均为上面的01111111 11111111 11111111 11111100

算术移位变成10000000 00000000 00000000 00000100

vs编译器的右移操作符采用的是算术右移,大部分编译器采用的是算术右移

注意:移位操作数只能是正整数,不要移动负数位,负数在标准里未定义

C.位操作符

位操作符有:

按位与     &         按(2进制)位与

按位或     |          按(2进制)或

按位异或     ^     按(2进制)异或

注:它们的操作数必须是整数

1.按位与

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main()
{
    int a = 3;
    int b = -5;
    int c = a & b;

    printf("c = %d\n",c);

    return 0;
}

结果是3,为什么结果是3

 3的原码00000000 00000000 00000000 00000011

-5的原码10000000 00000000 00000000 00000101

-5的反码11111111 11111111 11111111 11111010(符号位不变,按位取反)

-5的补码11111111 11111111 11111111 11111011(在反码的基础上+1)

 3的补码00000000 00000000 00000000 00000011

当-5和3按位与的时候,两个位含有0的时候就是0,两个都是1的时候才是1

则-5和3按位与的结果是

00000000 00000000 00000000 00000011(补码)

%d是打印一个有符号的整数

求00000000 00000000 00000000 00000011的原码

首位是0,其原码为00000000 00000000 00000000 00000011

结果是3

结论:两个位含有0的时候就是0.两个都是1的时候才是1

2.按位或

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main()
{
    int a = 3;
    int b = -5;

    int c = a|b;
    printf("%d",c);

    return 0;
}

3的补码,00000000 00000000 00000000 00000011

-5的补码,11111111 11111111 11111111 11111011

3和-5按位或后,得11111111 11111111 11111111 11111011

也就是-5

结论: 两个对应的位,含有1就是1,两个都为0才是0

3.按位异或:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main()
{
    int a = 3;
    int b = -5;

    int c = a ^ b;
    printf("%d",c);

    return 0;
}

先得出3和-5的二进制补码

分别为

00000000 00000000 00000000 00000011

11111111 11111111 11111111 11111011

3和-5异或后得到

11111111 11111111 11111111 11111000(补码)

11111111 11111111 11111111 11110111(反码)

10000000 00000000 00000000 00001000(原码)

-8

结论:相同为0,相异为1

a ^ a = 0,a ^ 0 = a

3^3^5 = 5

3^5^3 = 5

异或操作符支持交换律

二进制的减法,向上一位借数的时候当成借2

比如100-001 = 011

操作符的应用:

法1.不创建临时变量,实现两个数的交换

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main()
{
    int a = 3;
    int b = 5;

    a = a + b;
    b = a - b;
    a = a - b;

    return 0;
}

如果a+b的值极大,就存在溢出的问题

法2.用按位异或操作符实现两个数的交换

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main()
{
    int a = 3;
    int b = 5;
    printf("交换前:%d %d",a,b);
    
    a = a ^ b;//a = 3^5
    b = a ^ b;//b = 3^5^5 = 3
    a = a ^ b;//a = 3^5^3^5^5 = 5

    printf("交换后:%d %d",a,b);

    return 0;
}

练习:

编写代码实现:求一个整数存储在内存中的二进制中1的个数

求补码中二进制中1的个数

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main()
{
    int num = 10;
    
    return 0;
}

D.赋值操作符

赋值操作符是一个很棒的操作符,它可以让你得到一个你之前不满意的值,也就是你可以给自己重新赋值

int weight = 109;//初始化
weight = 80;//赋值,不满意就换值

赋值操作符可以连续使用

复合赋值符:

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

    a=a+5;
    //等价于
    a+=5;

E.单目操作符

!        逻辑反操作

-          负值

+         正值

&         取地址

sizeof        操作数的类型长度(以字节为单位)

~          对一个数的二进制按位取反

--         前置,后置--

++       前置,后置++

*         间接访问操作符(解引用操作符)

(类型)        强制类型转换

sizeof 计算的是变量所占内存空间的大小

即类型所创建的变量占内存空间的大小,单位是字节

sizeof 还可以直接统计数组总长度

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main()
{
    int a = 2;
    int c = sizeof(a);//计算的是a所占内存的大小,单位是字节

    printf("%d\n",sizeof(a));
    printf("%d\n",sizeof(int));

    printf("%d\n",sizeof a);//right,正确
    //printf("%d\n",sizeof int);//err,错误
    return 0;
}

sizeof是操作符,不是函数

strlen是库函数,用来求字符串长度

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

void test1(int arr[])
{
    printf("%d",arr);//4
}

void test2(char ch[])
{
    printf("%d",ch);//4,因为传入的参数是数组首元素的地址,指针变量的大小都是4字节
}

int main()
{
    int arr[10] = { 0 };
    char ch[10] = { 0 };

    printf("%d\n",sizeof(arr));//40
    printf("%d\n",sizeof(ch));//10

    return 0;
}

~  按位取反操作符

按位取反后符号位也按位取反

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main()
{
    int a = 0;
//00000000 00000000 00000000 00000000  //0的补码
printf("%d\n", ~a);
//按位取反
//11111111 11111111 11111111 11111111
//11111111 11111111 11111111 11111110
//10000000 00000000 00000000 00000001
//得到-1

    int b = 3;
//00000000 00000000 00000000 00000011  //3的补码
//按位取反
//11111111 11111111 11111111 11111100
//11111111 11111111 11111111 11111011
//10000000 00000000 00000000 00000100
//结果是-4

    return 0;
}

修改一个整数在内存中的二进制序列中的某位数

将某一位中的0换成1

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main()
{
    int a = 13;

//00000000 00000000 00000000 00001101
//将第二位置换成1
//按位或有1就是1,两个都是0才是0
//让第二位与00000000 00000000 00000000 00000010按位或就可以得到所需结果
//也就是00000000 00000000 00000000 00001111 --- 补码,正数,原反补码相同
//15

//如何得到00000000 00000000 00000000 00000010
//可以理解为将1的二进制序列向左移动一位获取
//即1<<1

    a |= (1<<1);
    printf("%d",a);

    return 0;
}

 将某一位的1换成0

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main()
{
    int a = 29;
//00000000 00000000 00000000 00011101
//将第五位换成0
//按位与,有0就是0,两个都是1才是1
//11111111 11111111 11111111 11101111
//可以通过00000000 00000000 00000000 00010000按位取反得到
//00000000 00000000 00000000 00010000可以通过1左移4位得到

    a &= (~(1<<4));
    printf("%d",a);

    return 0;
}

++ 和 --

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main()
{
    int a = 3;
    printf("%d\n",a++);//结果是3,因为a++先取值再自增
    printf("%d",a);//结果是4


    return 0;   
}

F.关系操作符

>

>=

<

<=

!=        用于测试不相等

==       用于测试相等

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main()
{

    if("abc" == "abcde")
    //这里不是比较两个字符串的长度,而是比较两个字符串首元素的地址是否相等
    {
    }

    return 0;
}

G.逻辑操作符

&&     逻辑与

||        逻辑或

区分逻辑与和按位与

区分逻辑或和按位或

逻辑与只关注真假,真为1,假为0,两个都为真输出1,有一个为假就是假,输出0

逻辑或只关注真假,有一个为真就是真输出1,两个都为假才为假,输出0

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main()
{
    int a = 3;
    int b = 5;
    int c = a&&b;
printf("%d",c);

//    if( c )
//{
//    printf("%d",c);
//}
    return 0;
]

练习

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main()
{
    int i = 0, a = 0, b = 2, c = 3, d = 4;
    i = a++ && ++b && d++;

    printf("a= %d\nb = %d\nc = %d\nd = %d\n",a,b,c,d);

    return 0;
}

在i = a++&&++b&&d++中,a先取值,后自增,此时a为0,逻辑或后,结果仍为假,则后面的自增不再执行,即只有a执行了自增,b和d没有执行自增

则输出结果是1,2,3,4

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main()
{
    int i = 0, a = 1, b = 2, c = 3, d = 4;
    i = a++||++b||d++;

    printf("a = %d\nb = %d\nc = %d\nd = %d\n",a,b,c,d);

    return 0;
}

在i = a++||++b||d++中,当a++判定为真时,在逻辑或语句中,如果前面判定为真,后面的语句不再执行,即a为2,b为2,d为4

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main()
{
    int i = 0, a = 0, b = 2, c = 3, d = 4;
    i = a++||++b||d++;

    printf("a = %d\nb = %d\nc = %d\nd = %d\n",a,b,c,d);

    return 0;
}

在i = a++||++b||d++中,a为0,继续向后执行,接着判断到b为3,结果为真,停止判断,输出结果是0,3,3,4

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

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

H.条件操作符(三目操作符)

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

表达式1为真,就执行表达式2

表达式1为假,就执行表达式3

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main()
{
    int a = 3;
    int b = 0;

    if(a>0)
        b = 3;
    else
        b = -3;
//等价于

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

    return 0;
}

输出两个数中较大的一个

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main()
{
    int a = 1;
    int b = 3;

    int max = (a>b?a:b);

    return 0;
}

I.逗号表达式

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

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

代码1

int a = 1;
int b = 2;
int c = (a>b,a=b+10,a,b=a+1);
//输出13

代码2

a = get_val();
count_val(a);

while(a>0)
{

    //业务处理

    a = get_val();
    count_val(a);

}
while(a=get_val(),count_val(a),a>0)
{
    //业务处理
}

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

1.下标引用操作符

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main()
{
    int arr[10] = { 0 };
    arr[7] = 8;

//这里的[]是操作数,arr和7都是操作数
//也可以写成
    7[arr] = 19;//不推荐使用

//arr[7]也等价于*(arr+7)也等价于*(7+arr)
//arr是数组首元素地址
//arr+7就是跳过7个元素指向第八个元素
//*(arr+7)就是第八个元素

    return 0;
}

2.函数调用操作符

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int Add(int x, int y)
{
    return x+y;
}

int main()
{
    int a = 10;
    int b = 20;

    //函数调用
    //()就是函数调用操作符
    int c = Add(a,b);
    //操作数分别是Add,a,b

    return 0;
}

3.结构成员访问操作符

.           结构体.成员名

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

ps->age

等价于

(*ps).age

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

//定义类型
struct Stu
{
    char name[20];
    int age;
    double score;
};

//赋值
void set_stu(struct Stu ss)
{

    // ss.name = "zhangsan";
    //会报错,因为数组名是地址,不能把字符串赋值给地址
    //要把zhangsan放到name所指向的空间里
    strcpy(ss.name,"zhangsan");
    //strcpy把zhangsan拷贝放到name所指向的空间里
    ss.age = 20;
    ss.score = 100.0;
}
//打印
void print_stu(struct Stu ss)
{
    printf("%s %d %lf\n",ss.name,ss.age,ss.score);

}
int main()
{
    struct Stu s = { 0 };//初始化
    set_stu(s);//赋值
    print_stu(s);//打印

    return 0;
}

打印的结果都是0,因为形参不改变实参,赋值只改变了形参的值,实参仍然处于初始化状态

 要传实参的地址过去,来达到给实参赋值的功能

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

//定义类型
struct Stu
{
    char name[20];
    int age;
    double score;
};

//赋值
void 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 print_stu(struct Stu *ps)
{
    printf("%s %d %lf\n",ps->name,ps->age,ps->score);
//比ss.name更加节省空间,因为ps->name直接从Stu中提取数据,省去了拷贝数据再提取的步骤
}
int main()
{
    struct Stu s = { 0 };//初始化
    set_stu(&s);//赋值
    print_stu(s);//打印

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值