#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 )
//没有创建临时变量完成交换
}