欢迎光临散文网 会员登陆 & 注册

C语言位操作

2023-01-31 10:03 作者:虚云幻仙  | 我要投稿

#include <stdio.h>

#include <stdbool.h>

#include <ctype.h>


unsigned int bstrtoi(char* s);

void operate1(char* s1, char* s2);

void display_binary(unsigned int n);

int bit_open_num(unsigned int drive_code);

int is_open(unsigned int drive_code, int bit_pos);

int rotate(unsigned int a, int bits);

void font_setting(void);

void get_font_info(void);

void font_setting2(void);

void show_long(unsigned long l);

int main(int argc, char* argv[])    //main函数的两个参数,参数1为int,值为命令行参数的项数。参数2为char* []字符指针数组,指针数组每个指针元素指向一个命令行参数字符串

{

     return 0;

}

unsigned int bstrtoi(char* s) //输入一个二进制数字的字符串,转化为int

{

     unsigned int res = 0;

     while (*s)

     {

         res = (res << 1) + (*s++ == '1'); //从二进制字符串的最高位开始一位一位读取,每读取新的一位(比之前的位低1位)就将之前的结果*2再加新位的值,直到第一个1之前所有的0*2都为0

         // <<左移位运算符,将二进制数每一位向左移动,左边界超出范围的值舍弃,右边界空出来的填0,<<1代表左移1位,结果和*2^1相同,<<n和*2^n结果相同

         //*解引用运算符比++递增运算符优先级低,所以*s++是将s的地址使用一次后递增,解引用了s,s变为s+1,字符串中该位为'1'时==返回1,和表示的值一致

     }

     return res;

}

void operate1(char* s1, char* s2)

{

     unsigned int a1 = bstrtoi(s1);

     unsigned int a2 = bstrtoi(s2);

     printf("~%s=", s1);

     display_binary(~a1); //~取反运算符,将二进制数的每一位切换(01互换)以8位二进制数为例,~00000001(1)为11111110,一个数和该数取反的值相加一定为11111111即255或-1(有符号)所以a+(~a)=255(-1)所以~a=-a+255或者-a-1

     putchar('\n');

     printf("~%s=", s2);

     display_binary(~a2);

    //减法运算x-y=x+(~y+1) 转换为加取反的值加1

     putchar('\n');

     printf("%s&%s=", s1, s2);

     display_binary(a1&a2); //&按位与运算符,将两个二进制数逐位进行运算并产生一个新的值,两个数同一位的值都为1则该位运算结果为1,否则为0,&运算产生新的值,原a1a2不受影响,10101&01111=00101

     putchar('\n');

     printf("%s|%s=", s1, s2);

     display_binary(a1|a2); //|按位或运算符,两个数同位的值都为0则该位运算结果为0,否则为1,10101|00111=10111

     putchar('\n');

     printf("%s^%s=", s1, s2);

     display_binary(a1^a2); //^按位异或运算符,两个数同位的值互异(一个为1一个为0)则该位运算结果为1,否则(值相同)为0,10101^00111=10010

     putchar('\n');

}

void display_binary(unsigned int n)

{

     unsigned int mask = 1 << 31; //本系统int为32位,1<<31将1左移31位从第1位移动到第32位即最高位

     int i;

     for (i = 0; (mask & n) != mask && i < 32; mask >>= 1, i++) //mask在(mask&n)中起到掩码的作用,mask的二进制数为只有一位是1其他位均为0,和n做按位与运算时会将所有mask上是0的位置重置为0(因为0&1或0&0都为0),而1&会保持另一个数的值(1&1保持另一个数的1,1&0保持另一个数的0),所以mask&n是掩盖住所有mask上是0的位,只显示mask上是1的位在n上对应的位的值,当mask=1<<31时,mask&n用于查看n的最高位是1还是0,每一次mask>>1之后查看的位换成上一次的右侧位,如果该位n上为0,则&的结果为0(因为其他位都掩盖为0了),如果该位n上为1,则&的结果和mask相等,从最高位开始逐位查看,直到找到第一个1的位或者看完所有32位均为0结束循环

         continue; //>>右移位运算符,将所有位向右移动n位,超出右边界的舍弃,左边界空出的分两种情况,如果该数为无符号unsigned则左边空出的位填0,如果该数为有符号则因实现而异mask>>=1等价mask=mask>>1,同样的mask<<=1等价mask=mask<<1

     if (mask) //if(mask)和if(i<32)作用相同,i==32意味着mask右移了32位,所有数都舍弃了变为0

     {

         putchar('1'); //从该位开始打印字符

         for ( i++,mask>>=1; i < 32; mask >>= 1, i++)

         {

             putchar(((mask & n) == mask) + '0'); //ASCII码中'1'比'0'字符编码大1,表达式(mask & n) == mask为1时即该位为1,1+'0'='1',为0时即该位为0,放入'0'

             if (i % 8 == 7)

                 putchar(' ');

         }

     }

     else

     {

         putchar('0');

     }

}

