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

效果展示:

相关文章
|
20天前
|
存储 网络协议 C语言
【C语言】位操作符详解 - 《开心消消乐》
位操作符用于在位级别上进行操作。C语言提供了一组位操作符,允许你直接操作整数类型的二进制表示。这些操作符可以有效地处理标志、掩码、位字段等低级编程任务。
65 8
|
20天前
|
C语言
【C语言】逻辑操作符详解 - 《真假美猴王 ! 》
C语言中有三种主要的逻辑运算符:逻辑与(`&&`)、逻辑或(`||`)和逻辑非(`!`)。这些运算符用于执行布尔逻辑运算。
62 7
|
20天前
|
C语言 计算机视觉
【C语言】移位操作详解 - 《凌波微步 ! 》
移位操作符是C语言中非常重要的工具,提供了高效的位级操作方法。理解和正确使用移位操作符,对于编写高性能和高效能的程序至关重要。本文详细介绍了左移和右移操作符的使用方法、应用场景及注意事项,希望对您理解和使用C语言移位操作有所帮助。
82 5
|
22天前
|
存储 算法 程序员
C 语言递归算法:以简洁代码驾驭复杂逻辑
C语言递归算法简介:通过简洁的代码实现复杂的逻辑处理,递归函数自我调用解决分层问题,高效而优雅。适用于树形结构遍历、数学计算等领域。
|
24天前
|
存储 算法 C语言
用C语言开发游戏的实践过程,包括选择游戏类型、设计游戏框架、实现图形界面、游戏逻辑、调整游戏难度、添加音效音乐、性能优化、测试调试等内容
本文探讨了用C语言开发游戏的实践过程,包括选择游戏类型、设计游戏框架、实现图形界面、游戏逻辑、调整游戏难度、添加音效音乐、性能优化、测试调试等内容,旨在为开发者提供全面的指导和灵感。
40 2
|
2月前
|
存储 缓存 C语言
【c语言】简单的算术操作符、输入输出函数
本文介绍了C语言中的算术操作符、赋值操作符、单目操作符以及输入输出函数 `printf` 和 `scanf` 的基本用法。算术操作符包括加、减、乘、除和求余,其中除法和求余运算有特殊规则。赋值操作符用于给变量赋值,并支持复合赋值。单目操作符包括自增自减、正负号和强制类型转换。输入输出函数 `printf` 和 `scanf` 用于格式化输入和输出,支持多种占位符和格式控制。通过示例代码详细解释了这些操作符和函数的使用方法。
48 10
|
2月前
|
存储 编译器 C语言
初识C语言5——操作符详解
初识C语言5——操作符详解
188 0
|
20天前
|
存储 C语言 开发者
【C语言】字符串操作函数详解
这些字符串操作函数在C语言中提供了强大的功能,帮助开发者有效地处理字符串数据。通过对每个函数的详细讲解、示例代码和表格说明,可以更好地理解如何使用这些函数进行各种字符串操作。如果在实际编程中遇到特定的字符串处理需求,可以参考这些函数和示例,灵活运用。
40 10
|
20天前
|
存储 程序员 C语言
【C语言】文件操作函数详解
C语言提供了一组标准库函数来处理文件操作,这些函数定义在 `<stdio.h>` 头文件中。文件操作包括文件的打开、读写、关闭以及文件属性的查询等。以下是常用文件操作函数的详细讲解,包括函数原型、参数说明、返回值说明、示例代码和表格汇总。
42 9