C语言操作符详解(上)

简介: C语言操作符详解(上)

前言

一、算术操作符 + - * / %(运算符)

除法注意点:/ 除法 得到的是商

1.除法操作符的两个操作数都是整数的话,执行的是整数除法.用%d打印

2.除法操作符的两个操作数只要有一个浮点点数,执行的是小数除法.用%lf打印

% 取模(取余) 得到的是余数:

1.取模操作符的两个操作数必须是整数,返回的是整除之后的余数.

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

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main() {
  //printf("%lf\n", 10 / 3);   //不能这么写,因为两个操作数都是整数,应该用%d
  printf("%lf\n", 10 / 3.0);
  printf("%d\n", 10 / 3);
}

二、移位操作符(操作数只能是整数)

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

左移操作符: 左边丢弃,右边补0

数值的表示形式:

2进制,8进制,10进制,16进制

10进制的数据中,都是0-9的数字组成的

2进制的数据中,0-1的数字组成的

8进制的数据中,0-7的数字组成的

16进制的每一位是:

0~15

0 1 2 3 4 5 6 7 8 9 a b c d e f

比如:

表示数值10

1010----2进制

12-------8进制

10-------10进制

a---------16进制

数据的二进制表示:

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

原码:

把一个数按照正负直接翻译成二进制就是原码

最高位表示符号位,0表示正数,1表示负数

正整数的原码反码补码相同,

负整数的原码反码补码是要计算的

反码:

原码的符号位不变,其他位按位取反就是反码

补码:

反码+1

举例说明:

5,-5是整数,整数是存放在整型变量中的

一个整型变量是4个字节~32比特位

5:

原码 00000000000000000000000000000101

反码 00000000000000000000000000000101

补码 00000000000000000000000000000101

-5:

原码10000000000000000000000000000101

反码 11111111 11111111 11111111 11111010

补码 11111111 11111111 11111111 11111011

整数在内存中存储的是:二进制补码

4个比特位可以作为一个16进制数

例如:-5

补码 11111111 11111111 11111111 11111011

4个一组表示成16进制:

FF FF FF FB

正整数左移操作符举例说明:

3的二进制位:

00000000 00000000 00000000 00000011

向左移动一位,左边丢弃,右边补0

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main() {
  int a = 3;
  //3的二进制位:(正数的原码反码补码相同)
  //00000000 00000000 00000000 00000011
  int b=a << 1;
  //向左移动一位,左边丢弃,右边补0
  //b是正数,原码反码补码相同
  //00000000 00000000 00000000 00000110  
  //b也就是6
  printf("%d\n", b);
  printf("%d\n", a);
  return 0;
}

负整数左移操作符举例说明:

原码------>补码:取反+1

补码------>原码:-1,取反 或者 取反+1

-3的二进制位左移一位:

由于整数在内存中存储的是补码,左移的时候实际上是将补码左移一位,右边补0.

首先求出-3的补码,左移补0之后得到的是结果的补码,还需要转换成原码,最后通过原码得出结果是多少

int main() {
  int a =-3;
  /*-3的二进制位:
  10000000 00000000 00000000 00000011   -3的原码
  11111111 11111111 11111111 11111100   -3的反码
  11111111 11111111 11111111 11111101   -3的补码*/
  // 
  int b=a << 1;
  //-3的补码向左移动一位,左边丢弃,右边补0得到结果的补码
  //11111111 11111111 11111111 11111010   b的补码      
  //11111111 11111111 11111111 11111001   b的反码
  //10000000 00000000 00000000 00000110   b的原码 -6
  printf("%d\n", b);
  printf("%d\n", a);
  return 0;
}

右移操作符:

1.>> 算术右移:

右边丢弃,左边用原来的符号来填充

2.>> 逻辑右移:

右边丢弃,左边直接用0来填充

右移的时候,到底采用的是算术右移还是逻辑右移,是取决于编译器的!!

由下面的代码可以看出,vs2019编译器采用的是算术右移.

