之前参加的一次面试,有一个小小的题目需要交给我来做,然后要求是纯粹的C代码才可以。我本业就是做C的,所以也不是很难
问题:给定一个字符串,要求可以达成查询字符串中某一个字节的第多少位是0还是1,还需要能做到可以主动设置某一个字节的某一个位为0或者1。实质上,考的就是按位操作的一个知识吧,所以要先考虑思路是怎么样的。(同时要感谢我的朋友老段大哥给我提出的修改意见)
题目实例:
整体思路:
1、首先,要把输入当作字符串来处理,比如"ab",每一个字符都是八个字节的无符号的(要不然会有符号位的影响)
2、其次,由于二进制下,每八个比特位代表一个字符,所以要注意判断属于第几个字节,而且要判断是否越界
3、最后,由于将比特位设置为0和设置为1有很高的耦合性,所以可以合为一个函数,使得代码更加美观
函数思路:
1、get_bit(char * buf, int index)
要达成的功能:获取字符串中任意一个字符的任意比特位
构想:利用 & 的特性来做判断,要注意判断是第几个字符的比特位
参数:字符串buf和标志位index
返回值:返回此比特位是0还是1
2、set_buf_bit(char * buf, int index,int setting)
要达成的功能:设置字符串中任意一个字符的任意比特位为0或者是1
构想:利用 || 和 && 的特性来做按位计算,要注意判断是第几个字符的第几个比特位,
要先右移8-index(index注意范围)位,然后按照需求进行运算,然后再左移8-index位,将字符串复原
参数:字符串buf和标志位index
返回值:void
代码:
int get_bit(char *buf, int index) {
if (index > sizeof(buf) * 2) {
return -1; //越界
}
unsigned char *buf_p = (char *) malloc(sizeof(buf));
memset(buf_p, 0x00, sizeof(buf));
int index_y = 0;
int ack = 0;
if (index > 8) {
index_y = 8 - index % 8;
} else {
index_y = 8 - index;
}
index -= 1;
int index_s = index / 8;
strcpy(buf_p, buf + index_s);
*buf_p = *buf_p >> (index_y);
int acc = *buf_p & 0x01;
if (acc) {
ack = 1;
} else {
ack = 0;
}
return ack;
}
此函数是为了获取相应字节的比特位数据是多少,比如如果buf为0x80,按照二进制来看即使1000 0000(高位在前),get_bit(buf, 1)就应该等于1。拿到字符串之后要先判断一下传入的index值,如果index值大于字符串中最后一个字节的位置,就会导致越界,所以为了防止我要先给一个判断防止越界。至于为什么是sizeof(buf)2,是因为这里的buf是一个指针,它占据的是4个bit,char类型的字节在进行补全之后占据8个bit,所以算是倒换了一手哈哈。
然后就是指针的运用,我在堆上新开辟了一个空间,为了将字符串的内容复制过来,因为如果对原字符串进行操作会导致原来的字符串里的比特位被改掉(也有可能是我才疏学浅没搞明白),所以搞了一个替代品。而且做了一个判断,因为8这个值很特殊,index=9的时候也就是第二个字节的第一个比特位,在判断的时候会导致8-index变位负数,所以要判断是第几个字节,然后再去看比特位的事情。
buf是一个字符串数组,我们知道要看的字节和比特位数的时候,就可以实际操作了。我设置了两个变量,index_s和index_y,第一个是用来判断是第几个字节的,第二个是用来判断要移动的位数。基本操作就是先右移一定位数,让要查看的比特位在最右边,然后让当前的字节去与0x01相与,原理就是0100 0101 & 0000 0001 = 1,或者0100 0100 & 0000 0001 = 0。然后我拿到这个判断出来的结果来做一个flag,如果是1那代表这个比特位是1,反之亦然,函数要返回相应的值,这个函数就ok了。
然后就是设置比特位的函数,这里就相对简单的多了。
void set_buf_bit(char *buf, int index, int setting) {
if (index > 8)
index -= ((index / 8) * 8);
*buf = *buf >> (8 - index);
if (setting) {
*buf |= 0x01;
} else {
*buf &= ~0x01;
}
*buf = *buf << (8 - index);
}
void realContrlSetBufBit(char *buf, int index, int setting) {
int read_index = 0;
read_index = index / 8;
unsigned char *buf_p = &buf[read_index];
set_buf_bit(buf_p, index, setting);
}
可以看到我其实是分成了两部分写,吸取了查询函数的经验,我决定还是要分开写好一些。一部分为了判断是第几个字节,另一部分为了进行实际逻辑操作,为了简单一点,我把要进行置0和置1设置了一个参数,setting,置1的函数就是利用|=1的操作,置0就是利用&=~1的操作,而且移位完了别忘了移回去(我觉得需要)。
完整main函数截图:
这几个ack分别为了测试第一个字节里面,第二个字节里面,还有越界的情况的测试,都是成功的,输出结果如下:
总结:计算机还是很神奇的一门艺术,对于比特位的操作已经算是很精细的操作了,虽然我的程序还有一些问题,在资源利用和函数执行以及算法逻辑上需要再加精细,还有改善的空间,之后如果可以我还会继续研究如何改良的。