int bit_open_num(unsigned int drive_code) //返回该参数中打开位的数量

{

     //假设drive_code是硬件驱动的字节指令,每一位都有专门的作用,某一位为1则为打开位,对应开启硬件的某一个功能,某一位为0则为关闭某一个功能

     int num = 0;

     int mask = 1; //使用掩码,0000 0001掩盖住其他位只显示最低位的值

     do

     {

         num += mask & drive_code; //&的结果只有1或0,为1说明该位为1,计数+1,为0说明该位为0,计数+0

     } while (drive_code>>=1); //每次循环将参数右移1,直到所有的1都被舍弃

     return num;

}

int is_open(unsigned int drive_code, int bit_pos) //查看该数的指定位是否打开,以字节为例,1000 0000最左侧的1为编号7的位,最右侧的0为编号0的位,bit_pos==7则要查看编号7的位

{

     int mask = 1 << bit_pos;

     return (mask & drive_code) == mask; //查看该位是否为1

}

int rotate(unsigned int a, int bits) //将a左移或右移bits位,将溢出的位补到空出的位上

{

     if (bits>0) //规定正数向左移

     {

         return (a << bits) | (a >> (32 - bits)); //每个单独的<<或>>运算会使空出的为补0,所以用两个部分相加将溢出的位补回来,而因为空出的都是0,所以可以使用|按位或代替加法

     }

     else

     {

         bits = -bits;

         return (a >> bits) | (a << (32 - bits));

     }

}

struct font_struct { //声明一个位字段结构用于储存/表示字体设置信息

     unsigned int id : 8; //格式为 类型 变量名:位数 ,使用unsigned int则该结构至少占一个unsigned int的大小,而id字段占8位,能够表示0-255号字体id

     unsigned int size : 7; //有的实现将最先声明的字段放在结构所在内存块的最右侧,声明顺序从上往下分配地址从右往左,size字段占7位,能够表示0-127号字体大小

     unsigned int : 1; //未命名字段代表跳过指定位数,这使得在内存中前后两个字段中间有间隔

     unsigned int align : 2; //表示范围0-3,0代表左对齐,1代表居中,2代表右对齐

     //bool boldface : 1; //boldface粗体,1代表开,0代表闭,_Bool布尔类型,头文件中包含<stdbool.h>后可以使用bool作为_Bool别名,并且可以使用true和false代表1和0,和c++兼容

     //bool italic : 1; //italic斜体,1开0闭

     //bool underline : 1; //下划线,有的实现中bool可以填充空着的位字段

     unsigned int boldface : 1;

     unsigned int italic : 1;

     unsigned int underline : 1;

     unsigned int : 3; //8+7+1+2+1+1+1+3占用3个字节,剩余未声明的字节无法通过结构的字段访问,如果写作unsigned int:0;会迫使下一个字段与下一个unsigned int对齐(即跳到下一个unsigned int)。一个字段不允许跨越两个unsigned int,这时编译器会自动移动跨界的字段保持边界对齐

};

union { //联合

     struct font_struct font_s; //该结构变量占4字节

     unsigned int font_i; //该变量占4字节,既可以用位字段结构来设置和读取信息,也可以用unsigned int变量来操作

} font = {

    1,12,0,0,0,0 // 联合初始化默认用第一个字段(这里结构是联合的第一个字段)的形式

};

