操作符详解(1)

简介: 操作符详解(1)


1. 算术操作符

算术操作符分为:+、-、*、/、%

  1. 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数。
  2. 对于 / 操作符如果两个操作数都为整数,执行整数除法;而只要有浮点数,执行的就是浮点数除法。
  3. % 操作符的两个操作数必须为整数,返回的是整除之后的余数。
#include <stdio.h>
int main()
{
  //int r = 7 / 2;
  //printf("%d\n", r);//3
  //double d = 7 / 2;
  //printf("%lf\n", d);//3.000000
  double d = 7 / 2.0;
  printf("%lf\n", d);//3.500000
  
  return 0;
}
#include <stdio.h>
int main()
{
  int r = 15 % 8;//% 得到的是整除后的余数
  printf("%d\n", r);//7
  return 0;
}

以下写法是错误的:

#include <stdio.h>
int main()
{
  int n = 0;
  int r = 5 / n;
  return 0;
}

2. 移位操作符

<< 左移操作符

>> 右移操作符

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

int main()
{
  int a = 15;
  int b = a >> 1;//移动的是a中的2进制信息
  return 0;
}

我们先来看一下整数的三种二进制表示形式:

int main()
{
  int a = 15;
  //00000000000000000000000000001111 - 原码
  //00000000000000000000000000001111 - 反码
  //00000000000000000000000000001111 - 补码
  int b = -15;
  //10000000000000000000000000001111 - 原码
  //11111111111111111111111111110000 - 反码(原码的符号位不变,其他位按位取反得到的就是反码)
  //11111111111111111111111111110001 - 补码(反码+1就是补码)
  
  //整数在内存中存储的是补码
  //计算的时候也是使用补码计算的
  return 0;
}

注: 符号位是1表示负数,符号位是0表示正数

2.1 右移操作符

右移运算分两种:

  1. 算术右移:右边丢弃,左边补原来的符号位
  2. 逻辑右移:右边丢弃,左边直接补0

注: C语言没有明确规定到底是算术右移还是逻辑右移,一般编译器上采用的是算术右移

//移位移动的是补码的二进制序列
#include <stdio.h>
int main()
{
  int a = 15;
  int b = a >> 1;
  printf("%d\n", b);//7
  printf("%d\n", a);//15
  return 0;
}
#include <stdio.h>
int main()
{
  int a = -15;
  int b = a >> 1;
  printf("%d\n", b);//-8
  printf("%d\n", a);//-15
  return 0;
}

由以上两个例子我们可以得知:右移1位可以理解为除2再向下取整

2.2 左移操作符

左移:左边丢弃,右边补0

#include <stdio.h>
int main()
{
  int a = 6;
  //左移操作符 - 左边丢弃,右边补0
  //[00000000000000000000000000000110] - 6的补码
  //[00000000000000000000000000001100] - 补码
  int b = a << 1;
  printf("%d\n", b);//12
  printf("%d\n", a);//6
  //a = a << 1;
  //a <<= 1;
  //a = a + 1;
  //a += 1;
  return 0;
}

以下写法是有问题的:

int main()
{
  int a = 5;
  int b = a >> -2;//标准未定义行为
  return 0;
}

3. 位操作符

  1. & 按位与
  2. | 按位或
  3. ^ 按位异或

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


  1. & 按位与
#include <stdio.h>
int main()
{
  int a = 3;
  //00000000000000000000000000000011 - 补码
  int b = -5;
  //10000000000000000000000000000101
  //11111111111111111111111111111010
  //11111111111111111111111111111011 - 补码
  int c = a & b;
  //& -- 对应二进制位有0则为0,两个同时为1,才是1
  //00000000000000000000000000000011
  //11111111111111111111111111111011
  //00000000000000000000000000000011 - 补码
  printf("%d\n", c);//3
  return 0;
}

  1. | 按位或
