首页 >> 资讯 > >> 详情

焦点速讯:C语言位操作

日期:2022-02-07 16:32:10  来源:哔哩哔哩

#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 )

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

}

标签: 二进制数 可以使用 前后两个