void font_setting(void)

{

     get_font_info();

     unsigned int value;

     int scan_success;

     for (char ch; puts("f)change font\ts)change size\ta)change alignment\nb)toggle bold\ti)toggle italic\tu)toggle underline\nq)quit"), (ch = tolower(getchar())) != 'q';)

     {

         if (ch!='\n')

             while (getchar() != '\n')

                 continue;

         switch (ch)

         {

             case 'f':

                 printf("Enter font id(0-255):");

                 scan_success = scanf_s("%u", &value);

                 while (getchar() != '\n')

                     continue;

                 if (scan_success == 1&&value>=0&&value<=255)

                 {

                     font.font_i = (font.font_i & (~255)) | value; //通过位操作对二进制数的某几位赋值时,先将这几位置0,再和要赋的值按位或运算,255为id所在的位的掩码,作用是只显示id掩盖其他位,取反~255将这8位变0其他位变1,作用为保留其他位并将id位置0,因为0|value=value,1会对按位或造成影响,value必须只在id对应的8位上存在1,即value在[0,255]之间,这样才不会对其他位造成影响。1&值保持原值,0|值保持原值,0^值保持原值

                     break;

                 }

                 else

                 {

                     puts("font id from 0 to 255.");

                     continue;

                 }

             case 's':

                 printf("Enter font size(0-127):");

                 scan_success = scanf_s("%u", &value);

                 while (getchar() != '\n')

                      continue;

                 if (scan_success == 1 && value >= 0 && value <= 127)

                 {

                     font.font_s.size = value;    //使用结构的位字段成员直接赋值

                     break;

                 }

                 else

                 {

                     puts("font id from 0 to 127.");

                     continue;

                 }

             case 'a':

                 puts("Select alignment:\n1)left\t2)center\t3)right");

                 scan_success = scanf_s("%u", &value);

                 while (getchar() != '\n')

                      continue;

                 if (scan_success == 1 && value >= 1 && value <= 3)

                 {

                     font.font_s.align = value-1;

                     break;

                 }

                 else

                 {

                     puts("font id from 1 to 3.");

                     continue;

                 }

             case 'b':

                 font.font_s.boldface = !font.font_s.boldface; //切换加粗开关,因为只有1位所以不需要用户再次输入值

                 break;

             case 'i':

                 font.font_i ^= 1 << 19; //0^值保持原值,1^值切换该位,1^值(1)=0将值切换,1^值(0)=1将值切换,0^1=1保持1,0^0=0保持0,斜体开关的位在编号19的位,从编号0的1左移19位

                 break;

             case 'u':font.font_s.underline = font.font_s.underline ? 0 : 1; break;

             default:

                 puts("Invalid input. Enter f/s/a/b/i/u/q");

                 continue;

         }

         get_font_info();

     }

     puts("Bye!");

}

void get_font_info(void)

{

     puts(" ID SIZE ALIGNMENT  B   I   U");

     printf("%3u %4u   ", font.font_i & 255, (font.font_i >> 8) & 127); //255即编号7到编号0全为1共8位,该掩码只显示id位字段的8位数字

     //本系统中font位字段结构顶部的字段位于低阶位,所以对应的unsigned int二进制数从左(高阶位)到右的位字段:下划线、斜体、粗体、对齐、大小、id

     //id占8位,id左侧为size,将二进制数右移8位使size移动到编号6-编号0,&127(二进制7个1)只显示size的7位数字

     switch (font.font_s.align)

     {

         case 0:printf("%-8s", "left"); break;

         case 1:printf("%-8s", "center"); break;

         case 2:printf("%-8s", "right"); break;

         default:printf("%-8s", " ");

     }

     printf("%3s %3s %3s\n", font.font_s.boldface ? "on" : "off", font.font_s.italic ? "on" : "off", font.font_s.underline ? "on" : "off");

}

void font_setting2(void) //使用unsigned long类型和按位运算符管理字体信息

