C/C++(位操作)


位操作(Bit Operation)

位操作與邏輯操作

位操作不同於邏輯操作,邏輯操作是一種整體的操作,而位操作是針對內部數據位補碼的操作。邏輯操作的世界里只有真假(零與非零),而位操作的世界里按位論真假(1和0)。運算也不相同。

數據的二進制形式表示

8位二進制數據的補碼

eg:打印一個32位數據的二進制


void dis32bin(int data)
{
    int i = 32;
    while(i--)
    {
        if(data & (1<<i))
            printf("1");
        else
            printf("0");
        if(i%4 == 0)
        {
            if(i%8 == 0)
                printf(" ");
            else
                printf("-");
        }
    }
    putchar(10);
}

int main() 
{
    int a = 0xffffffff;//-1內存中的形式
    dis32bin(a);

    return 0;
}

按位&(與)

同1為1,否則為0。
非1跟1按位與保持不變,1跟1按位與為1,跟0按位與清零。
性質:用1&,在某些位保持不變的情況下,某些清零。

void dis32bin(int data)
{
    int i = 32;
    while(i--)
    {
        if(data & (1<<i))
            printf("1");
        else
            printf("0");
        if(i%4 == 0)
        {
            if(i%8 == 0)
                printf(" ");
            else
                printf("-");
        }
    }
    putchar(10);
}

int main() 
{
    //int a = 0xffffffff;//-1內存中的形式
    int a = 3;
    int b = 11;
    dis32bin(a);
    dis32bin(b);
    printf("\n------------------------------\n");
    dis32bin(a&b);
    int c = a&b;
    printf("c = %d\n",c);

    return 0;
}
/*
0000-0000 0000-0000 0000-0000 0000-0011
0000-0000 0000-0000 0000-0000 0000-1011

---------------------------------------
0000-0000 0000-0000 0000-0000 0000-0011
c = 3
*/

按位或|(或)

只有兩個都為0時才為0,其余為1.
跟1按位或置1,非0跟0或保持不變,0跟0或為0.
性質:用0|,在某些位保持不變的情況下,某些置1.

void dis32bin(int data)
{
    int i = 32;
    while(i--)
    {
        if(data & (1<<i))
            printf("1");
        else
            printf("0");
        if(i%4 == 0)
        {
            if(i%8 == 0)
                printf(" ");
            else
                printf("-");
        }
    }
    putchar(10);
}
int main() {
    int a = 3;
    int b = 9;
    dis32bin(a);
    dis32bin(b);
    printf("\n-----------------------------------------\n");
    dis32bin(a|b);
    int c = a|b;
    printf("a|b = %d\n",c);
}
/*
0000-0000 0000-0000 0000-0000 0000-0011
0000-0000 0000-0000 0000-0000 0000-1001

-----------------------------------------
0000-0000 0000-0000 0000-0000 0000-1011
a|b = 11
*/

位取反(~)

個各位反轉,1->0,0->1
按位取反,用於間接的構造某些數據。

void dis32bin(int data)
{
    int i = 32;
    while(i--)
    {
        if(data & (1<<i))
            printf("1");
        else
            printf("0");
        if(i%4 == 0)
        {
            if(i%8 == 0)
                printf(" ");
            else
                printf("-");
        }
    }
    putchar(10);
}
int main()
{
    int a = 0x55;
    dis32bin(a);
    dis32bin(~a);//a本身並未發生變化

    dis32bin(0);
    dis32bin(~0);

    return 0;
}
/*
0000-0000 0000-0000 0000-0000 0101-0101
1111-1111 1111-1111 1111-1111 1010-1010
0000-0000 0000-0000 0000-0000 0000-0000
1111-1111 1111-1111 1111-1111 1111-1111
*/

位異或(^)(相異者或)

相異者1,相同者0
跟1按位異或取反,跟0按位異或保持不變。
性質:用1^,某些位保持不變的情況下,某些取反

void dis32bin(int data)
{
    int i = 32;
    while(i--)
    {
        if(data & (1<<i))
            printf("1");
        else
            printf("0");
        if(i%4 == 0)
        {
            if(i%8 == 0)
                printf(" ");
            else
                printf("-");
        }
    }
    putchar(10);
}
int main() 
{
    int a = 0x55;
    int b = 0xff;
    dis32bin(a);
    dis32bin(b);
    
    dis32bin(a^b);
    printf("\n=============================\n");
    int c = 0;
    dis32bin(a);
    dis32bin(c);
    dis32bin(a^c);
    printf("\n=============================\n");
    int d = 0x0f;
    dis32bin(a);
    dis32bin(d);
    dis32bin(a^d);

    return 0;

}

