一段tricky code

Wrote by mutouyun. (http://darkc.at/a-tricky-code/)

刚刚在网上闲逛,看到reddit上关于最受欢迎的代码的讨论贴,上面有一小段非常有意思的代码:

unsigned int v; // to count the number of bits set in v
unsigned int c; // c accumulates the total bits set in v
for (c = 0; v; c++)
{
    v &= v - 1;
}

能直接看出来这段代码想要干啥么?^_^

其实这段代码的作用是计算v的二进制位里1的个数……是不是有点囧?

那么它是怎么做到的呢?

为了方便说明,我们可以先给v一个默认的初值,比如321。这样v的二进制数字就是101000001。

现在开始第一轮循环:

v - 1 的二进制位此时很容易看出来:101000000。

那么 v &= v - 1 就相当于 v = 101000001 & 101000000,于是得到:v = 101000000。

可以发现,若在运算之前v最右边的数字是0,则 v - 1 将把它变成1;反之,最右边是1,则 v - 1 将把它变成0。因此我们可以知道,不论v最右边的数字是0还是1,经过本轮它都将变成0。

接着第二轮循环:

v - 1 >>> 101000000 - 1 >>> 100111111

v &= v - 1 >>> v = 101000000 & 100111111 >>> v = 100000000

这次的结果是非常有意思的。因为根据二进制位运算的规律,100……00 - 1 将得到 011……11,也就是说从低位开始连续的0,经过减1运算后会全部变成1,而第一个高位上的1将变成0。

又因为0与任何数(0或1)做位与运算,结果都将是0,因此 v &= v - 1 运算的实质是跳过所有低位连续的0,并把高位上第一个1改成0。

然后再看第三轮:

v - 1 >>> 100000000 - 1 >>> 011111111

v &= v - 1 >>> v = 100000000 & 011111111 >>> v = 0

此时v变为0,因此下一轮循环开始时循环跳出。

完整过程共计3轮,因此c等于3,恰好等于v中1的个数。

其实经过上面的分析,不难看出来c其实就是个计数器,每当 v &= v - 1 把v中的一个1改写成0时,c就会加1。

像上面这种技巧性很强的代码,平时是不多见的。不过在比较追求性能的时候可以考虑使用。因为这个算法每一轮循环都会定位到v中的一个1,比普通的移位计算法要快得多了。

Wrote by mutouyun. (http://darkc.at/a-tricky-code/)

一段tricky code

时间: 2024-07-29 10:08:57

一段tricky code的相关文章

BSS段、数据段、代码段、堆与栈

BSS段:BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域. BSS是英文Block Started by Symbol的简称.BSS段属于静态内存分配. 数据段:数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域. 数据段属于静态内存分配. 代码段:代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区 域.这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读

深入C语言内存区域分配(进程的各个段)详解(转)

原文地址:http://www.jb51.net/article/39696.htm 一般情况下,一个可执行二进制程序(更确切的说,在Linux操作系统下为一个进程单元,在UC/OSII中被称为任务)在存储(没有调入到内存运行)时拥有3个部分,分别是代码段(text).数据段(data)和BSS段.这3个部分一起组成了该可执行程序的文件 (1)代码段(text segment):存放CPU执行的机器指令.通常代码段是可共享的,这使得需要频繁被执行的程序只需要在内存中拥有一份拷贝即可.代码段也通常

给大家看段比较有意思的代码

package code.classloader; public class Interesting { public static int count1; public static int count2 = 0; public static Interesting ins = new Interesting(); private Interesting() { count1++; count2++; } public static Interesting getInstance() { re

可执行程序包括BSS段、数据段、代码段

可执行程序包括BSS段.数据段.代码段(也称文本段). 一.BSS BSS(Block Started by Symbol)通常是指用来存放程序中未初始化的全局变量和静态变量的一块内存区域.特点是:可读写的,在程序执行之前BSS段会自动清0.所以,未初始的全局变量在程序执行之前已经成0了. 注意和数据段的区别,BSS存放的是未初始化的全局变量和静态变量,数据段存放的是初始化后的全局变量和静态变量. UNIX下可使用size命令查看可执行文件的段大小信息.如size a.out. 二.数据段 在采

你真的了解一段Java程序的生命史吗

作为一名程序猿 ,我们每天都在写Code,但你真的了解它的生命周期么?今天就来简单聊下它的生命历程,说起一段Java Code,从出生到game over大体分这么几步:编译.类加载.运行.GC. 编译 Java语言的编译期其实是一段“不确定 ”的过程,因为可能是一个前端编译器把.java文件转变为.class文件的过程:也可能是指JVM的后端运行期编译器(JIT编译器)把字节码转变为机器码的过程:还可能是指使用静态提前编译器(AOT编译器)直接把.java文件编译成本地机器码的过程.但是在这里

(转)Linux下数据段的区别(数据段、代码段、堆栈段、BSS段)

进程(执行的程序)会占用一定数量的内存,它或是用来存放从磁盘载入的程序代码,或是存放取自用户输入的数据等等.不过进程对这些内存的管理方式因内存用途 不一而不尽相同,有些内存是事先静态分配和统一回收的,而有些却是按需要动态分配和回收的.对任何一个普通进程来讲,它都会涉及到5种不同的数据段. Linux进程的五个段 下面我们来简单归纳一下进程对应的内存空间中所包含的5种不同的数据区都是干什么的. BSS段:BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域.BS

Lua code reading(lua源码阅读)

http://blog.csdn.net/cnjet/article/details/7023526 Online Lua 5.1 source code browser Recommended reading order: lmathlib.c, lstrlib.c: get familiar with the external C API. Don't bother with the pattern matcher though. Just the easy functions. lapi.

bss段、data段、text段、堆(heap) 和 栈(stack)

bss段: bss段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域. bss是英文Block Started by Symbol的简称. bss段属于静态内存分配. data段: 数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域. 数据段属于静态内存分配. text段: 代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域. 这部分区域的大小在程序运行前就已经确定,并且内存

内存段是如何划分的

BSS段:BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域.BSS是英文Block Started by Symbol的简称.BSS段属于静态内存分配. 数据段:数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域.数据段属于静态内存分配. 代码段:代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域.这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读, 某