第五节-操作符详解(上)

第五节-操作符详解(上)

1. 操作符分类:

算术操作符

移位操作符

位操作符

赋值操作符

单目操作符

关系操作符

逻辑操作符

条件操作符

逗号表达式

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


2. 算术操作符

   +    -    *    /    %

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

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

⭐️3. ​% 操作符的两个操作数必须为整数。返回的是整除之后的余数。


3.移位操作符

<< 左移操作符
>> 右移操作符
    
注:移位操作符的操作数只能是整数。
    操作的对象是补码。

移动的是数的二进制位

数在计算机中运算是利用补码

但是 在输出时会转化为原码输出


二进制说明

正数的二进制有三种表示方式:原码,反码,补码。

✏️ 例:

a = 2

00000000 00000000 00000000 00000010 原码
00000000 00000000 00000000 00000010 反码
00000000 00000000 00000000 00000010 补码

正整数 原码、反码、补码相同

✏️例:

a = -2

10000000 00000000 00000000 00000010 原码

11111111 11111111 11111111 11111101 反码

11111111 11111111 11111111 11111110 补码

  1. 负数原码 --> 反码

    符号位不变,其他位按位取反。

  2. 反码 --> 补码

    反码加一,得到补码。
    (记忆:补码需要补上一)

计算机在计算中运算使用的是补码

3.1左移操作符

⭐️移位规则:

左边抛弃、右边补0
在这里插入图片描述

✏️例:

a = -2;
b = a << 1;

10000000 00000000 00000000 00000010 原码

11111111 11111111 11111111 11111101 反码

11111111 11111111 11111111 11111110 补码

在这里插入图片描述

移动的是补码,然后再转换成原码输出


3.2右移操作符

⭐️移位规则:

首先右移运算分两种:

  1. 逻辑移位

    左边用0填充,右边丢弃

  2. 算术移位

    左边用原该值的符号位填充,右边丢弃

具体用哪一种方式,由编译器决定

(vs编译器用的是算术移位)

⭐️警告⚠ :

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

例如:

int num = 10;
num>>-1;//error

✏️例:

a = -2;
b = a >> 1;

10000000 00000000 00000000 00000010 原码

11111111 11111111 11111111 11111101 反码

11111111 11111111 11111111 11111110 补码

在这里插入图片描述

⭐️ 1. 负数移位,不要移动符号位!

⭐️ 2. 向右移动后,补上符号位。

⭐️ 3. 无符号整数也看成正数。

⭐️ 4. 移位操作符的操作数必须是正整数。​


4.位操作符

位操作符有:

& //按位与
| //按位或
^ //按位异或
注:他们的操作数必须是整数。

⭐️ 操作数依然为 数的二进制​的补码。

⭐️ & 按(二进制)与 :二进制位都为1才是1

⭐️ | 按(二进制)或:二进制位至少有一个是1为1

⭐️ ^ 按(二进制)异或 :二进制位不同则为1

✏️ 练习一下:

#include <stdio.h>
int main()
{
	int num1 = -1;
	int num2 = 2;

	printf("%d\n", num1 & num2);//2
	printf("%d\n", num1 | num2);//-1
	printf("%d\n", num1 ^ num2);//3

	return 0;
}

⭐️注意以%d的形式打印,需要将补码转换成原码!

11111111 11111111 11111111 11111111  -1的补码
00000000 00000000 00000000 00000010   2的补码
00000000 00000000 00000000 00000010  -1&2的补码
11111111 11111111 11111111 11111111  -1|2的补码
以%d的形式打印,需要将补码转换成原码
  1. 观察符号位,发现 -1&2 为负数

  2. 负数 补码—>反码—>原码

    补码—>反码 :补码减一

    反码—>原码 :除符号位以外,按位取反

⭐️1. 符号位为0,所以为正数,原码、反码、补码大小相同。

⭐️2. 只要放在内存中就是补码。​

❓一道变态的面试题:

不能创建临时变量(第三个变量),实现两个数的交换。

方法一:

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

方法一 :当a+b的结果足够大时,可能会出现溢出的现象。

