C语言——操作符 (超详细归纳!!!)(中)

简介: C语言——操作符 (超详细归纳!!!)(中)

练习1:不用临时变量,交换a,b的值

不用临时变量,交换a,b的值


假设我们给定两个数,a = 3, b = 5;

要实现两个数的交换,我们会想到醋和酱油交换的生活案例,我们会先找一个空瓶子,先将醋或者酱油倒到空瓶子中,(假设将醋先倒入空瓶子中)然后我们会将酱油倒入原醋瓶中,将原空瓶中的醋倒入酱油瓶中。

方法①根据这种方式,我们会先创建一个临时变量来用于两者的数值交换

#include <stdio.h>
int main()
{
  int a = 3;
  int b = 5;
  int temp = 0;
  printf("before:a = %d,b = %d\n", a, b);
  temp = a;
  a = b;
  b = temp;
  printf("after:a = %d,b = %d", a, b);
  return 0;
}

但是我们题目中明确要求不能创建变量,所以这种方式明显不合适。

这时候我们考虑另外一种方式,②从数学的角度出发:如果我先将 a与b之和放到a到当中 然后再用两者之和减去b不就得到a了吗,并把这个值赋给b,实现了a的值转移给b 。同理,用两者之和减去刚刚赋予a值到b中的b,就能得到原b的值(听起来有点绕是不,简单来说:就是 值:a + b - a = b = > 变量a

//方法一:加减法
#include<stdio.h>
//不创建临时变量实现两个数的交换
int main()
{
  int a = 3;
  int b = 5;
  printf("before:a = %d,b = %d\n", a, b);
  a = a + b;
  b = a - b;//将a的值给b
  a = a - b;//将b的值给a
  printf("after:a = %d,b = %d", a, b);
  return 0;
}

image.png

虽然上面说的这种方式实现了不用创建临时变量就交换两个数值,但是仍然存在一定的缺陷,比如我们的变量a, b并不等于3和5,而是一个很大或者离int边界阈值很接近的值,如果我们用两者相加的方法,a + b很有可能就超出int类型的边界阈值,得到一个并非我们想要的结果,再拿着这个结果去减去a或者b中一个数,并不能得到原本的另一个数的正确值。也就是存在“可能溢出”的缺陷。

③既然这种加减法因为阈值的问题存在缺陷,我们就要去考虑一种既能够成功交换两个数的值,还不会存在阈值的缺陷,更深层次的思考,数值的存储方式-- - 二进制,二进制能实现两者交换……a thousand years later—>异或操作符 ^ 这种位操作符不会产生进位的情况,所以不存在溢出的可能。


////

//异或办法
#include<stdio.h>
//不创建临时变量实现两个数的交换
int main()
{
  int a = 3;
  int b = 5;
  printf("before:a = %d,b = %d\n", a, b);
  a = a ^ b;
  b = a ^ b;
  a = a ^ b;
  printf("after:a = %d,b = %d", a, b);
  return 0;
}

image.png


练习2:求一个整数存储在内存中的二进制中1的个数

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


方法①:看到这个问题,我们可以想到利用整数求其二进制原码,只要在求的过程中判断余数是否为1,是就计数 + 1,根据这种想法,编写我的代码可以得到:

#include<stdio.h>
int main()
{
  int a = 0;
  int count = 0;
  scanf("%d", &a);
  while (a)
  {
    if ((a % 2) == 1)
      count++;
        a = a / 2;
  }
  printf("count = %d\n", count);
  return 0;
}

image.png


这种方式看似正确,但是当我们输入负数的时候,比如 - 1, - 1补码为11111111 11111111 11111111 11111111 正确答案应该为:32 ,而我们程序运行却得到0。

(实际上这种方式还有补救方法,只要将 负数看作无符号位的整型,将a定义从 int a = 0; 成更改成unsigned int a - 1就会被当作一个超级大的数字11111111 11111111 11111111 11111111 来处理)


image.png


方法②:既然这种方法不可行,我们就应该换一种思路,寻找新的方法,比如我能否得到输入数字存储的二进制补码,然后统计该补码中1的个数?答案是:可以,我们应该要想到任何一个数如果和 1进行按位与操作,若其最后一位数为1,则结果为1,若为0,结果为0。再借助移位操作符右移便可以统计1的个数(int类型的大小 为32位bit),因此可以编写代码为:

#include<stdio.h>
int main()
{
  int a = 0;
  int count = 0;
  int i = 0;
  scanf("%d", &a);
  for (i = 0; i < 32; i++)
  {
    if (1 == ((a >> i) & 1))
    {
      count++;
    }
  }
  printf("count = %d\n", count);
  return 0;
}

这里我们的判断条件是:

if(1 == ((a >> i) & 1))

也就是将上面的1进行左移操作,然后将两者进行按位与操作,再比较1左移操作的结果 与按位与之后的结果是否相等,若相等,代表该二进制位置 为 1

方法③:通过对输入数字的二进制操作,从最后一位1开始,每一步减少一个1

#include<stdio.h>
int main()
{
  int a = 0;
  int count = 0;
  int i = 0;
  scanf("%d", &a);
  while (a)
  {
    count++;
    a &= a - 1;
    //a = a & (a - 1);
  }
  printf("count = %d\n", count);
  return 0;
}

a - 1能够让a的最后一位变成0 这种奇妙的感觉就像是三体小说中描写的“高维世界像低维世界塌陷”

有这样的一个公式 n & (n - 1)

假设n = 13 其后四位二进制补码为 1101


1101 ---- n

1100 ---- n - 1

1100 ---- n = n & (n - 1)

1011 ---- n - 1

1000 ---- n = n & (n - 1)

0111 ---- n - 1

0000 ---- n = n & (n - 1)


我们可以观察n的变化,发现每一次进行n = n & (n - 1)后,其最后一位的1(最右边的1)都会变成0(每执行一次,最右边的1都会消失,直到变成0,停止执行),那么在n变成0之前,n = n & (n - 1)能执行多少次,就代表最初的n中二进制补码就有多少个1。


这种算法的执行次数不用每次都执行32次,有多少个1,就执行多少次,所以效率很高!

注意:位操作符在进行运算的时候,不需要考虑符号位的影响,所有位都当做需要参与运算的二进制位,

比如 - 1 ^ -1 = 0, 即符号位也正常参与位操作符运算。


4.复合赋值符

image.png


①简单赋值操作符:

= 赋值操作符(注意:一个等号 = 表示赋值,两个等号 == 表示条件判断)

通过赋值操作符,我们可以给初始变量赋值,变更调整变量的值(也就是重新给变量赋值)

例如:

int weight = 80;//单位kg 
weight = 60;//对80不满意,重新赋值成60
double salary = 10000.0;
salary = 30000.0;//对salary只有10000.0不满意,变成30000.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;

#include<stdio.h>
int main()
{
  int a = 1;
  printf("%d\n", a += 1);
  printf("%d\n", a -= 1);
  printf("%d\n", a *= 2);
  printf("%d\n", a /= 2);
  printf("%d\n", a %= 2);
  printf("%d\n", a <<= 1);
  printf("%d\n", a >>= 1);
  printf("%d\n", a &= -1);
  printf("%d\n", a |= 1);
  printf("%d\n", a ^= 1);
  return 0;
}

image.png


5.单目操作符


image.png


sizeof 详细讲解:

举例一:    

#include<stdio.h>
int main()
{
  int a = 1;//一个整形为4的字节
  char b = 'A';
  int arr[20] = { 0 };
  printf("%d\n", sizeof(a));
  printf("%d\n", sizeof a);
  printf("%d\n", sizeof(int));
  printf("%d\n", sizeof(b));
  printf("%d\n", sizeof(char));
  printf("%d\n", sizeof(arr));
  printf("%d\n", sizeof(int[20]));
  return 0;
}

image.png


举例二:

#include<stdio.h>
int main()
{
  short i = 0;
  int j = 10;
  printf("%d\n", sizeof(i = j + 5));//sizeof计算所占空间的大小
  //不管这里的j是什么类型  j+5的结果都是放到变量 i当中  而i又是short类型
  //sizeof(i=j+5)等于 sizeof(i)等于sizeof(short)等于2
  printf("%d\n", i);
  //sizeof里面放的表达式并不会真实进行运算,只是一个摆设
  //也就是说 i=j+5 这一步操作并未真实执行,所以i还是原来的值:0
  return 0;
}

image.png


注意:sizeof括号中的表达式不参与实际运算!


~ 按位取反详解:

按二进制补码取反

例如 0:00000000 00000000 00000000 00000000

~0:11111111 11111111 11111111 11111111 补码 等于 - 1

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

image.png

举例1:

#include<stdio.h>
int main()
{
  int i = 11;
  //00000000 00000000 00000000 00001011 让倒数第三位变成1,其余不变
  //00000000 00000000 00000000 00000100  按位或  就能得到
  //1 << 2 1左移2位得到 00000000 00000000 00000000 00000100
  //int j = i | 4;
  int j = i | (1 << 2);
  printf("%d\n", j);
  //将j变成原来的i  只需要按位与操作
  //00000000 00000000 00000000 00001111
  //11111111 11111111 11111111 11111011
  //00000000 00000000 00000000 00001011
//而  11111111 11111111 11111111 11111011 可以由按位取反得到
//    00000000 00000000 00000000 00000100  这个数又可以通过1左移2位得到
  j = j & (~(1 << 2));
  printf("%d\n", j);
  //int a = 0;
  //printf("%d\n", ~a);
  return 0;
}

image.png


减减-- 、 加加 ++ 详解:

#include<stdio.h>
int main()
{
  int a = 10;
  //printf("%d\n", ++a);//前置++,先++,后打印
  //printf("%d\n", a++);//后置++,先打印,后++
  //printf("%d\n", --a);//前置--,先--,后打印
  printf("%d\n", a--);后置--,先打印,后--
  printf("%d\n", a);
  return 0;
}

image.png


(类型)强制类型转换详解:

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

image.png


练习:

#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));
  printf("%d\n", sizeof(ch));
  test1(arr);
  test2(ch);
  return 0;
}