#include <stdio.h>
int main()
{
  int a = 3;
  //00000000000000000000000000000011 - 补码
  int b = -5;
  //10000000000000000000000000000101
  //11111111111111111111111111111010
  //11111111111111111111111111111011 - 补码
  int c = a | b;
  //| -- 按(2进制)位或 - 对应的二进制位有1则为1,两个同时为0才是0
  //00000000000000000000000000000011
  //11111111111111111111111111111011
  //11111111111111111111111111111011 - 补码
  //11111111111111111111111111111010
  //10000000000000000000000000000101 - 原码
  printf("%d\n", c);//-5
  return 0;
}

  1. ^ 按位异或
#include <stdio.h>
int main()
{
  int a = 3;
  //00000000000000000000000000000011 - 补码
  int b = -5;
  //10000000000000000000000000000101
  //11111111111111111111111111111010
  //11111111111111111111111111111011 - 补码
  int c = a ^ b;
  //^ - 按(二进制)位异或 - 对应的二进制位相同为0,相异为1
  //00000000000000000000000000000011
  //11111111111111111111111111111011
  //11111111111111111111111111111000 - 补码
  //11111111111111111111111111110111
  //10000000000000000000000000001000 - 原码
  printf("%d\n", c);//-8
  return 0;
}

接下来我们举一个按位异或的例子:

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

首先,我们来复习一下通过创建临时变量来实现两个数的交换:

#include <stdio.h>
int main()
{
  int a = 3;
  int b = 5;
  printf("交换前: a=%d b=%d\n", a, b);
  int tmp = a;
  a = b;
  b = tmp;
  printf("交换后: a=%d b=%d\n", a, b);
  return 0;
}

那么不创建临时变量要如何实现呢?

#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;*/  //但是有缺陷,a和b如果都很大,它们的和放到a里面会放不下(溢出),被截断
  
  a = a ^ b;
  b = a ^ b;
  a = a ^ b;
  printf("交换后: a=%d b=%d\n", a, b);
  return 0;
}

以上代码可能有些难以理解,接下来稍作解释:

int main()
{
  int a = 3;
  int b = 5;
  //a^a -> 0
  //a^0 = a
  //异或是支持交换律的
  //a^b^a = 5
  //011
  //101
  //110
  //011
  //101
  //a^a^b = 5
  return 0;
}

4. 赋值操作符

int main()
{
  int weight = 120;//体重
  weight = 89;//不满意就赋值
  double salary = 10000.0;
  salary = 20000.0;//使用赋值操作符赋值。
  
  return 0;
}

赋值操作符也可以连续使用,比如:

int main()
{
  int a = 10;
  int x = 0;
  int y = 20;
  a = x = y + 1;//连续赋值
  
  return 0;
}

同样的语义,还可以这样写:

int main()
{
  int a = 10;
  int x = 0;
  int y = 20;
  x = y + 1;
  a = x;
  
  return 0;
}

这样的写法更加清晰爽朗而且易于调试。


赋值操作符中还包括复合赋值符:+=、-=、*=、/=、%=、>>=、<<=、&=、|=、^=

int main()
{
  int x = 10;
  x = x + 10;
  x += 10;//复合赋值,其他运算符一样的道理,这样写更加简洁。
  
  return 0;
}

5. 单目操作符

!     逻辑反操作

-     负值

+     正值

&     取地址

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

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

–     前置、后置–

++    前置、后置++

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

(类型)   强制类型转换

  1. 逻辑反操作
#include <stdio.h>
int main()
{
  int flag = 0;
  if (0 == flag)
  {
    printf("hehe\n");
  }
  if (!flag)//flag 为假 打印hehe
  {
    printf("hehe\n");
  }
  if (flag)
  {
    printf("haha\n");
  }
  return 0;
}

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

  1. 取地址、间接访问操作符(解引用操作符)
//& * 应用于指针
#include <stdio.h>
int main()
{
  int a = 10;
  //pa是指针变量
  int *pa = &a;//&-取地址操作符-取出a的地址
  *pa = 20;//解引用操作符(间接访问操作符)-单目操作符-通过pa中存放的地址,找到指向的空间(内容)
  int c = *pa;
  return 0;
}

  1. sizeof