/*
0000-0000 0000-0000 0000-0000 0101-0101
0000-0000 0000-0000 0000-0000 1111-1111
0000-0000 0000-0000 0000-0000 1010-1010

=============================
0000-0000 0000-0000 0000-0000 0101-0101
0000-0000 0000-0000 0000-0000 0000-0000
0000-0000 0000-0000 0000-0000 0101-0101

=============================
0000-0000 0000-0000 0000-0000 0101-0101
0000-0000 0000-0000 0000-0000 0000-1111
0000-0000 0000-0000 0000-0000 0101-1010
*/

左移(<<)和右移(>>)

規則:使操作數的各位左移,低位補0,高位溢出。
移位大於32位時,對32求模運算.
左移不溢出的情況下:數字左移相當於乘以2^幾次冪

void dis32bin(int data)
{
    int i = 32;
    while(i--)
    {
        if(data & (1<<i))
            printf("1");
        else
            printf("0");
        if(i%4 == 0)
        {
            if(i%8 == 0)
                printf(" ");
            else
                printf("-");
        }
    }
    putchar(10);
}
int main()
{
    int a = 0x01;
    dis32bin(a);
    dis32bin(a<<1);
    dis32bin( (a<<31)+1 << 1);
/*
0000-0000 0000-0000 0000-0000 0000-0001
0000-0000 0000-0000 0000-0000 0000-0010
0000-0000 0000-0000 0000-0000 0000-0010
*/

    dis32bin(a<<32);
    dis32bin(a<<33);
    dis32bin(a<<34);
    printf("a<<33 = %d\n",a<<32);
    printf("a<<33 = %d\n",a<<33);
    printf("a<<34 = %d\n",a<<34);
/*
0000-0000 0000-0000 0000-0000 0000-0001
0000-0000 0000-0000 0000-0000 0000-0010
0000-0000 0000-0000 0000-0000 0000-0100
a<<33 = 1
a<<33 = 2
a<<34 = 4
*/    
    return 0;
}

右移(>>)
規則:使操作數的各位右移,移除低位舍棄。
高位:

1.對無符號數和有符號中的整數補0;
2.有符號數中的負數,取決於所使用的系統;補0的稱為邏輯右移,補1的稱為算數右移
在不溢出的情況下,數字左移相當於除以2^幾次冪
同樣大於32時對32求模運算。

void dis32bin(int data)
{
    int i = 32;
    while(i--)
    {
        if(data & (1<<i))
            printf("1");
        else
            printf("0");
        if(i%4 == 0)
        {
            if(i%8 == 0)
                printf(" ");
            else
                printf("-");
        }
    }
    putchar(10);
} 
int main()
{
//第一種情況:無符號數和有符號整數,高位補0,低位舍棄
    unsigned int a = 0x55;
    dis32bin(a);
    dis32bin(a>>4);
    int b = 1;
    dis32bin(b);
    dis32bin(b>>1);
printf("\n===========================\n");
//第二種情況:有符號中的負數:高位補0邏輯右移,高位補1,算數右移。
    int c = 0x800000f0;
    dis32bin(c);
    dis32bin(c>>1);
    dis32bin(c>>2);
    dis32bin(c>>3);

    return 0;
}
/*
0000-0000 0000-0000 0000-0000 0101-0101
0000-0000 0000-0000 0000-0000 0000-0101
0000-0000 0000-0000 0000-0000 0000-0001
0000-0000 0000-0000 0000-0000 0000-0000

=======================================
1000-0000 0000-0000 0000-0000 1111-0000
1100-0000 0000-0000 0000-0000 0111-1000
1110-0000 0000-0000 0000-0000 0011-1100
1111-0000 0000-0000 0000-0000 0001-1110
*/

應用

掩碼

用一個狀態模擬8盞燈的狀態操作。
0x55 = 0101 0101,1開 0關

需求在此基礎上打開從右至左第四盞燈

void dis32bin(int data)
{
    int i = 32;
    while(i--)
    {
        if(data & (1<<i))
            printf("1");
        else
            printf("0");
        if(i%4 == 0)
        {
            if(i%8 == 0)
                printf(" ");
            else
                printf("-");
        }
    }
    putchar(10);
}

