欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C语言15-枚举、位运算

程序员文章站 2022-05-12 08:42:24
...

枚举

在编程的问题中,往往需要用一个变量,来保存某种“状态”,比如之前通过状态机实现语法高亮。
再比如,贪吃蛇中,需要一个变量,来保存他的前进方向。

#define DIR_UP 0
#define DIR_DOWN 1
#define DIR_LEFT 2
#define DIR_RIGHT 3
int nDirection = DIR_UP;

这样做在语法上没有问题,但是在编程逻辑过程中,可能埋下隐患,因为int表示的范围,比我们约定的方向种类,要大的多
比如说,万一有程序员,无意中写了一下的语句进行赋值:

nDirection = 0;//语法正确,逻辑正确,表示向上
nDirection = 5;//语法正确,逻辑不正确,因为5没有被约定

C语言中,为了解决这种“在限定范围内赋值”的问题,发明了枚举变量类型。

枚举的语法

定义枚举类型:

enum ENUM_DIRECTION/*类型名*/ 
{ 
    //状态范围 
    UP,
    DOWN, 
    LEFT, 
    RIGHT 
};

用枚举类型定义变量:

int main(int argc, char* argv[])
{
    enum ENUM_DIRECTION eSnakedir;
    eSnakedir=DOWN;
    return 0;
}

枚举的原理

总之,枚举变量,其实就是在编译时会做范围检查的int类型的变量

enum ENUM_DIRECTION/*类型名*/ 
{ 
    //状态范围 
    UP,
    DOWN, 
    LEFT, 
    RIGHT 
};

