关于位操作

#if 0
    @file: littleEndian.c
    @desc: 位操作的实验代码
    
    gcc  -W -Wall -pedantic -std=c99 -m32 -o bits littleEndian.c 

1.小端字节序
    低地址存放数据的低字节,高地址存放的是字数据的高字节
    例如 char sr[] = {0X12, 0X34, 0X56, 0X78};
    强转为整数后变为 int num = 0X78563412;

    The Intel x86 and AMD64 / x86-64 series of processors use the little-endian format. 
    x86、MOS Technology 6502、Z80、VAX、PDP-11等处理器为小端序;
    Motorola 6800、Motorola 68000、PowerPC 970、System/370、SPARC(除V9外)等处理器为大端序;
    ARM、PowerPC(除PowerPC 970外)、DEC Alpha、SPARC V9、MIPS、PA-RISC及IA64的字节序是可配置的。
    网络传输一般采用大端序,也被称之为网络字节序,或网络序。

    许多新的处理器是双端法,大小端可配置,实际情况是一旦选择了操作系统,那么字节序也固定了下来.
    比如ARM可以按大端小端处理,但这些芯片上的操作系统 Android  IOS  Linux 却只能运行小端。

    小端序(先传低位)的串行协议 RS-232、RS-422、RS-485、USB、以太网(虽然高字节先传,但每一字节内低位先传)

    https://en.wikipedia.org/wiki/Endianness

2.位运算 & | ^ ~
    表达式 ~0 将生成全1的掩码,不管机器的字节大小是多少.

    当 x=0x87654321,
    A. x的最低位有效,其余位置0.[0x00000012]        x & 0XFF
    B. 除了 x 的最低位,其余位都取补.[0x789ABC12]   x ^ ~0XFF
    C. 最低位设置成全 1 .                          x | 0XFF

    强制类型转换的结果保持位不变,只改变了解释这些位的方式

    当声明一个常量时,默认有符号,要创建一个无符号常量,必须加上后缀字符 U 或 u.

    无符号和有符号比较时,会转换为无符号, -1 将转换成最大值,比如 -1 < 1U-1的二进制全为1,比 1u.

    对于任意整数值x,求负 -x == ~x+1.

    建议:位运算时加上括号.

3. 二进制转换成整数
    比如一个 4 位的类型(w=4),最高位为符号位,权重 -2^(w-1).  w为二进制位数,这里的 ^ 表示 2 的 w-1 次方.
    0001 = -0*2^3 + 0*2^2 + 0*2^1 + 1*2^0 = 1
    0101 = -0*2^3 + 1*2^2 + 0*2^1 + 1*2^0 = 5
    1011 = -1*2^3 + 0*2^2 + 1*2^1 + 1*2^0 = -5
    1111 = -1*2^3 + 1*2^2 + 1*2^1 + 1*2^0 = -1

4. 反码和补码
    有符号类型用最高位表示,正数最高位0;负数最高位1;
    负数另一种解释:用正数的反码加 1 表示(即用正数的`补码`表示),
    例如,我们通过如下变换得到-7+7[0111],反码 -- > [1000],反码加1 -- > -7[1001]. 
    最终这个 -7[1001]+7[0111] 的补码。
    假如一个机器`4`位,则:
    正数范围`[0001]`~`[0111]`,即`1`-`7`,`[2^3-1]`
    负数范围`[1000]`~`[1111]`,即`-8`至`-1`

5. 乘以常数
    乘以2的整数次幂n,参照十进制乘以10的整数次幂,后面补0即可,二进制的话即左移n次.
    x*14 ==>  14 = 2^3 + 2^2 + 2^1 ==> (x << 3) + (x << 2) + (x << 1)
    https://graphics.stanford.edu/~seander/bithacks.html

6. 利用位操作简化代码
    /**** 求2的幂 ****/
    1 << 2;      4,22次方
    1 << 10;    1024,210次方
  
    /**** 使用 ^ 切换变量 0 或 1 ****/
     --- before ---
    if (toggle)  toggle = 0;
    else toggle = 1;
    或者 togle = toggle ? 0 : 1;
    --- after ---
    toggle ^= 1;

    /**** 使用 !! 将数字转为布尔值 ****/
    !!7;      true
    !!0;      false

    /**** 使用^判断符号是否相同 ****/
    (a ^ b) >= 0;    true 相同; false 不相同

    /**** 使用^来检查数字是否不相等 ****/
    --- before ---
    if (a !== 1171) {...}
    --- after ---
    if (a ^ 1171) {...}

7.// https://stackoverflow.com/questions/47981/how-do-you-set-clear-and-toggle-a-single-bit
    /* a: target variable, b: bit number to act upon 0-n */
    #define BIT_SET(a,b) ((a) |= (1ULL<<(b)))
    #define BIT_CLEAR(a,b) ((a) &= ~(1ULL<<(b)))
    #define BIT_FLIP(a,b) ((a) ^= (1ULL<<(b)))
    #define BIT_CHECK(a,b) (!!((a) & (1ULL<<(b))))        // '!!' to make sure this returns 0 or 1
#endif 

#include <stdio.h>

/************ 大小端测试 *************/

static char isLittleEndian() {
    char *str = "abcd";
    /* a 会放在num的低位, num 表示dcba */
    int *num = (int *)str;   
    return (*num & 0XFF) == str[0];
}

static char _isLittleEndian() {
    int num = 1;
    /* 1 会放在str的低地址str[0]中 */
    char *str = (char *)&num;
    return str[0];
}

static void test1() {
                 /* 低地址   --->  高地址 */
    char str[] = {0X12, 0X34, 0X56, 0X78};
    /* 强转为整数后变成 0X78563412  */
    int *num = (int *)str;   

         /* 高地址   <---  低地址 */
    /* int 值: 0X78563412 */

    printf("%X\n", *num);   /* 打印 78563412 */

}

static void test2() {
    if (isLittleEndian() && _isLittleEndian() ) 
        printf("little Endian\n");
    else 
        printf("big Endian\n");
}

static void test3() {
    size_t i;
    typedef union _Int_ {
        unsigned int num;
        unsigned char str[4];
    } Int;

    Int number;
    number.num = 0X78563412UL;
    for (i = 0; i < sizeof(Int); i++) 
        printf("%.2X", number.str[i]);
    /* 打印结果 12345678 */
    printf("\n");
}

static void test4() {
    char array[12] = {0X1, 0X2, 0X3, 0X4, 0X5, 0X6, 0X7, 0X8};
    short *pshort = (short *)array;
    int *pint = (int *)array;
    long long *pint64 = (long long *)array;
    printf("%#.4X, %#.4X, %#.16llX, %#.8X\n", 
        *pshort,        /* 2字节,输出0X0201 */
        *(pshort + 2),  /* 前进2步,每步2个字节, 0+4=4,指向 0X05, 0X06, 输出 0X0605 */
        *pint64,        /* 8字节,输出 0X0807060504030201 */
        *(pint + 2));   /* 前进2步, 每步4字节,0+8=8,指向array[8]~array[11],输出00000000 */
}


/************ 测试打印二进制数据 *************/

/*
 * showBits
 * n:   要展示的数字
 * T:   数据类型,char, short, int, long...
 * len: 要展示的长度,传入-1则打印全部长度
 */
#define showBits(n, T, len) do {                \
    char buffer[128] = {'\0'};                  \
    size_t truc, i, size, spaces, length, bit;  \
    bit = 1UL;                                  \
    truc = len;                                 \
    length = 8 * sizeof(T);                     \
    if (truc < length) length = truc;           \
    spaces = length / 4 - !(length % 4);        \
    size = length + spaces;                     \
    for (i = 0; i < length && size > 0; i++) {  \
        if (i && !(i % 4)) buffer[--size] = ' ';\
        if (bit&((size_t)n)) buffer[--size] = '1';\
        else buffer[--size] = '0';              \
        bit <<= 1;                              \
    }                                           \
    printf("%s\n", buffer);                     \
} while(0)

#define showBytes(n, T, len) do {               \
    size_t i, size = len, mask = 0XFFUL;        \
    if (size > sizeof(T)) size = sizeof(T);     \
    for (i = 0; i < size; i++)                  \
        printf("%.2X ", (unsigned char)((n>>(8*(size-1-i)))&mask));     \
    printf("\n");                               \
} while(0)

static void show_bytes(void *pointer, size_t len) {
    size_t i;
    unsigned char * ptr = (unsigned char *)pointer;
    for (i = 0; i < len; i++)
        printf("%.2X ", ptr[len - 1-i]);
    printf("\n");
}

/* 测试 size_t 可表示最大容量 */
static void test5() {
    size_t n = ~0;
    printf("==== test size_t ====\n");
    showBits(n, size_t, 8 * sizeof(size_t));
    /* 1111 1111 1111 1111 1111 1111 1111 1111 */

#ifdef __x86_64__
    printf("%lu bytes\n", n);
    printf("%lu kb\n", n / 1024);
    printf("%lu M\n", n / 1024 / 1024);
    printf("%lu G\n", n / 1024 / 1024 / 1024);
#else  
    printf("%u bytes\n", n);
    printf("%u kb\n", n / 1024);
    printf("%u M\n", n / 1024 / 1024);
    printf("%u G\n", n / 1024 / 1024 / 1024);
#endif 
}

static void test6() {
    int len = 15;
    char a, b;
    a = 8, b = -7;
    printf("==== test showBits ====\n");

    /* 显示二进制 */
    showBits(11, char, -1);     /* 0000 1011 */
    showBits(14, short, len);   /* 000 0000 0000 1110 */
    showBits(15, int, len);     /* 000 0000 0000 1111 */
    showBits(-8, char, len);    /* 1111 1000 */
    showBits(-7, long, -1);     /* 1111 1111 1111 1111 1111 1111 1111 1001 */
    showBits(&len, int *, -1);  /* 1111 1111 1000 0001 1000 0110 1111 1100 */

    /* 16进制按字节打印 */
    showBytes(-1, char, -1);    /* FF */
    showBytes(-2, short, len);  /* FF FE */
    showBytes(-15, int, len);   /* FF FF FF F1 */
    showBytes(-8, char, len);   /* F8 */
    showBytes(-7, int, len);    /* FF FF FF F9 */

    show_bytes(&a, sizeof(a));  /* 08 */
    show_bytes(&b, sizeof(b));  /* F9 */
}


int main() {
    test1();
    test2();
    test3();
    test4();
    test5();
    test6();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

luuyiran

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值