1.计算一个数的二进制表示形式下1的个数
例如:35=2^5+2+1,35的32位二进制表示为00000000 00000000 00000000 00010011 共3个1
思路:
1.1 不断%2,计算出现1的个数
设置计数器count,每次取模得到1后count++
1.2 先用二进制表示,然后转换为字符串格式,遍历查找1的个数
我们知道对十进制进行八进制、十六进制转换的时候,可以在采用打印格式"%#o"、"%#x",而二进制没有一个直接的打印格式。但是itoa函数可以实现进制转换,(还有另外一种atoi函数)可以大概了解一下:
char *itoa(int value/*要转换的数*/,char *buffer/*存放转换结果的字符串*/,int radix/*转换进制*/);
//“itoa”可以理解为“int to array”的缩写,把10进制数转换为其它进制数然后存放到字符串数组中
//同样atoi()函数中“atoi”可以理解为“array to int”的缩写,可大致了解一下
转换之后就可以对字符串进行查找,找1的个数。
1.3 移位运算,判断个位是否为1
int count = 0, num = 126;
for (int i = 0; num >> i; i++) {
if ((num >> i) & 1 == 1)//将数的二进制形式位移每一位然后与1相与,判断其个位是否为1
count++;
}
此方法还可以求两个整数二进制不同位的个数。
1.4 与上一个数进行按位与运算
我们发现,一个数(num)与它的上一个数(num-1)进行逻辑与运算之后,总可以使该数二进制中1的个数-1,例如1001 0000(160)&100001111(159)的结果为1000 0000,可以发现,进行一次运算后1001 0000中1的个数少了一个,再进行一次1000 0000&0111 1111=0000 0000,1的个数又少了一个,因此我们只需要统计与操作进行了多少次,就能得出有多少个1了
count=0;
while(num) {
num = num & (num - 1);
count++;
}
因为相邻两个数的高位往往相等,与的结果不变。我们只用判断低位,低位大概有这些情况(x...x表示有若干个二进制位的二进制数)
x...x1&x...x0=x...x0,少了一个1
x...x10...0&x...x01...1=x...x00...0,少了一个1
2.验证当前系统存储数据是采用大端存储还是小端存储
2.1 大小端存储的概念
我们知道,数据是以字节为单位存取的,而存储一个int类型的整数需要四个字节。在内存中如何存储这四个字节呢?通常有两种方式,即大端存储和小端存储。
在大端存储中,高位字节存储在低位地址,而低位字节存储在高位地址。在小端存储中,低位字节存储在低位地址,而高位字节存储在高位地址。
当我们在VS的x86平台下中定义了一个int类型的变量x并赋值时,调试查看内存,输入取x的地址,如图:
左侧0x00B9FEA4是x的首地址(虚拟地址),而右侧是该地址下(连续四个地址)存储的四个字节的数据值,为3。而整形3用16进制显示为00 00 00 03(实际是用2进制存储,四个字节即32位二进制序列),可以发现,系统采用的是小端存储。
那么如何不查看内存,通过程序验证是小端存储呢?
2.2 通过程序验证小端存储
在这个例子中,小端存储有效数值在第一个字节,而大端存储有效数值在最后一个字节。因此我们可以只改第一个字节的内容,比如改成1,然后输出x,判断x是否改为了1。如果x为1则采用的是小端存储,否则十六进制序列为01 00 00 03,此时x是一个比较大的数,为大端存储。
由上图可以看到x被改为了1,说明x是按照小端方式来存储的。应当注意的是,(char)x=1是指将1赋值给“将x强制类型转换为char类型的变量”,x自身还是int类型;我们可以用256来验证:
256的32位二进制序列为00000000 00000000 00000001 00000000,其小端存储16进制表现样式为00 01 00 00,执行(char)x=1后该序列变为01 01 00 00,数值为257。如果x变成了char类型,那么x只占1个字节,即第一个字节,值为1,而由输出结果可以看出x并未变为char类型。
事实上,无论是64位系统还是32位系统,大多数计算机架构都采用小端存储(Little-endian)。然而,某些特定的计算机架构可能采用大端存储(Big-endian)。例如,某些RISC体系结构中,如MIPS和PowerPC,支持可配置的字节序,可以选择使用大端或小端存储。
拓展
除了上述方法,我们还可以利用联合体的特性来判断,也是同样的方法。(插入链接)