image.png

目录
相关文章
|
11月前
|
C语言
C语言中条件操作符的应用
最后,条件操作符是个超级英雄,但不是每个代码问题都需要一个超级英雄来解决。一定要在适当的时候适度的使用它,那么它将成为你的编程工具箱中的一件强力工具。
470 75
|
存储 C语言 索引
【C语言篇】操作符详解(下篇)
如果某个操作数的类型在上⾯这个列表中排名靠后,那么⾸先要转换为另外⼀个操作数的类型后执⾏运算。
232 0
|
程序员 编译器 C语言
【C语言篇】操作符详解(上篇)
这是合法表达式,不会报错,但是通常达不到想要的结果, 即不是保证变量 j 的值在 i 和 k 之间。因为关系运算符是从左到右计算,所以实际执⾏的是下⾯的表达式。
788 0
|
存储 网络协议 C语言
【C语言】位操作符详解 - 《开心消消乐》
位操作符用于在位级别上进行操作。C语言提供了一组位操作符,允许你直接操作整数类型的二进制表示。这些操作符可以有效地处理标志、掩码、位字段等低级编程任务。
670 8
|
C语言
【C语言】逻辑操作符详解 - 《真假美猴王 ! 》
C语言中有三种主要的逻辑运算符:逻辑与(`&&`)、逻辑或(`||`)和逻辑非(`!`)。这些运算符用于执行布尔逻辑运算。
1340 7
|
存储 缓存 C语言
【c语言】简单的算术操作符、输入输出函数
本文介绍了C语言中的算术操作符、赋值操作符、单目操作符以及输入输出函数 `printf` 和 `scanf` 的基本用法。算术操作符包括加、减、乘、除和求余,其中除法和求余运算有特殊规则。赋值操作符用于给变量赋值,并支持复合赋值。单目操作符包括自增自减、正负号和强制类型转换。输入输出函数 `printf` 和 `scanf` 用于格式化输入和输出,支持多种占位符和格式控制。通过示例代码详细解释了这些操作符和函数的使用方法。
527 10
|
存储 编译器 C语言
【C语言】简单介绍进制和操作符
【C语言】简单介绍进制和操作符
482 1
|
C语言
C语言操作符(补充+面试)
C语言操作符(补充+面试)
219 6
|
存储 编译器 C语言
十一:《初学C语言》— 操作符详解(上)
【8月更文挑战第12天】本篇文章讲解了二进制与非二进制的转换;原码反码和补码;移位操作符及位操作符,并附上多个教学代码及代码练习示例
219 0
十一:《初学C语言》—  操作符详解(上)
|
存储 编译器 C语言
初识C语言5——操作符详解
初识C语言5——操作符详解
430 0