#include <stdio.h>
int main()
{
  int a = 10;
  printf("%d\n", sizeof(int));
  printf("%d\n", sizeof(a));
  printf("%d\n", sizeof a);//说明sizeof不是函数
  return 0;
}
#include <stdio.h>
int main()
{
  int arr[10] = { 0 };
  printf("%d\n", sizeof(arr));//40 - 计算整个数组的大小,单位字节
  printf("%d\n", sizeof(int[10]));
  
  return 0;
}

以下代码输出的分别是多少呢?

#include <stdio.h>
void test1(int arr[])//int*
{
  printf("%d\n", sizeof(arr));//4/8
}
void test2(char ch[])//char*
{
  printf("%d\n", sizeof(ch));//4/8
}
int main()
{
  int arr[10] = { 0 };
  char ch[10] = { 0 };
  printf("%d\n", sizeof(arr));//40
  printf("%d\n", sizeof(ch));//10
  test1(arr);//&arr[0]
  test2(ch);//&ch[0]
  
  return 0;
}

  1. 对一个数的二进制按位取反
//~ 按补码二进制位取反
#include <stdio.h>
int main()
{
  int a = 0;
  printf("%d\n", ~a);//-1
  //00000000000000000000000000000000
  //11111111111111111111111111111111 - 补码
  //11111111111111111111111111111110
  //10000000000000000000000000000001 - 原码
  return 0;
}

还记得多组输入要如何表示吗?

#include <stdio.h>
int main()
{
  int a = 0;
  while (1 == scanf("%d", &a))
  {
    printf("%d\n", a);
  }
  return 0;
}
#include <stdio.h>
int main()
{
  int a = 0;
  //scanf 读取失败返回的是EOF
  while (scanf("%d", &a) != EOF)
  {
    printf("%d\n", a);
  }
  return 0;
}

其实,也可以用~来表示

#include <stdio.h>
int main()
{
  int a = 0;
  //scanf 读取失败返回的是EOF
  //假设scanf 读取失败,返回EOF ---> -1
  //11111111111111111111111111111111 - 补码
  //00000000000000000000000000000000
  while (~scanf("%d", &a))
  {
    printf("%d\n", a);
  }
  return 0;
}

接下来,我们看一个题目(需要结合上面学的一些知识):

把13的二进制的第五位改成1,再改回来

#include <stdio.h>
int main()
{
  int a = 13;
  //00000000000000000000000000001101
  //00000000000000000000000000010000
  a |= (1 << 4);
  printf("%d\n", a);
  
  //00000000000000000000000000011101
  //11111111111111111111111111101111
  //00000000000000000000000000001101
  a &= (~(1 << 4));
  printf("%d\n", a);
  return 0;
}

  1. 前置、后置++;前置、后置–
#include <stdio.h>
int main()
{
  int a = 1;
  int b = a++;//后置++,先使用,后++
  //b=a,a=a+1
  printf("a=%d b=%d\n", a, b);//2 1
  return 0;
}
#include <stdio.h>
int main()
{
  int a = 1;
  int b = a--;//后置--,先使用,后--
  //b=a,a=a-1
  printf("a=%d b=%d\n", a, b);//0 1
  return 0;
}
#include <stdio.h>
int main()
{
  int a = 1;
  int b = ++a;//前置++,先++,后使用
  //a=a+1,b=a
  printf("a=%d b=%d\n", a, b);//2 2
  return 0;
}
#include <stdio.h>
int main()
{
  int a = 1;
  int b = --a;//前置--,先--,后使用
  //a=a-1,b=a
  printf("a=%d b=%d\n", a, b);//0 0
  return 0;
}

举例:

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

  1. 强制类型转换
#include <stdio.h>
int main()
{
  int a = (int)3.14;//强制
  printf("%d\n", a);
  // int a = int(3.14)//err
  return 0;
}

6. 关系操作符

>

>=

<

<=