方法二:

#include <stdio.h>
int main()
{
	int a = 10;
	int b = 20;
	a = a ^ b;  a=10^20
	b = a ^ b;  10^20^20 ···> b=10
	a = a ^ b;  10^20^20 ···> a=20
	printf("a = %d b = %d\n", b, a);
	return 0;

例 : 3 ^ 3 和 3 ^ 0
3 的原码: 00000000000000000000000000000011
0 的原码: 00000000000000000000000000000000

3 ^ 3结果:00000000000000000000000000000000
3 ^ 0结果:00000000000000000000000000000011

存在规律:
a ^ a = 0
a ^ 0 = a

方法二 :更加安全,不会存在溢出的现象。但可读性不够好,方法一操作效率更高,而且异或只能用于整形 。实践中多用方法一。

✏️例子 练习:
999

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


5.赋值操作符

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

int weight = 120; 体重
weight = 89; 不满意就赋值
double salary = 10000.0;
salary = 20000.0; 使用赋值操作符赋值。

赋值操作符可以连续使用,比如:
int a = 10;
int x = 0;
int y = 20; a = x = y+1; 连续赋值
这样的代码感觉怎么样?
    
那同样的语义,你看看:
x = y+1; 
a = x;
这样的写法是不是更加清晰爽朗而且易于调试。

复合赋值符

+=

-=

*=

/=

%=

>>=

<<=

&=

|=

^=

这些运算符都可以写成复合的效果。

比如:

int x = 10;
x = x+10; 
x += 10;  复合赋值
 其他运算符一样的道理。这样写更加简洁。

6.单目操作符

6.1单目操作符介绍

弹幕操作符:只有一个操作数

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

6.1.1 !

! :逻辑反操作

C语言中,0表示假,非0表示真。

int main()
{
    int flag = 3;
    
    //flag为真,进入if
    if (flag)
    {}
    
    //flag为假,进入if
    if (!flag)
    {}
    return 0;
}

6.1.2 + / -

int main()
{
    int a = -10;
    int b = +10;
    printf("%d", a);//10
    printf("%d", b);//10
    return 0;
}

6.1.3 &

int main()
{
    int a = 10;
    printf("%p", &a);//10
    return 0;
}
  • ​ ⭐️取出的是变量在内存中的起始地址

  • ​ ⭐️%p :取地址

int *p = &a;
  • p是指针变量,是专门用来存放地址的
  • p的类型是int *

6.1.4 sizeof

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

6.1.5 ~

按(二进制)位取反

int a = 0;
00000000 00000000 00000000 00000000 补码
11111111 11111111 11111111 11111111 ~0 (还是补码,需要转化成原码才能打印出来)
11111111 11111111 11111111 11111110 反码
10000000 00000000 00000000 00000001 原码

❓用途

✏️例 :把二进制某一位由0改为1 或者由1

把从低到高第四位由0改成1

00000000 00000000 00000000 00000001 1
00000000 00000000 00000000 00001101 a
00000000 00000000 00000000 00010000  1 << 4  
  
1. 1 << 41向左移动四位 
    00000000 00000000 00000000 00010000
2. a |= (1 << 4)
    00000000 00000000 00000000 00011101

把从低到高第四位由1改成0

00000000 00000000 00000000 00011101    a
00000000 00000000 00000000 00000001    1
00000000 00000000 00000000 00010000  1 << 4
    
1. 1 << 41向左移动四位
    00000000 00000000 00000000 00010000
2. ~1 << 4) 按位取反  
    11111111 11111111 11111111 11101111
    00000000 00000000 00000000 00011101 a
3. a &= ~1 << 400000000 00000000 00000000 00001101

6.1.6 ++/–

在这里插入图片描述

b = ++a
  • 前置++

    a先加一,再赋值给b。

  • 后置++

    a先赋值给b,再加一。

  • 同理可得前置 --和后置 –


6.1.7 *

int main()
{
    int a = 10;
    int *p = &a;
    *p = 20;
    printf("%d\n" ,a);
    
    return 0;
}

在这里插入图片描述