int main() {
  int a = -5;
  int b = a >> 1;
  //10000000 00000000 00000000 00000101
  //11111111 11111111 11111111 11111010
  //11111111 11111111 11111111 11111011
  printf("b=%d\n", b);
  //算术右移
  //11111111 11111111 11111111 11111101
  //10000000 00000000 00000000 00000010
  //10000000 00000000 00000000 00000011     -3
  printf("a=%d\n", a);
  return 0;
}

三、位操作符(操作数必须是整数)

位:指的是二进制位

int main() {
  int a = 3;
  int b = -5;
  int c = a & b;
  //00000000 00000000 00000000 00000011  3的补码
  //10000000 00000000 00000000 00000101  -5的原码
  //11111111 11111111 11111111 11111010  -5的反码
  //11111111 11111111 11111111 11111011  -5的补码
  //00000000 00000000 00000000 00000011  3的补码
  //11111111 11111111 11111111 11111011  -5的补码
  //00000000 00000000 00000000 00000011   a&b的补码,由于是正整数,原码也是这个
  printf("%d\n", c);  //3
  int d = a | b;  //按位或
  //00000000 00000000 00000000 00000011  3的补码
  //11111111 11111111 11111111 11111011  -5的补码
  //11111111 11111111 11111111 11111011  a|b的补码,可以发现与-5的补码一样,所以他的原码数值等于-5
  printf("%d\n", d);
  int e = a ^ b;  //按位异或--对应的二进制位相同为0,相异为1
  //00000000 00000000 00000000 00000011  3的补码
  //11111111 11111111 11111111 11111011  -5的补码
  //11111111 11111111 11111111 11111000  a^b的补码
  //11111111 11111111 11111111 11110111  补码-1
  //00000000 00000000 00000000 00001000  取反得到原码,结果为-8
  printf("%d\n", e);
}

交换a和b的值:

方法一:创建临时变量

int main() {
  int a = 3;
  int b = 5;
  printf("交换前:a=%d b=%d\n", a, b);
  int tmp = 0;
  tmp = 0;
  tmp = a;
  a = b;
  b = tmp;
  printf("交换后:a=%d b=%d\n", a, b);
}

方法二:不创建临时变量

存在潜在风险,当数字过大时,加和之后可能超出整数的最大范围,有一定局限性

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;
}

方法三:使用异或操作符,不创建临时变量

注意两个公式:
a^a=0
0^a=a

3^ 3^5=5

011 3

011 3

000 3^3

101 5

101 3^ 3^5结果5

353=5

异或支持交换律的

异或只能用于整数操作

异或只会产生0和1,不会进位出现2,所以永远不会溢出

int main() {
  int a = 3;
  int b = 5;
  printf("交换前:a=%d b=%d\n", a, b);
  a = a ^ b;  
  b = a ^ b;      //b=a^b^b=a^0=a
  a = a ^ b;      //a=a^a^b=0^b=b   
  printf("交换后:a=%d b=%d\n", a, b);
}

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

思路点拨:

int main() {
  int a = 11;
  a & 1;
  //一个整数存储在内存中的二进制中1的个数
  //只要每次右移,每次和1按位与得到最后一位看一下是不是1
  a = a >> 1;
  //00000000 00000000 00000000 00001011
  //00000000 00000000 00000000 00000001
  //发现:最低位和1按位与,还是原来的数字
  //如果想要得到这个数的最低位是几,只要和1按位与就可以了
  //结果
  //00000000 00000000 00000000 00000001
}

四、赋值操作符

=

复合操作符

int main() {
  int a = 3;
  a = a + 3;
  a += 3;
  a = a ^ 5;
  a ^= 5;
  a = a >> 3;
  a >>= 3;
}

区分赋值和初始化:

int main() {
  int a = 3;//创建并初始化
  a = 5;//赋值
  return 0;
}

六、单目操作符

!的应用场景:

int main() {
  int flag = 5;
  if (flag) {  //flag为真做什么
  }
  if (!flag) {   //flag为假做什么
  }
  return 0;
}

&取地址操作符

int main() {
  int a = 10;
  &a;//&取地址操作符
  int arr[10];
  &arr;//取出数组的地址
}

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

*

int main() {
  int a = 10;
  int* p=&a;  //取地址操作符  p里面存的是a的地址
  *p = 20;   //解引用操作符(间接访问操作符)
  int arr[10];
  &arr;   //取出数组的地址
  return 0;
}

sizeof

int main() {
  int a = 10;
  printf("%d\n", sizeof(a));
  printf("%d\n", sizeof a);   //变量求长度时的小括号可以不写,但是int必须写
  printf("%d\n", sizeof(int));
  int arr[10] = { 0 };
  printf("%d\n", sizeof(arr));     //数组的长度
  printf("%d\n", sizeof(arr[0]));   //数组元素的长度
  int sz = sizeof(arr) / sizeof(arr[0]);    //数组元素的个数
  printf("%d\n", sz);
  return 0;
}

~的用法

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

int main() {
  int a = 0;
  //00000000 00000000 00000000 00000000
  //~a:按位取反
  //11111111 11111111 11111111 11111111 补码(内存里面的字都是补码)
  //11111111 11111111 11111111 11111110 反码
  //00000000 00000000 00000000 00000001  原码  -1
  printf("%d\n", ~a);   //-1
}

下面举例灵活应用单目操作符:

int main() {
  int b = 3;
  //00000000 00000000 00000000 00000011     b
  //如何把上面的数字倒数第四位变成1?
  //00000000 00000000 00000000 00000011      b
  //用b|
  //00000000 00000000 00000000 00001000     (1 << 3)
  b |= (1 << 3);
  //00000000 00000000 00000000 00001011     b=11
  printf("%d\n", b);  //11
  //如何将下面的数字
  //00000000 00000000 00000000 00001011
  //倒数第四位还原成0??
  //
  // 0&1=0 1&1=1
  // 所以将原数字与下面的数字按位与,就可以将倒数第四位还原成0
  //11111111 11111111 11111111 11110111  这个数字怎么表示呢?
  //我们发现它刚好是00000000 00000000 00000000 00001000     (1 << 3)取反可以得到
  //
  b &= (~(1 << 3));
  printf("%d\n", b);  //3
  return 0;
}

小知识点:

scanf读取失败的时候返回-1

原码

10000000 00000000 00000000 00000001

反码

11111111 11111111 11111111 11111110

补码

11111111 11111111 11111111 11111111

~按位取反之后:全0

00000000 00000000 00000000 00000000

此时就变成:

while(0)不进入循环,scanf读取失败

前置++ --:在使用之前++ - -
后置++ --:在使用之后++ - -

int main() {
  int a = 10;
  int b = a++;
  printf("%d\n", a);    
  printf("%d\n", b);  //后置++,先使用,再++
  //相当于int b=a;a=a+1;
}

后置++

int main() {
  int a = 10;
  printf("%d\n", a++);//先使用,后++
  printf("%d\n", a);
}

前置++

int main() {
  int a = 10;
  printf("%d\n", ++a);  //先++,再使用
  printf("%d\n", a);
}

前置- -和后置- -同上面一个道理

(类型) 强制类型转换

仅仅是临时改变数据类型的一个状态

int main() {
  float a = 3.14f;
  int b = (int)a;  //强制性类型转换
  return 0;
}

sizeof()和数组

1.sizeof()内部单独放一个数组名,表示整个数组,所以(1)(3)处的值为40,10

2.注意:数组在传参的时候,实际上是传过去首元素的地址

