操作符精讲——这些操作符你还记得几个?

简介: 操作符精讲——这些操作符你还记得几个?

前言

Hello各位老铁们,还记得我在 初识C语言(下) 中介绍的操作符吗?在初识部分,我向大家罗列了C语言中所有的操作符,并且选择性的讲解了一些常用操作符。本章在此基础之上,继续深入学习和使用操作符!

一、操作符分类

下面是C语言中所有的操作符种类:

  1. 算术操作符
  2. 移位操作符
  3. 位操作符
  4. 赋值操作符
  5. 单目操作符
  6. 关系操作符
  7. 逻辑操作符
  8. 条件操作符
  9. 逗号表达式
  10. 下标引用、函数调用和结构成员

在初识C语言(下)中,小编已经对部分操作符进行了详细介绍,这里就不在赘述。下面就对余下操作符进行讲解:

二、操作符精讲

🍑1、原码、反码、补码

在介绍移位操作符和位操作符之前,我们有必要了解一下有符号整数在计算机中的存储方式:

  1. 首先整数在计算机中以2进制表示,其中有符号整形的2进制又包含原码、反码以及补码。
  2. 规定有符号整数的最高位表示符号位,0表示正数,1表示负数。其它位为数值位。
  3. 正整数的原码、反码、补码相同。
  4. 负整数的原码:即直接将数值翻译为2进制位+符号位。
  5. 负整数的反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。
  6. 负整数的补码:反码+1就得到补码。
  7. 整数在内存实际存放的是补码,而我们使用的是原码。

📝例如:

正整数的原、反、补相同:

负整数的原反补需要转换:

🍑2、移位操作符

<< 左移操作符

>> 右移操作符

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

(1)左移操作符

移位规则:左边抛弃、右边补0

📝例如:以4和-4为例

注意:a<<1,当a的未被赋值的情况下,自身的值不会发生变化。右移同理

代码展示:

#include<stdio.h>
int main()
{
  int a = 4;
  //00000000000000000000000000000100 - 4的补码
  int b = a << 1;//把a向左移动一位
  //00000000000000000000000000001000 - 8的补码
  printf("a=%d b=%d\n", a, b);
  return 0;
}
#include<stdio.h>
int main()
{
  int a = -4;
  //10000000000000000000000000000100 - -4的原码
  //11111111111111111111111111111011 - -4的反码
  //11111111111111111111111111111100 - -4的补码

  int b = a << 1;//把a向左移动一位
  //11111111111111111111111111111000 - b中存储的补码
  //11111111111111111111111111110111 - b的反码
  //10000000000000000000000000001000 - b的原码
  //-8
  printf("a=%d b=%d\n", a, b);

  return 0;
}

拓展:左移是把2进制向高位移动,每移动1位有扩大2倍的效果

(2)右移操作符

移位规则:

  1. 逻辑移位 左边用0填充,右边丢弃
  2. 算术移位 左边用原该值的符号位填充,右边丢弃

在C语言中,C语言标准并没有规定有符号数应该使用那种类型的右移,而是取决于编译器。但是绝大多数编译器都采用算术右移。对于无符号数,自然采用逻辑右移。

📝例如:同样以4和-4为例

#include<stdio.h>
int main()
{
  int a = 4;
  //00000000000000000000000000000100 补码
  int b = a >> 1;
  //00000000000000000000000000000010 补码
  printf("a=%d b=%d\n", a, b);
  return 0;
}
#include<stdio.h>
int main()
{
  int a = -4;
  //10000000000000000000000000000100 - -4的原码
  //11111111111111111111111111111011 - -4的反码
  //11111111111111111111111111111100 - -4的补码

  int b = a >> 1;
  //11111111111111111111111111111100
  //11111111111111111111111111111110 - b在内存中的补码
  //11111111111111111111111111111101 - b的反码
  //10000000000000000000000000000010 - b的原码
  //-2
  printf("a=%d b=%d\n", a, b);

  return 0;
}

拓展:右移是把2进制向低位移动,每移动1位有缩小2倍的效果

🍑3、位操作符

& 按位与

| 按位或

^ 按位异或

~取反运算符

注:他们的操作数必须是整数。

(1) & 按位与操作符

规则: 有0则为0,两个同时为1才为1

(2) | 按位或操作符

规则: 有1则为1,两个同时为0才为0

(3) ^ 按位异或操作符

规则: 相同为0,相异为1

(4) ~ 按位取反操作符

规则: 0为1,1为0

📝一道变态的面试题:

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

方法一:

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

  a = a + b;//将(a+b)的和存到a中
  b = a - b;//a的值为(a+b)的和-b,由于a=a+b即b=(a=a-b)
  a = a - b;//b的值为(a+b)的和-a,由于a=a+b且b=a,即a=(b=a-b)
  printf("a=%d b=%d\n", a, b);
  return 0;
}

缺陷:两个整数加和可能越界

方法二:

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

这道题在交换变量内容时一反常态,解题思路比较绕,建议大家反复观看,重在理解。

🍑4、sizeof详解

(1) sizeof+变量或类型

sizeof操作符的作用是:返回操作数的类型长度(以字节为单位)

注意:sizeof的操作数是变量名时可以不带括号,如果是类型名必须带括号

