C语言实例_位操作运算符详解

举报
DS小龙哥 发表于 2024/08/07 09:56:26 2024/08/07
【摘要】 在C语言中,位操作运算符是一种强大的工具,用于直接对整数进行二进制位级别的操作。这些运算符包括按位与(&)、按位或(|)、按位异或(^)、按位取反(~)、左移(<<)和右移(>>)。位操作在底层编程、图形处理、加密解密等领域有着广泛的应用。

一、前言

在C语言中,位操作运算符是一种强大的工具,用于直接对整数进行二进制位级别的操作。这些运算符包括按位与(&)、按位或(|)、按位异或(^)、按位取反(~)、左移(<<)和右移(>>)。位操作在底层编程、图形处理、加密解密等领域有着广泛的应用。

(1)按位与(&)

按位与运算符比较两个操作数的每一位,如果两个相应的位都是1,则结果位为1;否则,结果位为0。

例如,考虑两个整数a=5(二进制为0101)和b=3(二进制为0011),a&b的结果将是1(二进制为0001)。

(2)按位或(|)

按位或运算符比较两个操作数的每一位,如果两个相应的位中至少有一个是1,则结果位为1;否则,结果位为0。

例如,对于a=5(二进制为0101)和b=3(二进制为0011),a|b的结果将是7(二进制为0111)。

(3)按位异或(^)

按位异或运算符比较两个操作数的每一位,如果两个相应的位不同,则结果位为1;否则,结果位为0。

例如,对于a=5(二进制为0101)和b=3(二进制为0011),a^b的结果将是6(二进制为0110)。

(4)按位取反(~)

按位取反运算符将操作数的每一位进行反转,1变为0,0变为1。

例如,如果a=5(二进制为0101),~a的结果将是-6(二进制为1010,考虑到C语言中整数的表示方式)。

(5)左移(<<)

左移运算符将操作数的二进制表示向左移动指定的位数,右边空出的位置用0填充。

例如,如果a=5(二进制为0101),a<<1的结果将是10(二进制为1010)。

(6)右移(>>)

右移运算符将操作数的二进制表示向右移动指定的位数,左边空出的位置用符号位填充(对于无符号数,填充0)。

例如,如果a=5(二进制为0101),a>>1的结果将是2(二进制为0010)。

位操作运算符在许多场景中都有应用,如硬件编程、数据压缩、加密算法等。理解和掌握它们能帮助你更高效地处理二进制数据和优化程序性能。

二、位运算的案例

(1)按位与(&)

#include <stdio.h>

int main() {
    int a = 5; // binary: 0101
    int b = 3; // binary: 0011
    int result = a & b; // binary: 0001, decimal: 1
    printf("Result of a & b is %d\n", result);
    return 0;
}

(2)按位或(|)

#include <stdio.h>

int main() {
    int a = 5; // binary: 0101
    int b = 3; // binary: 0011
    int result = a | b; // binary: 0111, decimal: 7
    printf("Result of a | b is %d\n", result);
    return 0;
}

(3)按位异或(^)

#include <stdio.h>

int main() {
    int a = 5; // binary: 0101
    int b = 3; // binary: 0011
    int result = a ^ b; // binary: 0110, decimal: 6
    printf("Result of a ^ b is %d\n", result);
    return 0;
}

(4)按位取反(~)

#include <stdio.h>

int main() {
    int a = 5; // binary: 0101
    int result = ~a; // binary: 1010, decimal: -6 (in two's complement)
    printf("Result of ~a is %d\n", result);
    return 0;
}

(5)左移(<<)

#include <stdio.h>

int main() {
    int a = 5; // binary: 0101
    int result = a << 1; // binary: 1010, decimal: 10
    printf("Result of a << 1 is %d\n", result);
    return 0;
}

(6)右移(>>)

#include <stdio.h>

int main() {
    int a = 5; // binary: 0101
    int result = a >> 1; // binary: 0010, decimal: 2
    printf("Result of a >> 1 is %d\n", result);
    return 0;
}

三、文件加密解密

以下是一个使用C语言和位运算来实现文件加密和解密的示例。在这个例子中,使用异或(XOR)运算符作为加密和解密的手段,因为XOR具有可逆性,即A XOR B XOR B = A。

首先,需要创建一个密钥,然后读取文件内容,对每一个字节进行XOR操作,最后将加密后的数据写入到新的文件中。同样的过程可以用于解密。

#include <stdio.h>
#include <stdlib.h>

#define KEY 0x5A // 定义密钥

void encryptFile(const char *inputFile, const char *outputFile) {
    FILE *in = fopen(inputFile, "rb");
    if (!in) {
        perror("Error opening input file");
        exit(EXIT_FAILURE);
    }

    FILE *out = fopen(outputFile, "wb");
    if (!out) {
        perror("Error opening output file");
        exit(EXIT_FAILURE);
    }

    unsigned char c;
    while ((c = fgetc(in)) != EOF) {
        fputc(c ^ KEY, out); // 加密
    }

    fclose(in);
    fclose(out);
}