*p 通过地址找到原来的数


6.1.8 ()

int main()
{
    int a = (int)3.14;
    printf("%d", a);
    return 0;
} 

✏️

srand((int)time(NULL));

time 函数的返回值类型是 time_t 实际上又是long long int 类型,利用(int)强制类型转换


6.2 sizeof 和数组

  • sizeof是一个操作符。
  • 计算的是变量所占内存空间的大小。
  • 计算类型所占空间的大小,单位是字节。
int a = 10;
int n = sizeof(int);//4
int m = sizeof(a);//4

演示代码:

#include <stdio.h>
int main()
{
	int a = -10;
	int* p = NULL;
	printf("%d\n", !2);//0
	printf("%d\n", !0);//1
	a = -a;
	p = &a;
	printf("%d\n", sizeof(a));//4
	printf("%d\n", sizeof(int));//4
	printf("%d\n", sizeof a);//这样写行不行? //ok
	printf("%d\n", sizeof int);//这样写行不行?//err
	return 0;
}

在这里插入图片描述

⭐️1. sizeof是操作数,不是函数。

⭐️2. strlen是库函数,仅用来求字符串长度。

#include <stdio.h>
void test1(int arr[])
{
	printf("%d\n", sizeof(arr));//(2) 
    传过去的是指针,是地址,用指针变量接受
}
void test2(char ch[])
{
	printf("%d\n", sizeof(ch));//(4)
    传过去的是指针,是地址,用指针变量接受
}
int main()
    
{
	int arr[10] = { 0 };
	char ch[10] = { 0 };
	printf("%d\n", sizeof(arr));//(1)
	printf("%d\n", sizeof(ch));//(3)
	test1(arr); 传过去的是arr数组的首元素的地址
	test2(ch);  传过去的是arr数组的首元素的地址
	return 0;
}

问:
(1)、(2)两个地方分别输出多少?
(3)、(4)两个地方分别输出多少?
    
40
4
10  实际上把十个元素的第一个元素的地址传过去了
4   同上,并且指针不管是什么类型,他的大小都是四个字节或者八个字节。

7.关系操作符

关系操作符

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

这些关系运算符比较简单,没什么可讲的,但是我们要注意一些运算符使用时候的陷阱。

⭐️警告:

在编程的过程中== 和=不小心写错,导致的错误。

int main()
{
    if ("abc" == "abcdef");
    这样写是比较两个字符串首字符的地址
    {
        
    }
    
    return 0;
}

⭐️ ⭐️ ⭐️

两个字符串比较大小

  • 不能用 == 比较
  • 应该用库函数 strcmp 比较
    str cmp :
    str : string (字符串)
    cmp : compare (比较)

8.逻辑操作符

逻辑操作符有哪些:

&&     逻辑与
||     逻辑或

区分逻辑与按位与

区分逻辑或按位或

1&2----->0 
1&&2---->1 
    
1|2----->3 
1||2---->1
int main()
{
    int a = 3;
    int b = 5;
    int c = a && b; 并且
    int d = a || b; 或者
    printf("%d\n", c);
    printf("%d\n", d);
    
    return 0}

✏️ 例:判断 是否为闰年

int is_leap_year(int y)
{
    if((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0))
    { 
        return 1;
    }
    else
    {
        return 0;
    }
}

✏️360笔试题

#include <stdio.h>
int main()
{
    int i = 0,a=0,b=2,c =3,d=4;
    i = a++ && ++b && d++;
    printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
    return 0; 
    }
//程序输出的结果是什么?

1 2 3 4
#include <stdio.h>
int main()
{
    int i = 0,a=0,b=2,c =3,d=4;
    i = a++||++b||d++;
    printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
    return 0; 
    }
//程序输出的结果是什么?

1 3 3 4
  • 或运算符 | 当遇到 真 时就不再向下进行
  • 与运算符 & 当遇到 假 时就不再向下进行
  • a 不论前置++,还是后置++,a本身都会加一​
  • 前置++/-- :先自身加一,再代入运算。
  • 后置++/-- :先代入运算,再自身加一。
  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值