C語言位操作
#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函數(shù)的兩個參數(shù),參數(shù)1為int,值為命令行參數(shù)的項數(shù)。參數(shù)2為char* []字符指針數(shù)組,指針數(shù)組每個指針元素指向一個命令行參數(shù)字符串
{
???? return 0;
}
unsigned int bstrtoi(char* s) //輸入一個二進制數(shù)字的字符串,轉化為int
{
???? unsigned int res = 0;
???? while (*s)
???? {
???????? res = (res << 1) + (*s++ == '1'); //從二進制字符串的最高位開始一位一位讀取,每讀取新的一位(比之前的位低1位)就將之前的結果*2再加新位的值,直到第一個1之前所有的0*2都為0
???????? // <<左移位運算符,將二進制數(shù)每一位向左移動,左邊界超出范圍的值舍棄,右邊界空出來的填0,<<1代表左移1位,結果和*2^1相同,<<n和*2^n結果相同
???????? //*解引用運算符比++遞增運算符優(yōu)先級低,所以*s++是將s的地址使用一次后遞增,解引用了s,s變?yōu)閟+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); //~取反運算符,將二進制數(shù)的每一位切換(01互換)以8位二進制數(shù)為例,~00000001(1)為11111110,一個數(shù)和該數(shù)取反的值相加一定為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); //&按位與運算符,將兩個二進制數(shù)逐位進行運算并產(chǎn)生一個新的值,兩個數(shù)同一位的值都為1則該位運算結果為1,否則為0,&運算產(chǎn)生新的值,原a1a2不受影響,10101&01111=00101
???? putchar('\n');
???? printf("%s|%s=", s1, s2);
???? display_binary(a1|a2); //|按位或運算符,兩個數(shù)同位的值都為0則該位運算結果為0,否則為1,10101|00111=10111
???? putchar('\n');
???? printf("%s^%s=", s1, s2);
???? display_binary(a1^a2); //^按位異或運算符,兩個數(shù)同位的值互異(一個為1一個為0)則該位運算結果為1,否則(值相同)為0,10101^00111=10010
???? putchar('\n');
}
void display_binary(unsigned int n)
{
???? unsigned int mask = 1 << 31; //本系統(tǒng)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的二進制數(shù)為只有一位是1其他位均為0,和n做按位與運算時會將所有mask上是0的位置重置為0(因為0&1或0&0都為0),而1&會保持另一個數(shù)的值(1&1保持另一個數(shù)的1,1&0保持另一個數(shù)的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結束循環(huán)
???? ????continue; //>>右移位運算符,將所有位向右移動n位,超出右邊界的舍棄,左邊界空出的分兩種情況,如果該數(shù)為無符號unsigned則左邊空出的位填0,如果該數(shù)為有符號則因實現(xiàn)而異mask>>=1等價mask=mask>>1,同樣的mask<<=1等價mask=mask<<1
???? if (mask) //if(mask)和if(i<32)作用相同,i==32意味著mask右移了32位,所有數(shù)都舍棄了變?yōu)?
???? {
???????? 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) //返回該參數(shù)中打開位的數(shù)量
{
???? //假設drive_code是硬件驅動的字節(jié)指令,每一位都有專門的作用,某一位為1則為打開位,對應開啟硬件的某一個功能,某一位為0則為關閉某一個功能
???? int num = 0;
???? int mask = 1; //使用掩碼,0000 0001掩蓋住其他位只顯示最低位的值
???? do
???? {
???? ????num += mask & drive_code; //&的結果只有1或0,為1說明該位為1,計數(shù)+1,為0說明該位為0,計數(shù)+0
???? } while (drive_code>>=1); //每次循環(huán)將參數(shù)右移1,直到所有的1都被舍棄
???? return num;
}
int is_open(unsigned int drive_code, int bit_pos) //查看該數(shù)的指定位是否打開,以字節(jié)為例,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) //規(guī)定正數(shù)向左移
???? {
???? ????return (a << bits) | (a >> (32 - bits)); //每個單獨的<<或>>運算會使空出的為補0,所以用兩個部分相加將溢出的位補回來,而因為空出的都是0,所以可以使用|按位或代替加法
???? }
???? else
???? {
???????? bits = -bits;
???????? return (a >> bits) | (a << (32 - bits));
???? }
}
struct font_struct { //聲明一個位字段結構用于儲存/表示字體設置信息
???? unsigned int id : 8; //格式為 類型 變量名:位數(shù) ,使用unsigned int則該結構至少占一個unsigned int的大小,而id字段占8位,能夠表示0-255號字體id
???? unsigned int size : 7; //有的實現(xiàn)將最先聲明的字段放在結構所在內(nèi)存塊的最右側,聲明順序從上往下分配地址從右往左,size字段占7位,能夠表示0-127號字體大小
???? unsigned int : 1; //未命名字段代表跳過指定位數(shù),這使得在內(nèi)存中前后兩個字段中間有間隔
???? 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; //下劃線,有的實現(xiàn)中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個字節(jié),剩余未聲明的字節(jié)無法通過結構的字段訪問,如果寫作unsigned int:0;會迫使下一個字段與下一個unsigned int對齊(即跳到下一個unsigned int)。一個字段不允許跨越兩個unsigned int,這時編譯器會自動移動跨界的字段保持邊界對齊
};
union { //聯(lián)合
???? struct font_struct font_s; //該結構變量占4字節(jié)
???? unsigned int font_i; //該變量占4字節(jié),既可以用位字段結構來設置和讀取信息,也可以用unsigned int變量來操作
} font = {
????1,12,0,0,0,0 // 聯(lián)合初始化默認用第一個字段(這里結構是聯(lián)合的第一個字段)的形式
};
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; //通過位操作對二進制數(shù)的某幾位賦值時,先將這幾位置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位數(shù)字
???? //本系統(tǒng)中font位字段結構頂部的字段位于低階位,所以對應的unsigned int二進制數(shù)從左(高階位)到右的位字段:下劃線、斜體、粗體、對齊、大小、id
???? //id占8位,id左側為size,將二進制數(shù)右移8位使size移動到編號6-編號0,&127(二進制7個1)只顯示size的7位數(shù)字
???? 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; //按位異或前后兩個運算對象順序無所謂,根據(jù)異或的用法(1^切換位,0^保留位)這里暫且看作根據(jù)*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為根據(jù)*p1的位值對*p2的位進行切換的結果,再次根據(jù)*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 )
????//沒有創(chuàng)建臨時變量完成交換
}