操作符详解(1)

简介: 操作符详解(1)

1. 操作符分类:

算术操作符

移位操作符

位操作符

赋值操作符

单目操作符

关系操作符

逻辑操作符

条件操作符

逗号表达式

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

2. 算术操作符

+ - * / %

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

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

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


3. 移位操作符

<< 左移操作符

>> 右移操作符

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

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

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

原码:按照数值的正负,直接写出的二进制序列就是原码,一个整数4个字节,32个bit位,一个整数的二进制序列就是32个bie位。对于有符号的整数来说,最高位是符号位,0为正数,1为负数。对于无符号整数来说,没有符号位,所有位都是有效位。

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

补码:反码的二进制+1得到补码。

对于正整数来说,原码,反码,补码相同,无需计算。

对于负整数来说,原码,反码,补码需要计算。

不管是正整数还是负整数,在内存中存储的都是补码的二进制序列。

整数在计算的时候使用的也是补码。

10
原码:00000000 00000000 00000000 00001010
反码:00000000 00000000 00000000 00001010
补码:00000000 00000000 00000000 00001010
-10
原码:10000000 00000000 00000000 00001010
反码:11111111 11111111 11111111 11110101
补码:11111111 11111111 11111111 11110110

3.1 左移操作符

移位规则:

左边抛弃、右边补0.

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
  //左移操作:左边丢弃,右边补0
  //00000000000000000000000000000111
  int m = 7;
  int n = m << 1;
  //00000000000000000000000000001110
  printf("%d\n", m);
  printf("%d\n", n);
  return 0;
}

在左移后m的值还是7,m只是参与运算,<<操作符有*2的类似效果。

负数:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
  //左移操作:左边丢弃,右边补0
  //原码:10000000000000000000000000000111
  //负数要计算补码
  //反码:11111111111111111111111111111000
  //补码:11111111111111111111111111111001
  int m = -7;
  int n = m << 1;
  //左移后:11111111111111111111111111110010
  //打印出来的是原码,所以我们计算左移后的原码,-1取反得到原码
  //11111111111111111111111111110001
  //10000000000000000000000000001110
  printf("%d\n", m);
  printf("%d\n", n);
  return 0;
}


左移操作符可以使得一些二进制数字来到我们想要的地方。

3.2 右移操作符

移位规则:

首先右移运算分两种:

1. 逻辑移位

左边用0填充,右边丢弃

2. 算术移位

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

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
  int a = -10;
  //原码:10000000 00000000 00000000 00001010
  //反码:11111111 11111111 11111111 11110101
  //补码:11111111 11111111 11111111 11110110
  int b = a >> 1;
  //逻辑位移:01111111 11111111 11111111 11111011
  //算数位移:11111111 11111111 11111111 11111011
  //反码:11111111 11111111 11111111 11111010
  //原码:10000000 00000000 00000000 00000101-> -5
  printf("a=%d\n", a);
  printf("b=%d\n", b);
  return 0;
}


使用逻辑位移还是算术位移取决的是编译器,大部分是算术右移,逻辑右移太简单粗暴。

右移有类似除2的效果。

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

例如:

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

4. 位操作符

位操作符有:

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

&运算,用补码进行运算,对应的二进制位有0则为0,两者为1才为1。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
  int a = 3;
  int b = -5;
  int c = a & b;//按(2进制)位与
  //00000000000000000000000000000011 --- 3的补码
  //10000000000000000000000000000101 
  //11111111111111111111111111111010
  //11111111111111111111111111111011 --- -5的补码
  //00000000000000000000000000000011 --- 3的补码
  //00000000000000000000000000000011
  printf("%d\n", c);
  return 0;
}

&的特点是得到某一个你想要的位。任何数字&1得到的他二进制位的最后一位。配合移位操作符将我们想要的二进制数字移位到最后一位,&1就可以得到这一位。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
  int a = 3;
  //如果想要得到3的最低位
  //a&1
  //00000000000000000000000000000011 --- 3的补码
  //00000000000000000000000000000001 ---1的补码
  //遇到0则为0,3的最低位之前的所有位都会变成0,如果结果是0,表示3的最低位是0,如果结果是1,3的最低位就是1
  return 0;
}

| 运算:

用补码进行运算,对应的二进制位有1则为1,两者为0才为0。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
  int a = 3;
  int b = -5;
  //00000000000000000000000000000011 --- 3的补码
  //10000000000000000000000000000101 
  //11111111111111111111111111111010
  //11111111111111111111111111111011 --- -5的补码
  int c = a | b;
  //00000000000000000000000000000011 --- 3的补码
  //11111111111111111111111111111011 --- -5的补码
  //11111111111111111111111111111011 
  //11111111111111111111111111111010
  //10000000000000000000000000000101   -5
  printf("%d\n", c);
  return 0;
}

^操作符:

相同为0,相异为1.\

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
  int a = 3;
  int b = -5;
  //00000000000000000000000000000011 --- 3的补码
  //10000000000000000000000000000101 
  //11111111111111111111111111111010
  //11111111111111111111111111111011 --- -5的补码
  int c = a ^ b;
  //00000000000000000000000000000011
  //11111111111111111111111111111011
  //11111111111111111111111111111000
  //10000000000000000000000000000111
  //10000000000000000000000000001000
  //
  printf("%d\n", c);
  return 0;
}