!=   用于测试“不相等”

==  用于测试“相等”

这些关系运算符比较简单,就不过多进行讲解,只需要注意一下==和=不要搞混淆了。

7. 逻辑操作符

&&   逻辑与

||   逻辑或

//逻辑操作符
//&& || ! - 计算结果是真,使用1表示
#include <stdio.h>
int main()
{
  int a = 3 && 5;
  printf("%d\n", a);//1
  return 0;
}
#include <stdio.h>
int main()
{
  int a = 0;
  int b = 0;
  scanf("%d %d", &a, &b);
  //a 和 b 都是5 打印hehe
  if (5 == a && 5 == b)
  {
    printf("hehe\n");
  }
  //a 或者 b是5 打印haha
  if (5 == a || 5 == b)
  {
    printf("haha\n");
  }
  return 0;
}

判断闰年:

#include <stdio.h>
int main()
{
  int y = 0;
  scanf("%d", &y);
  //1. 能被4整除,并且不能被100整除
  //2. 能被400整除是闰年
  if (((0==y%4)&&(y%100!=0)) || (0==y%400))
  {
    printf("闰年\n");
  }
  else
  {
    printf("不是闰年\n");
  }
  return 0;
}

例题:

//&& 操作符,左边为假,右边就不计算了
//|| 操作符,左边为真,右边不再计算
#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\nb = %d\nc = %d\nd = %d\n", a, b, c, d);
  
  return 0;
}


目录
相关文章
|
2月前
操作符详解
操作符详解
14 0
|
5月前
|
SQL 数据库
IN 操作符
【7月更文挑战第15天】IN 操作符。
46 4
|
5月前
|
SQL 数据库
BETWEEN 操作符
【7月更文挑战第15天】BETWEEN 操作符。
45 3
|
7月前
|
存储 编译器
操作符详解1(一)
本文介绍了编程中的操作符分类和位操作,包括算术操作符(如 +, -, *, /, %)、移位操作符(<<, >>)、位操作符(&, |, ^)、赋值操作符、单目操作符、关系操作符、逻辑操作符和条件操作符。还讨论了原码、反码和补码的概念,以及左移(<<)和右移(>>)操作符的工作原理,包括逻辑右移和算术右移的区别。最后提到了位操作符按位与(&)、按位或(|)、按位异或(^)和按位取反(~)的运算规则。
61 1
|
7月前
|
编译器 C语言 C++
操作符详解2(二)
本文介绍了编程中的操作符转换和表达式解析规则。当操作数类型不同时,会进行寻常算术转换,按照long double、double、float等类型顺序进行向上转换。表达式求值时,虽然操作符有优先级,但不能决定操作数的求值顺序,例如`a*b + c*d + e+f`中乘法先于加法,但具体计算顺序不确定,可能导致不同结果。同样,如`c++ + c--`这样的表达式也是有歧义的,因为++和--的左右结合性不能确定操作数的获取顺序。文章强调,复杂的表达式可能因编译器差异产生不同结果,应避免使用可能导致非唯一计算路径的表达式以减少潜在风险。
51 0
|
7月前
|
存储 程序员 C语言
操作符详解1(二)
该内容是一个关于C语言操作符和结构体的教程摘要。首先提到了加法操作符`+`的使用,并给出了一种不使用临时变量交换两个数的方法。接着讨论了如何计算整数的二进制表示中1的个数,通过位操作符实现。然后介绍了逗号表达式和函数调用操作符`()`,以及结构体成员访问操作符`.`和`-&gt;`,用于访问和修改结构体内的成员变量。文章以讲解结构体的声明、初始化和通过指针访问结构体成员为重点,展示了如何直接和间接地操作结构体数据。
56 0
|
7月前
|
编译器 索引
操作符详解3
操作符详解3
48 0
|
存储
操作符详解上(非常详细)
操作符详解上(非常详细)
80 1
|
编译器
详解操作符(下)
详解操作符(下)
操作符详解(一)
操作符详解(一)
86 0