【C语言】 操作符(上): -- 算数 -- 移位 -- 位操作符 -- 赋值 -- 单目 -- 关系 -- 逻辑操作符3

简介: 【C语言】 操作符(上): -- 算数 -- 移位 -- 位操作符 -- 赋值 -- 单目 -- 关系 -- 逻辑操作符3

3.3 按位异或

按位异或的规则是:对应的二进制位上相同为0,相异为1

我们来看一段代码:

#include <stdio.h>
int main()
{
  int a = 3;
  int b = -5;
  int c = a ^ b;//按位异或:对应的二进制位上相同为0,相异为1
  //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 11111000 -- ^的结果
  printf("%d\n", c);
  return 0;
}

按照这个规则我们来分析一下 c 的补码是:


11111111 11111111 11111111 11111011 -- -5的补码


00000000 00000000 00000000 00000011 -- 3的补码


11111111 11111111 11111111 11111000 -- ^的结果


这里的符号位为 1 (代表是负数),因此这需要将补码转为原码。


原码:10000000 00000000 00000000 00001000


c 的原码转化为 10 进制就是 -8。


效果展示:



3.4 练习

Q:不创建临时变量怎么交换两个数?

法一:

#include <stdio.h>
//a^a = 0; 相同为 0
//a^0 = a; 相异为 1
//3^5^3 = 5 二进制码来做
//3^5^3 = 5
//异或支持交换律
int main()
{
  int a = 3;
  int b = 5;
  a = a ^ b;//1式
  b = a ^ b;// 将1式代入此式当中,得出b = a
  a = a ^ b;// 将1式带入此式当中,得出a = b
  printf("a = %d, b = %d\n", a, b);
  return 0;
}

效果展示:

法二:

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

效果展示:


但是这种方法存在弊端,如果 a 和 b 过大 a+b 就会存在栈溢出的问题,存在缺陷。因此法一是符合要求的方法,但是只是针对不创建临时变量来讲更合理。^ 交换两个数存在 3 个弊端:


1.是 ^ 只针对整形可以交换,浮点型不可以;


2.它的可读性比较差:


3.如果可以创建临时变量,效率会高点。


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

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

我们来分析一下此代码是如何实现问题的:


1的补码与任何数与只会得到两个数:0 / 1(因为按位与的规则是:有 0 得 0 ,同为 1 得 1。1 的二进制序列除了最后一个数字是 1 之外全是 0 ,因此任何数与 1 按位与只会得到 0 / 1)。按照这个规律我们展开想,假设我们输入了一个 3 ,3 的二进制序列是00000000 00000000 00000000 00000011,我们让它与 1 的二进制序列00000000 00000000 00000000 00000001,按位与,如果得到的数是 1 ,我们就让计数器+1,然后对 3 进行右移,得到下一个二进制数再进行按位与操作,得到的是 1 就再让计数器+1,循环 32 次,这样就可以得出一个整数的二进制序列到底有多少个 1。




4、赋值操作符

赋值操作符是一个很棒的操作符,它可以让你得到一个你满意的值。也就是你可以对自己重新赋值。

初始化的 = 与赋值的 = 是不一样的,创建变量的时候的 = 是初始化,后面对变量的值进行更改用到的 = 才是赋值操作符。

int weight = 120;//体重,这里的=不是赋值操作符,这是初始化
weight = 90;//不满意就重新赋值
//赋值操作符可以连续使用,比如:
int a = 10;
int x = 0;
int y = 20;
a = x = y+1;//连续赋值,先把y+1赋给x,再把x赋给a
//这样的可读性并不好,因此下面的代码可读性会更好一点
x = y + 1;
a = x;
//这样的写法更加清晰明了,而且易于调试

4.1 复合赋值符

+=        -=        *=        /=        %=        >>=        <<=        &=        |=        ^=

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

例如:

#include <stdio.h>
int main()
{
    int a = 3;
    a = a + 3;
    a += 3;
    a = a >> 3;
    a >>= 3;
    a = a * 3;
    a *= 3;
    a = a / 3;
    a /= 3;
    return 0;
}

5、单目操作符

! 逻辑反操作

- 负值

+ 正值

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

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

-- 前置、后置--

++ 前置、后置++

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

(类型) 强制类型转换

我们这里只讲一部分:

5.1 ! -- 逻辑反操作

一般是在判断句中做判断调价的调整。

例如:

int main()
{
    int flag = 1;
    if(flag)//flag为真就进入if语句里面
    {
    }
    if(!flag)//这里就是flag为假的时候进到if语句里面去
          //(flag为假,!flag就变成了真,这样就实现了为假进入)
    {
    }
    return 0;
}

5.2 & -- 取地址操作符

一般用于指针,取某个地址。

例如:

#include <stdio.h>
int main()
{
    int a = 3;
    &a;//取出变量a的地址
    int arr[10] = { 0 };
    &arr;//取出数组arr的地址(这里是拿到整个数组的地址)
    return 0;
}

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

#include <stdio.h>
int main()
{
    int a = 3;
    int* p = &a;//p里面存放变量a的地址,*p是通过p里面存放的地址找到a,因此*p也就是a
    *p = 5;//承接上一句,这里看到的是改*p,实则通过地址改了a
    printf("%d\n", a);
    return 0;
}

效果展示:


因此&和*是一对,一般都是在一起用的。

5.4 sizeof

sizeof是计算所占内存空间大小或者类型的长度的(单位:字节)。

例如:

#include <stdio.h>
int main()
{
    int a = 3;
    printf("%d\n", sizeof(a));//我们这里打印的就是a所占空间的大小
    //a两端的()可以不写
    printf("%d\n", sizeof a);
    printf("%d\n", sizeof(int));//我们这打印a的类型是一样的,因为a是整形
    int arr[10] = { 0 };
    printf("%d\n", sizeof(arr));//sizeof可以计算数组总大小
    //引申
    printf("%d\n", sizeof(arr[0]));//还可以计算数组首元素大小
    printf("%d\n", sizeof(arr)/sizeof(arr[0]));//也可以计算数组长度
    return 0;
}

效果展示:


总结:sizeof在计算变量的时候可以省略( ),但是计算类型的时候不可以省略。

5.4.1 sizeof练习

我们看一段代码给出答案

#include <stdio.h>
void test1(int arr[])
{
    printf("%d\n", sizeof(arr));
}
void test2(char arr[])
{
    printf("%d\n", sizeof(arr));
}
int main()
{
    int arr1[10] = { 0 };
    char arr2[10] = { 0 };
    printf("%d\n", sizeof(arr1));
    printf("%d\n", sizeof(arr2));
    test1(arr1);
    test2(arr2);
    return 0;
}

我们分析一下:

sizeof(数组名)求的是整个数组的大小,因此打印的一二行分别是40,10。

数组传参传的是首元素地址,是一个指针,test1,test2函数计算的都是指针的大小,在X32的环境下就是4,X64的环境下就是8。我们来看看结果:

效果展示:


5.5 ~ -- 二进制按位取反

我们以一段代码来看看~ 操作符:

#include <stdio.h>
int main()
{
    int a = 0;
    //二进制
    //a的补码: 00000000 00000000 00000000 00000000
    //~a的补码:11111111 11111111 11111111 11111111
    //~a的反码:10000000 00000000 00000000 00000000
    //~a的原码:10000000 00000000 00000000 00000001
    //原码转化为10进制数为-1
    printf("%d\n", ~a);
    return 0;
}

分析:


a的二进制补码:00000000 00000000 00000000 00000000


~a的二进制补码:11111111 11111111 11111111 11111111


~a的二进制反码:10000000 00000000 00000000 00000000


~a的二进制原码:10000000 00000000 00000000 00000001


~a的二进制原码转化为10进制数是 -1。


效果展示:


Q:如何将3的二进制序列00000000 00000000 00000000 00000011中标记的0改为1,最后再改回来?

我们来分析一下这个问题:

我们按照此分析来写代码:

#include <stdio.h>
int main()
{
    int a = 3;
    //处理
    a |= (1 << 3);
    printf("%d\n", a);
    //回到原来的a
    a &= (~(1 << 3));
    printf("%d\n", a);
    return 0;
}

效果展示:

相关文章
|
2月前
|
C语言
C语言判断逻辑的高阶用法
在C语言中,高级的判断逻辑技巧能显著提升代码的可读性、灵活性和效率。本文介绍了六种常见方法:1) 函数指针,如回调机制;2) 逻辑运算符组合,实现复杂条件判断;3) 宏定义简化逻辑;4) 结构体与联合体组织复杂数据;5) 递归与分治法处理树形结构;6) 状态机管理状态转换。通过这些方法,可以更高效地管理和实现复杂的逻辑判断,使代码更加清晰易懂。
229 88
|
26天前
|
存储 缓存 C语言
【c语言】简单的算术操作符、输入输出函数
本文介绍了C语言中的算术操作符、赋值操作符、单目操作符以及输入输出函数 `printf` 和 `scanf` 的基本用法。算术操作符包括加、减、乘、除和求余,其中除法和求余运算有特殊规则。赋值操作符用于给变量赋值,并支持复合赋值。单目操作符包括自增自减、正负号和强制类型转换。输入输出函数 `printf` 和 `scanf` 用于格式化输入和输出,支持多种占位符和格式控制。通过示例代码详细解释了这些操作符和函数的使用方法。
34 10
|
1月前
|
存储 编译器 C语言
【C语言】简单介绍进制和操作符
【C语言】简单介绍进制和操作符
160 1
|
1月前
|
存储 编译器 C语言
初识C语言5——操作符详解
初识C语言5——操作符详解
170 0
|
1月前
|
编译器 C语言 C++
【C语言】精妙运用内存函数:深入底层逻辑的探索
【C语言】精妙运用内存函数:深入底层逻辑的探索
|
6月前
|
存储 安全 编译器
【 c 语言 】赋值操作符详解
【 c 语言 】赋值操作符详解
253 0
|
存储 算法 C语言
【C语言初阶】带你轻松玩转所有常用操作符(2) ——赋值操作符,单目操作符
【C语言初阶】带你轻松玩转所有常用操作符(2) ——赋值操作符,单目操作符
128 0
|
1月前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
33 3
|
6天前
|
C语言
c语言调用的函数的声明
被调用的函数的声明: 一个函数调用另一个函数需具备的条件: 首先被调用的函数必须是已经存在的函数,即头文件中存在或已经定义过; 如果使用库函数,一般应该在本文件开头用#include命令将调用有关库函数时在所需要用到的信息“包含”到本文件中。.h文件是头文件所用的后缀。 如果使用用户自己定义的函数,而且该函数与使用它的函数在同一个文件中,一般还应该在主调函数中对被调用的函数做声明。 如果被调用的函数定义出现在主调函数之前可以不必声明。 如果已在所有函数定义之前,在函数的外部已做了函数声明,则在各个主调函数中不必多所调用的函数在做声明
21 6