接下来我们看一道面试题:不创建临时变量,实现两个数的交换。

这个代码存在缺陷,变量的值如果太大就出问题了。

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

这个代码使用^操作符来实现,但是这种方法的效率不是很高,创建临时变量是最好的。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
  int a = 3;
  int b = 5;
  int c = 0;//中间变量
  printf("a=%d b=%d\n", a, b);
  a = a ^ b;//a = 3^5
  b = a ^ b;//b=3^5^5 b=3
  a = a ^ b;//a= 3^5^3^5^5 a=5
  printf("a=%d b=%d\n", a, b);
  return 0;
}

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;
这样的写法是不是更加清晰爽朗而且易于调试

复合赋值符

1. +=
2. -=
3. *=
4. /=
5. %=
6. >>=
7. <<=
8. &=
9. |=
10. ^=

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

比如:

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

6. 单目操作符

6.1 单目操作符介绍

单目操作符就是只有一个操作数。

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

!可以把假变成真,把真变成假。

&是取地址操作符,用指针变量来存储。

*是解引用操作符,*x是通过x中存放的地址,知道x指向的对象。

sizeof其实我们之前已经见过了,可以求变量(类型)所占空间的大小。

前置++:计算口诀:先+1,后使用。

后置++:口诀:先使用,后+1。

--跟++是一样的。

6.2 sizeof 和 数组

数组传参是是首元素地址,是指针,地址的大小在32位平台是4个字节,64位平台是8个字节。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#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);
  test2(ch);
  return 0;
}


7. 关系操作符

1. >
2. >=
3. <
4. <=
5. != 用于测试“不相等”
6. == 用于测试“相等


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

警告:

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

8. 逻辑操作符

1. && 逻辑与
2. || 逻辑或

&&都真才为真,||一个为真就是真。

360笔试题

#include <stdio.h>
int main()
{
int i = 0,a=0,b=2,c =3,d=4;
i = a++ && ++b && d++;
//i = a++||++b||d++;
printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
return 0;
}

a++,a首先是0,所以这个表达式都为假,后面的不需要算了,所以只有a+1了,其他没变,i=0。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include <stdio.h>
int main()
{
  int i = 0, a = 0, b = 2, c = 3, d = 4;
  /*i = a++ && ++b && d++;*/
  i = a++||++b||d++;
  printf(" a = %d\n b = %d\n c = %d\n d = %d\n i = %d\n", a, b, c, d,i);
  return 0;
}

这里有一个数不为0则为真,所以都要运算,i=1.


相关文章
|
3月前
|
存储 C++
C/C++中位操作符(&,|,^,~)的详解使用
C/C++中位操作符(&,|,^,~)的详解使用
|
10月前
|
编译器 C语言 索引
操作符详解下(非常详细)
操作符详解下(非常详细)
33 0
|
1月前
|
SQL 数据库
BETWEEN 操作符
【7月更文挑战第15天】BETWEEN 操作符。
23 3
|
2月前
|
编译器 Linux C语言
操作符详解(2)
操作符详解(2)
28 0
|
3月前
|
存储 C语言
操作符详解2(一)
C语言操作符具有优先级和结合性,决定表达式求值顺序。优先级高的运算符先执行,相同优先级时看结合性,左结合从左到右,右结合从右到左。例如,3+4*5中乘法优先级高于加法,5*6/2中乘法和除法优先级相同,但都是左结合,所以先计算5*6。圆括号具有最高优先级,可改变其他运算符优先级。表达式求值时,整型运算以缺省整型精度进行,小整型值会被提升为int或unsigned int。正数提升时高位补0,负数提升时高位补1,无符号整型提升始终补0。
36 0
|
3月前
|
存储 程序员 C语言
操作符详解1(二)
该内容是一个关于C语言操作符和结构体的教程摘要。首先提到了加法操作符`+`的使用,并给出了一种不使用临时变量交换两个数的方法。接着讨论了如何计算整数的二进制表示中1的个数,通过位操作符实现。然后介绍了逗号表达式和函数调用操作符`()`,以及结构体成员访问操作符`.`和`-&gt;`,用于访问和修改结构体内的成员变量。文章以讲解结构体的声明、初始化和通过指针访问结构体成员为重点,展示了如何直接和间接地操作结构体数据。
31 0
|
3月前
|
编译器 C语言 C++
操作符详解2(二)
本文介绍了编程中的操作符转换和表达式解析规则。当操作数类型不同时,会进行寻常算术转换,按照long double、double、float等类型顺序进行向上转换。表达式求值时,虽然操作符有优先级,但不能决定操作数的求值顺序,例如`a*b + c*d + e+f`中乘法先于加法,但具体计算顺序不确定,可能导致不同结果。同样,如`c++ + c--`这样的表达式也是有歧义的,因为++和--的左右结合性不能确定操作数的获取顺序。文章强调,复杂的表达式可能因编译器差异产生不同结果,应避免使用可能导致非唯一计算路径的表达式以减少潜在风险。
29 0
|
3月前
|
编译器 索引
操作符详解3
操作符详解3
27 0
|
编译器
详解操作符(下)
详解操作符(下)
|
存储 编译器 C语言
7.27C语言知识点之操作符
7.27C语言知识点之操作符
47 0