void decryptFile(const char *inputFile, const char *outputFile) {
    FILE *in = fopen(inputFile, "rb");
    if (!in) {
        perror("Error opening input file");
        exit(EXIT_FAILURE);
    }

    FILE *out = fopen(outputFile, "wb");
    if (!out) {
        perror("Error opening output file");
        exit(EXIT_FAILURE);
    }

    unsigned char c;
    while ((c = fgetc(in)) != EOF) {
        fputc(c ^ KEY, out); // 解密,由于XOR的性质,加密和解密的过程是一样的
    }

    fclose(in);
    fclose(out);
}

int main() {
    encryptFile("test.txt", "encrypted.bin");
    decryptFile("encrypted.bin", "decrypted.txt");

    return 0;
}

在这个例子中,定义了一个密钥KEY,然后在encryptFile函数中,打开输入文件并读取每一个字节,然后使用XOR运算符将其与密钥进行异或操作,得到加密后的数据,再将加密后的数据写入到输出文件中。同样的,在decryptFile函数中,我们使用相同的密钥和XOR运算符对加密后的数据进行解密,得到原始的数据。

四、STM32单片机里位运算的应用

4.1 LED初始化

/*
函数功能: LED初始化
硬件连接: PA8 PD2
特性: 低电平点亮
*/
void LED_Init(void)
{
    //开时钟
    RCC->APB2ENR|=1<<2;
    RCC->APB2ENR|=1<<5;
    
    //配置GPIO口
    GPIOA->CRH&=0xFFFFFFF0;
    GPIOA->CRH|=0x00000003;
    GPIOD->CRL&=0xFFFFF0FF;
    GPIOD->CRL|=0x00000300;
    
    //上拉
    GPIOA->ODR|=1<<8;
    GPIOD->ODR|=1<<2;
}

在初始化LED的过程中,位运算符被广泛使用以精确控制寄存器的特定位。例如:

  • RCC->APB2ENR |= 1<<2;  RCC->APB2ENR |= 1<<5; 这两行代码通过使用按位或运算符(|),将APB2时钟使能寄存器的相应位设置为1,从而开启了GPIOA和GPIOD的时钟。
  • GPIOA->CRH &= 0xFFFFFFF0;  GPIOA->CRH |= 0x00000003; 这两行代码先使用按位与运算符(&)清除了GPIOA高速配置寄存器的低四位,然后再使用按位或运算符(|)设置了新的值,以配置PA8引脚的模式和速度。
  • 同样地,GPIOD->CRL &= 0xFFFFF0FF;  GPIOD->CRL |= 0x00000300; 这两行代码用于配置PD2引脚。
  • GPIOA->ODR |= 1<<8;  GPIOD->ODR |= 1<<2; 这两行代码通过使用按位或运算符(|)和左移运算符(<<),将GPIOA和GPIOD的输出数据寄存器的相应位设置为1,从而实现了上拉功能。

4.2 按键初始化

/*
函数功能:按键初始化
硬件连接:WEK_UP=PA0  按下高电平
k1=PC5 k2=PA15 按下低电平--IO口默认为低电平,需要上拉

*/
void KEY_Init(void)
{
    //开时钟
    RCC->APB2ENR|=1<<2;
    RCC->APB2ENR|=1<<4;
    
    //配置模式
    GPIOA->CRL&=0xFFFFFFF0;
    GPIOA->CRL|=0x00000008;
    
    GPIOA->CRH&=0x0FFFFFFF;
    GPIOA->CRH|=0x80000000;
        
    GPIOC->CRL&=0xFF0FFFFF;
    GPIOC->CRL|=0x00800000;
    
    GPIOC->ODR|=1<<5;
    GPIOA->ODR|=1<<15;
    
}

在按键初始化过程中,同样使用了位运算符来配置GPIO引脚:

  • RCC->APB2ENR |= 1<<2;  RCC->APB2ENR |= 1<<4; 开启了GPIOA和GPIOC的时钟。
  • 通过使用按位与运算符(&)和按位或运算符(|),修改了GPIOA和GPIOC的配置寄存器,以配置PA0、PA15和PC5引脚的模式和速度。
  • GPIOC->ODR |= 1<<5;  GPIOA->ODR |= 1<<15; 这两行代码将GPIOC和GPIOA的输出数据寄存器的相应位设置为1,实现了上拉功能。

4.3 总结

位运算符在STM32单片机的编程中起着至关重要的作用,它们能够精确地控制硬件寄存器,从而实现对LED和按键等外设的初始化和控制。通过上述代码,可以看到位运算符如何被用来开启时钟、配置引脚模式和速度以及实现上拉功能。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。