void test1(int arr[])  //整形的地址放到整形的指针里面,int本质是int*,存放地址,指针变量的大小都是四个字节或者八个字节
{
  printf("%d\n", sizeof(arr));//(2)X86环境下指针的大小是4,X64环境下指针的大小是8
}
void test2(char ch[])   //char本质是char*,存放地址
{
  printf("%d\n", sizeof(ch));//(4)X86环境下指针的大小是4,X64环境下指针的大小是8
}
int main()
{
  int arr[10] = { 0 };
  char ch[10] = { 0 };
  printf("%d\n", sizeof(arr));//(1)  计算的是整个数组的大小:40
  printf("%d\n", sizeof(ch));//(3)   计算char类型的数组大小:10
  test1(arr);
  test2(ch);
  return 0;
}

七、关系操作符

在编程中注意==和=不要使用错误

八、逻辑操作符

&& 逻辑与 并且的意思 只关注真假
| | 逻辑或 或者的意思 只关注真假

& 按位与 二进制位进行计算

| 按位或 二进制位进行计算

int main() {
  int a = 3 && 5;
  int b = 3 && 0;
  printf("%d\n", a);
  printf("%d\n", b);
  int c = 1 || 0;
  int d = 0 || 0;
  printf("%d\n", c);
  printf("%d\n", d);
  return 0;
}

&& 操作符左边为假,右边不再计算
| | 操作符左边为真,右边不再计算

举例说明:

int main() {
  int i = 0, a = 0, b = 2, c = 3, d = 4;
  i = a++ && ++b && d++;  //0&&...a=0,后面不用计算
  printf("a=%d\nb=%d\nc=%d\nd=%d\n", a, b, c, d);
  return 0;
}

int main() {
  int i = 0, a = 1, b = 2, c = 3, d = 4;
  i = a++ && ++b && d++;  //1&&3&&4
  printf("a=%d\nb=%d\nc=%d\nd=%d\n", a, b, c, d);//2 3 3 5
  return 0;
}

int main() {
  int i = 0, a = 1, b = 2, c = 3, d = 4;
  i = a++ || ++b || d++;  //1||...或运算,左边有一个1,右边不用计算
  printf("a=%d\nb=%d\nc=%d\nd=%d\n", a, b, c, d);//2 2 3 4
  return 0;
}

int main() {
  int i = 0, a = 0, b = 2, c = 3, d = 4;
  i = a++ || ++b || d++;   //0||2...右边不用计算
  printf("a=%d\nb=%d\nc=%d\nd=%d\n", a, b, c, d);//1 3 3 4
  return 0;
}


这篇文章的内容你是否有所收获呢?小伙伴们,记得点赞收藏博客,关注后续的C语言操作符(下)内容哦~😉😉💕

相关文章
|
3月前
|
C语言
C语言操作符逻辑与,逻辑或面试真题(2)
C语言操作符逻辑与,逻辑或面试真题(2)
|
3月前
|
程序员 C语言 C++
C语言操作符if语句好习惯 详解分析操作符(详解4)
C语言操作符if语句好习惯 详解分析操作符(详解4)
|
3月前
|
C语言
操作符详解(3)C语言复合赋值操作符单目操作符
操作符详解(3)C语言复合赋值操作符单目操作符
|
3月前
|
C语言
C语言操作符详解(3)初始化和赋值操作符h
C语言操作符详解(3)初始化和赋值操作符h
|
3月前
|
C语言
C语言的详解操作符(2)之取余操作符
C语言的详解操作符(2)之取余操作符
|
3月前
|
存储 编译器 C语言
『C语言初阶』第六章-操作符详解
『C语言初阶』第六章-操作符详解
|
2月前
|
存储 小程序 编译器
learn_C_deep_10 深刻认识C语言重点操作符(上)
learn_C_deep_10 深刻认识C语言重点操作符
|
4天前
|
存储 编译器 C语言
C语言之操作符详解篇
C语言之操作符详解篇
41 1
|
9天前
|
存储 编译器 C语言
C语言:操作符详解
C语言:操作符详解
|
2月前
|
存储 编译器 人机交互
learn_C_deep_10 深刻认识C语言重点操作符(下)
learn_C_deep_10 深刻认识C语言重点操作符

相关产品

  • 云迁移中心