int main(int argc, char* argv[])
{
    enum ENUM_DIRECTION eSnakedir;
    eSnakedir=LEFT;
    printf("%d, %d", sizeof(eSnakedir), eSnakedir);
    return 0;

以上的结果是4, 2
4意味着枚举变量的大小是4字节.
经过测试知道

enum ENUM_DIRECTION/*类型名*/ 
{ 
    //状态范围 
    UP, //0
    DOWN,//1 
    LEFT, //2
    RIGHT //3
};

我们还可以自己规定枚举对应数字的起点

enum ENUM_DIRECTION/*类型名*/ 
{ 
    //状态范围 
    UP=2,
    DOWN, 
    LEFT, 
    RIGHT 
};

枚举只是编译时检查,运行时并不做检查
一下为强制通过指针来改变eSnakedir的值

*(int *)&eSnakedir=5;

位运算

语法

C语言中位运算有:

&
|
^
~

前三种是二元运算符,最后一种是一元运算符。
位运算是指按一个变量中的每一个bit进行运算,用表来总结
C语言15-枚举、位运算
实际举例

#define NEGATIVE_ONE (~0)//此处用宏定义了一个-1
~(~char)0x03=0xFC
0x23 ^ 0x11 = 0x32
0x23 & 0x11 = 0x01
0x23 | 0x11 = 0x33

位运算的语法非常简单,掌握怎么运算即可。
对他应用场景的理解,比掌握语法更重要。

应用

标志约定
我们经常用到标志,比如找对象(女找男):可能我们会对男人的属性进行归类,这些归类的属性,我们可以使用“有”或者“没有”来记录。

/* 秃头与否
抽烟与否
有车与否
有房与否
有否腹肌
近视与否
**与否
诚实与否 */ 
char chIsBare;
char chSmoke; 
char chIsCar; 
char chIsHouse; 
char chIsMuscle; 
char chIsGlasses; 
char chIsGamble; 
char chIsHoneset;

可能会使用多个变量为标志,存储这些特征:

//渣渣
 chIsBare = 1; 
 chIsSmoke = 1; 
 chIsCar= 0; 
 chIsHouse = 0; 
 chIsMuscle = 0; 
 chIsGlasses = 1; 
 chIsGamble = 1; 
 chIsHonest = 0;

以上代码,就定义了一个“人”。但是,从存储角度看,这浪费了内存空间
因为每个标志(与否),只需要一个bit。所以8个标志,严格来说需要8个bit(一个字节)。
所以,可以从位运算的角度去改进:
即,使用一个char8个bit一个字节,来表示不同标志:

//自上往下,分别是第0~7bit位
char chIsBare;
char chSmoke; 
char chIsCar; 
char chIsHouse; 
char chIsMuscle; 
char chIsGlasses; 
char chIsGamble; 
char chIsHoneset;

这样,特征的二进制就变成了:

0110 0011=>0x63

以上的技巧可以说,没有技巧,只是约定。
光有约定,并不利于编程。
比如现在,我想要一个不抽烟,有钱,其他清空和0x63一样的人。
但是,有了位运算,我们就有技巧。
首先,我们可以用不同的数字,表示不同bit的有效位。

#define FLAG_ISBARE 0x01
#define FLAG_ISSOMKE 0x02 
#define FLAG_ISCAR 0x04 
#define FLAG_ISHOUSE 0x08 
#define FLAG_ISMUSCLE 0x10 
#define FLAG_ISGLASSES 0x20 
#define FLAG_ISGAMBLE 0x40 
#define FLAG_ISHONSET 0x80

接着,不同的位运算,就发挥了不同的功能。
|运算符的“打开”标志功能

char chOldOne = ...; 
chOldOne = chOldOne | FLAG_ISBARE;

**&运算符的“关闭”标志功能

char chOldOne = ...; 
chOldOne = chOldOne &(~FLAG_ISBARE);

**^运算符的“切换”标志功能

char chOldOne = ...; 
chOldOne = chOldOne ^FLAG_ISCAR 
/* 秃头与否
抽烟与否
有车与否
有房与否
有否腹肌
近视与否
**与否
诚实与否 */
#define FLAG_ISBARE 0x01
#define FLAG_ISSOMKE 0x02 
#define FLAG_ISCAR 0x04 
#define FLAG_ISHOUSE 0x08 
#define FLAG_ISMUSCLE 0x10 
#define FLAG_ISGLASSES 0x20 
#define FLAG_ISGAMBLE 0x40 
#define FLAG_ISHONSET 0x80

int main (int argc, char* argv[])
{
char chPerfectOne=0 |
(FLAG_ISCAR)
| (FLAG_ISHOUSE)
| (FLAG_ISMUSCLE)
printf("%02X", chPerfectOne);

return 0;
}

利用位运算进行无中间变量交换
普通交换:

int nValue1 = 0x1111111;
int nValue2 = 0x222222;

//交换
int nTemp = nValue1;
nValue1= nValue2;
nValue 2=temp;

使用异或,可以省掉中间变量:

int main (int argc, char* argv[])
{
int nValue1=0x1;
int nValue 2=0x2;
printf("%p, %p\r\n", nValue1, nValue2);

nValue1=nValue1^nValue2;
nValue2=nValue1^nValue2;//nValue2变成原来的nValue1;
nValue1=nValue^nValue2;

printf("%p, %p\r\n", nValue1, nValue2);
return 0;
}

通过以下算法进行类比:

nValue1 = nVaulue1 + nValue2; 
nValue2 =nValue1 ‐ nValue2; 
nValue1 = nValue1 ‐ nValue2 ;

异或在加密解密中的应用
什么是加密算法
加密算法与编码算法最本质的区别在于:加密算法需要“**”(key)。
异或天生适用于加密和解密:

  • nValue ^ key //变为密文
  • 密文^key//变为原文
void EncryptBlock(char* pData, int nLen, char* pKey, int nKeyLen)
{
    for (size_t i = 0; i < nLen; i++)
    {
        pData[i] ^= pKey[i%nKeyLen];
    }
}

int main(int argc, char* argv[])
{    
    char szData[] = "helloworld";
    EncryptBlock(szData, strlen(szData),
    "hello", strlen("hello"));
    return 0;
}

非常多得加密解密算法中,使用了疑惑运算(比如AES、MDS、SHA256、CRC)

相关标签: C语言入门开发