int main()
{
    //0101 0101,
    int ch = 0x55;
    int mask = 1<<3;//從本身位置開始移動
    dis32bin(ch);
    ch = ch | mask;
    dis32bin(ch);
    /*
0000-0000 0000-0000 0000-0000 0101-0101
0000-0000 0000-0000 0000-0000 0101-1101    
    */
    return 0;
}

需求2:將從左至右第五位關閉

int main()
{
// 0101 0101
// 1110 1111  求&運算即可
//~0001 0000
    int ch = 0x55;
    int mask = ~(1<<4);
    dis32bin(ch);
    dis32bin(mask);
    ch = ch & mask;
    dis32bin(ch);

    return 0;
}
/*
0000-0000 0000-0000 0000-0000 0101-0101
1111-1111 1111-1111 1111-1111 1110-1111
0000-0000 0000-0000 0000-0000 0100-0101

*/

需求3:從左至右將第三位,第五位關閉

分析:
原有狀態:0101 0101
假設狀態:1110 1011
假設取反:0001 0100
只需完成假設取反的狀態和原有狀態取反即可
1左移4位:0001 0000
1左移2位:0000 0100
<=> (1<<4) | (1<<2)

int main()
{
    int ch = 0x55;
    int mask = ~ ( (1<<4) | (1<<3) );
    //ch = ch & mask;
    ch &= mask;
    dis32bin(ch);

    return 0;

    //0000-0000 0000-0000 0000-0000 0100-0001
}

需求4:從左至右第三位到第六位反轉

分析:
原有狀態:0101 0101 ^異或運算
假設狀態:0011 1100
目標狀態:0110 1001

假設狀態:0010 0000
假設狀態:0001 0000
假設狀態:0000 1000
假設狀態:0000 0100

最終狀態:0011 1100

int main()
{
    int ch = 0x55;
    int mask = (1<<5)|(1<<4)|(1<<3)|(1<<2);
    dis32bin(ch);
    ch ^= mask;
    dis32bin(mask);
    dis32bin(ch);
    return 0;
/*
0000-0000 0000-0000 0000-0000 0101-0101
0000-0000 0000-0000 0000-0000 0011-1100
0000-0000 0000-0000 0000-0000 0110-1001
*/
}

查看某一位的狀態

需求從左至右第五位的狀態
分析:
原有狀態:0101 0101
假設狀態:0001 0000 求 & =1

int main()
{
    int ch = 0x55;
    int mask = 1<<4;
    if(ch&mask)
        printf("此位為1\n");
    else
        printf("此位為0\n");

    return 0;
    //此位為1
}

位操作的總結

1.你要操作的那幾位
2.找到合適的掩碼
3.找到合適的位運算

test:

從鍵盤輸入一個整數,輸出3-6位構成的數(從低位0號開始編號)

//先進行掩碼的設置操作,之后在位移
int main()
{   
    //0101 0101
    int a = 0x55;
    int mask = a<<3|a<<4|a<<5|a<<6;
    a &= mask;
    a >>= 3;
    printf("a = %d\n",a);
}
//先位移,在進行掩碼的設置操作
int main()
{
    int a = 0x55;
    a >>= 3;
    int mask = 0x0f;
    a &= mask;
    printf("a = %d\n",a);

    return 0;
}

優先級

() > 成員運算 > (!) 算術 > 關系 > 邏輯 > 賦值>
() > 成員運算 > (~!) 算術 > 關系 > (>> <<) 位邏輯(& | ^) 邏輯 > 賦值>

循環移位

#include<stdio.h>
void dis32bin(int data)
{
    int i = 32;
    while(i--)
    {
        if(data & (1<<i))
            printf("1");
        else
            printf("0");
        if(i%4 == 0)
        {
            if(i%8 == 0)
                printf(" ");
            else
                printf("-");
        }
    }
    putchar(10);
}

//用無符號的類型,避免了右移補1的問題
void circleMove(unsigned int *pa,int n)
{
    n %= 32;
    if(n>0)//左移
        *pa = (*pa<<n) | (*pa>>(sizeof(*pa)*8-n));
    else//右移邏輯
        *pa = (*pa>>(-n)) | (*pa<<(sizeof(*pa)*8-(-n)));
} 
int main()
{
    int a = 0x80000001;//1000***0001

    circleMove(&a,1);
    dis32bin(a);

    return 0;
}

無參交換