int main()
{
  int a = 10;
  int* p;
  int arr[10];
    //%zu专门打印sizeof的返回值
  printf("%zu\n", sizeof(a));//int  4
  printf("%zu\n", sizeof a);//int  4
  printf("%zu\n", sizeof(int));//int  4
  //printf("%zu\n", sizeof int);//err
  
  printf("%zu\n", sizeof(p));//int* 4
  printf("%zu\n", sizeof(arr));//(int [10])40去掉数组名剩下的就是数组类型 
  printf("%zu\n", sizeof(arr[10]));//int 4

  return 0;
}

(2) sizeof和数组

#include <stdio.h>
void test1(int arr[])
{
  printf("%d\n", sizeof(arr));
}
void test2(char ch[])
{
  printf("%d\n", sizeof(ch));
}
int main()
{
  int arr[10] = { 0 };
  char ch[10] = { 0 };
  printf("%d\n", sizeof(arr));//sizeof 数组名,计算的是整个数组的大小//40
  printf("%d\n", sizeof(ch)); //10
  test1(arr);//数组传参传递的是首元素的地址//8或4(取决于32位还是64位)
  test2(ch);//数组传参传递的是首元素的地址 //8或4(取决于32位还是64位)
  return 0;
}

(3) sizeof和表达式

注意:sizeof里面的表达式不参与计算

#include<stdio.h>
int main()
{
  short s = 10;
  int a = 2;
  s = a + 5;
  //sizeof返回的是类型的大小,而表达式s=a+5返回的是s的类型。即short类型
  //sizeof里面的表达式不参与计算
  printf("%d\n", sizeof(s = a + 5));//2
  printf("%d\n", s);//7

  return 0;
}
//输出:2,7

🍑5、逻辑操作符

&& 逻辑与

|| 逻辑或

! 逻辑非

(1) 操作符&&与||

&&与||使用和理解还是比较简单的,比如判断闰年:

#include<stdio.h>
int main()
{
  int year = 2000;
  if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
  {
    printf("是闰年");
  }
  return 0;
}

补充:&&与||在表达式计算时是会控制求值顺序的。例如下面一道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; }
//程序输出的结果是什么?

当i=a++&&++b&&d++时,由于a=0即为假,对于&&操作符,一旦有一个表达式为假,后面的表达式就不在计算,判定整体为假。所以只计算a++,求得:a=1,b=2,c=3,d=4


当i=a++||++b||d++时,a=0为假,++b=3为真,所以a++||++b整体为真,对于||操作符,有真则为真,所以表达式d++不在计算,求得:a=1,b=3,c=3,d=4

(2) 逻辑非:

“逻辑非”就是指本来值的反值。

例如:" !0" 这个逻辑表达式的值为1.

" !1" 这个逻辑表达式的值为0.

🍑6、逗号表达式

exp1, exp2, exp3, …expN

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

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

📝例如:

//代码1
int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1);//逗号表达式
//可求得:(a>b)==0,a=12,a=12,b=13,即c=13

//代码2
//用于判断
if (a =b + 1, c=a / 2, d > 0)

逗号表达式的其它用法:

逗号表达式在一定情境下还可以简化代码。

     ...
count_val(a);
while (a > 0) 
{
  //业务处理
  a = get_val();
  count_val(a);
}
//如果使用逗号表达式,改写:
while (a = get_val(), count_val(a), a > 0) {
  //业务处理
}
//是不是简洁多了?
     ...

总结

本章重点介绍了原、反、补码,移位操作符、位操作符、sizeof、逻辑操作符以及逗号表达式。最后希望看完本文能够对您有所帮助,如有疑问欢迎随时交流!铁汁们,我们下期再见!

下期预告:表达式求值——整形提升与算数转换


相关文章
|
4月前
|
C语言
C语言操作符(补充+面试)
C语言操作符(补充+面试)
48 6
|
7月前
|
存储 编译器 程序员
C语言第十六弹---操作符(下)
C语言第十六弹---操作符(下)
|
存储 算法 编译器
【面试题精讲】常量折叠
【面试题精讲】常量折叠
|
7月前
|
存储 C语言 C++
C语言第十五弹---操作符(上)
C语言第十五弹---操作符(上)
|
Java 编译器
【JavaSE专栏8】运算符、表达式和语句
【JavaSE专栏8】运算符、表达式和语句
109 0
|
7月前
|
存储 C语言
【C语言初阶】什么操作符你还没搞懂?试试这篇文章让你彻底理解各种操作符!
【C语言初阶】什么操作符你还没搞懂?试试这篇文章让你彻底理解各种操作符!
69 1
|
7月前
|
存储 编译器 C语言
|
编译器 C语言
《C和指针》读书笔记(第五章 操作符和表达式)
《C和指针》读书笔记(第五章 操作符和表达式)
|
存储 编译器 C语言
初阶C语言 第四章-------《操作符》 (逻辑操作符,算术操作符,逗号表达式,三目操作符)知识点+基本练习题+深入细节+通俗易懂+完整思维导图+建议收藏
初阶C语言 第四章-------《操作符》 (逻辑操作符,算术操作符,逗号表达式,三目操作符)知识点+基本练习题+深入细节+通俗易懂+完整思维导图+建议收藏
|
算法 编译器 C语言
你是真的“C”——详细剖析操作符知识点【上篇】
详解C语言中操作符模块相关知识点
120 0