{

     unsigned long l = 0;

     int scan_success;

     unsigned long value;

     show_long(l);

     for (char ch; puts("f)change font\ts)change size\ta)change alignment\nb)toggle bold\ti)toggle italic\tu)toggle underline\nq)quit"), (ch=tolower(getchar()))!='q'; )

     {

         if (ch != '\n')

         while (getchar() != '\n')

         continue;

         switch (ch)

         {

             case 'f':

                 printf("Enter font id(0-255):");

                 scan_success = scanf_s("%lu", &value);

                 while (getchar() != '\n')

                     continue;

                 if (scan_success == 1 && value >= 0 && value <= 255)

                 {

                     l = (l & (~255L)) | value;

                     break;

                 }

                 else

                 {

                     puts("font id from 0 to 255.");

                     continue;

                 }

             case 's':

                 printf("Enter font size(0-127):");

                 scan_success = scanf_s("%lu", &value);

                 while (getchar() != '\n')

                     continue;

                 if (scan_success == 1 && value >= 0 && value <= 127)

                 {

                     l = (l & (~(127L << 8))) | (value << 8);

                     break;

                 }

                 else

                 {

                     puts("font id from 0 to 127.");

                     continue;

                 }

             case 'a':

                 puts("Select alignment:\n1)left\t2)center\t3)right");

                 scan_success = scanf_s("%lu", &value);

                 while (getchar() != '\n')

                      continue;

                 if (scan_success == 1 && value >= 1 && value <= 3)

                 {

                     l = (l&(~(3L<<(8+7)))) | ((--value) << (8 + 7));

                     break;

                 }

                 else

                 {

                     puts("font id from 1 to 3.");

                     continue;

                 }

             case 'b':

                 l ^= 1L << (8 + 7 + 2);

                 break;

             case 'i':

                 l ^= 1L << (8 + 7 + 2 + 1);

                 break;

             case 'u':l ^= 1L << (8 + 7 + 2 + 1 + 1); break;

             default:

                 puts("Invalid input. Enter f/s/a/b/i/u/q");

                 continue;

         }

         show_long(l);

     }

}

void show_long(unsigned long l)

{

     puts(" ID SIZE ALIGNMENT  B   I   U");

     printf("%3lu %4lu   ", l & 255L, (l >> 8) & 127L);

     switch (l&3L<<(8+7))

     {

         case 0L << (8 + 7) : printf("%-8s", "left"); break;

         case 1L << (8 + 7) :printf("%-8s", "center"); break;

         case 2L << (8 + 7) :printf("%-8s", "right"); break;

         default:printf("%-8s", " ");

     }

     printf("%3s %3s %3s\n", l & (1L << (8 + 7 + 2)) ? "on" : "off", l & (1L << (8 + 7 + 2 + 1)) ? "on" : "off", l & (1L << (8 + 7 + 2 + 1 + 1)) ? "on" : "off");

}

void swap1(int* p1, int* p2)

{

     int temp1 = *p1 ^ *p2; //按位异或前后两个运算对象顺序无所谓,根据异或的用法(1^切换位,0^保留位)这里暂且看作根据*p1的二进制值对*p2进行切换,使*p1中为1的位对应的*p2中的位切换,保留*p1中为0的位对应的*p2中的位的值

     int i1 = temp1 ^ *p2; //temp1中有部分位和*p2相同(在*p1中为0的位),其他位相反(在*p1中为1的位),相同的位^异或的结果为0,相反的位异或的结果为1,所以运算的结果i1==*p1

     int i2 = *p1 ^ temp1; //temp1为根据*p1的位值对*p2的位进行切换的结果,再次根据*p1的位值对切换过的结果进行切换,会将之前切换过的位又切回来,而保留的位依旧保留,所以两次切换的结果i2==*p2

     //结论:a^b的值再^a得到b,a^b的值再^b得到a

     *p1 = *p1 ^ *p2; //将*p1变成中间值(即a^b)

     *p2 = *p1 ^ *p2; //将中间值^*p2,使*p2的值为最初*p1的原值(即(a^b)^b=a)

     *p1 = *p1 ^ *p2; //将中间值^原*p1的值,使*p1的值为原*p2的值(即 (a^b)^((a^b)^b)=b )

    //没有创建临时变量完成交换

}


C语言位操作的评论 (共 条)

分享到微博请遵守国家法律