int mySwap(int *pa,int *pb)
{
//引入第三者
    int t = *pa;
    *pa = *pb;
    *pb = t;
}
//以知兩者的和,可以求任何其中之一,有益處的弊端
int mySwap1(int *pa1,int *pb1)
{
    *pa1 = *pa1 + *pb1;
    *pb1 = *pa1 - *pb1;
    *pa1 = *pa1 - *pb1;
}
//x,y,x^y,三者之間兩兩求異或運算即可得到第三者。和加法的思路一樣。
int mySwap2(int *pa2,int *pb2)
{
    *pa2 = *pa2 ^ *pb2;
    *pb2 = *pa2 ^ *pb2;
    *pa2 = *pa2 ^ *pb2;
    /*
    *pa2 ^= *pb2;
    *pb2 ^= *pa2;
    *pa2 ^= *pb2;
    */
}
int main() {
    int a = 3;
    int b = 5;
    mySwap(&a,&b);

    return 0;
}

異或加密(文本與二進制)

void encode(char *buf,char ch)
{
    int len = strlen(buf);
    for(int i = 0;i < len;i++)
    {
        buf[i] ^= ch;
    }
}
void decode(char *buf,char ch)
{
    int len = strlen(buf);
    for(int i = 0;i < len;i++)
    {
        buf[i] ^= ch;
    }
}

int main()
{
    char buf[] = "I love C++";
    printf("buf = %s\n",buf);
    char ch = 'a';//這種只要傳入相同的字符就會出錯。
    encode(buf,ch);
    printf("buf = %s\n",buf);
    decode(buf,ch);
    printf("buf = %s\n",buf);

    return 0;
}

int a;
a ^= a;
printf("a = %d\n";a); //自身異或清零。

改進:

void encode(char *buf,char ch)
{
    int len = strlen(buf);
    for(int i = 0;i < len;i++)
    {
        if(buf[i] == ch)
            continue;
        buf[i] ^= ch;
    }
}
void decode(char *buf,char ch)
{
    int len = strlen(buf);
    for(int i = 0;i < len;i++)
    {
        if(buf[i] == ch)
            continue;
        buf[i] ^= ch;
    }
}

int main()
{
    char buf[] = "I love C++";
    printf("buf = %s\n",buf);
    char ch = 'a';//這種只要傳入相同的字符就會出錯。
    encode(buf,ch);
    printf("buf = %s\n",buf);
    decode(buf,ch);
    printf("buf = %s\n",buf);

    return 0;
}

升級:

#include<stdio.h>
void encode(char *buf,char *px)
{
    int len = strlen(buf);
    int n = strlen(px);
    int j = 0;
    for(int i = 0;i < len;i++)
    {
        if(buf[i] == px[j])
            j++;
        else
        {
            buf[i] ^= px[j++];
            if(j == n)
                j = 0;
        }
    }

}
void decode(char *buf,char *px)
{
    int len = strlen(buf);
    int n = strlen(px);
    int j = 0;
    for(int i = 0;i < len;i++)
    {
        if(buf[i] == px[j])
            j++;
        else
        {
            buf[i] ^= px[j++];
            if(j == n)
                j = 0;
        }
    }

}

int main()
{
    char buf[] = "i love you";
    char xx[] = "19920415";
    encode(buf,xx);
    printf("buf = %s\n",buf);

    char buf2[1024];
    scanf("%s",buf2);
    decode(buf,buf2);
    printf("buf = %s\n",buf);

    return 0;
}

二進制加密沒有上述是否相等問題。

循環移位加密

二進制加密:

void encode(char *buf,int n);
void decode(char *buf,int n)
int main()
{
    FILE *pfr = fopen("01.png","rb+");
    if(pfr == NULL)
        exit(-1);

    FILE *pfw = fopen("02.png","wb+");
    if(pfw == NULL)
        exit(-1);
/* 解密
    FILE *pfr = fopen("02.png","rb+");
    if(pfr == NULL)
        exit(-1);

    FILE *pfw = fopen("03.png","wb+");
    if(pfw == NULL)
        exit(-1);

*/


    char buf[1024];
    int n;
    while((n = fread(buf,1,1024,pfr)) > 0)
    {
        encode(buf,n);
        //decode(buf,n);解密
        fwrite(buf,1,n,pfw);
    }
    fclose(pfr);
    fclose(pfw);

    return 0;
}
void encode(char *buf,int n)
{
    for(int i = 0;i < n;i++)
    {
        unsigned char ch = buf[i];
        buf[i] = ch<<1 | ch>>7;
    }
}

void decode(char *buf,int n)
{
    for(int i = 0;i < n;i++)
    {
        unsigned char ch = buf[i];
        buf[i] = ch>>1 | ch<<7;
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM