找回密码
 立即注册
首页 业界区 科技 位运算的奇技淫巧:builtin内建函数

位运算的奇技淫巧:builtin内建函数

骂治并 2025-9-29 01:46:22
什么是内建函数:
GCC 编译器自带的内建函数。这些_builtin*形式的内建函数一般是基于不同硬件平台采用专门的硬件指令实现的,因此性能较高。
介绍一些常用的内建函数:

__builtin_ctz()

返回从最低位开始(右起)的连续 0 的个数
根据数据类型不同有:
  1. int __builtin_ctz (unsigned int x)
  2. int __builtin_ctzl (unsigned long x)
  3. int __builtin_ctzll (unsigned long long x)
复制代码
参数 x 虽然都是无符号整型,但是值必须大于0(0 怎么也找不到 1 吧),否则会发生无法定义的后果。
example:
  1. unsigned int x = 0x0820; // 0000 1000 0010 0000
  2. unsigned int zero = __builtin_ctz(x); // zero = 5
复制代码
__builtin_clz()

返回从最高位开始(左起)的连续 0 的个数
根据数据类型不同有:
  1. int __builtin_clz (unsigned int x)
  2. int __builtin_clzl (unsigned long x)
  3. int __builtin_clzll (unsigned long long x)
复制代码
参数 x 虽然都是无符号整型,但是值必须大于0(0 怎么也找不到 1 吧),否则会发生无法定义的后果。
example:
  1. unsigned int x = 0x0820; // 0000 1000 0010 0000
  2. unsigned int zero = __builtin_clz(x); // zero = 4
复制代码
__builtin_ffs()

返回输入数二进制表示的最低非 0 位的下标,下标从 1 开始计数;如果传入 0 则返回 0。
根据数据类型不同有:
  1. int __builtin_ffs (unsigned int x)
  2. int __builtin_ffsl (unsigned long x)
  3. int __builtin_ffsll (unsigned long long x)
复制代码
example:
  1. unsigned int x = 0x0820; // 0000 1000 0010 0000
  2. unsigned int index = __builtin_ffs(x); // index = 5
复制代码
除 0 外,发现传入其他的数,值就是 __builtin_ctz()+1,实际函数实现也是如此。
__builtin_popcount()

返回输入的二进制表示中 1 的个数;如果传入 0 则返回 0 。
根据数据类型不同有:
  1. int __builtin_popcount (unsigned int x)
  2. int __builtin_popcountl (unsigned long x)
  3. int __builtin_popcountll (unsigned long x)
复制代码
example:
  1. unsigned int x = 0x0820; // 0000 1000 0010 0000
  2. unsigned int count = __builtin_popcount(x); // count = 2
复制代码
解决实际项目的例子

使用两字节(16 位)即可控制 16 个任意 IO 口的输出状态
例如设立一个 uint16_t ioCtrl = 0xC8A6; // 1100 1000 1010 0110,那么就是对第 1、2、5、9、11、14、15 位 IO 进行控制,再设立一个 uint16_t ioValue = 0x0C9D; // 0000 1100 1001 1101,来表示 16 位 IO 的状态。
常规的写法是使用循环来对每一个控制的 IO 写入值:
  1. void IOCtrl_WriteIO(uint16_t ioCtrl, uint16_t ioValue)
  2. {
  3.     for (uint16_t i = 0; i < 16; i++)
  4.     {
  5.         if (ioCtrl & (1 << i))
  6.         {
  7.             WriteIO(IO[i].port, IO[i].pin, ioValue & (1 << i));
  8.         }
  9.     }
  10. }
复制代码
只会循环 1 的个数次数,而不用每次都遍历整个 16 位。
扩展

ioCtrl &= ioCtrl - 1是怎么将最后一位 1 置 0 的?
假设 ioCtrl = 0xA8; // 1010 1000
0xA8 - 1 = 0xA7,转成 2 进制后是 1010 0111
0xA8:1010 1000
0xA7:1010 0111
位与操作后就变成了 1010 0000,如此就将最后一位 1 置 0 了。
如何判断一个数是否是 2 的幂?

在位运算里,一个数如果是 2 的幂,那么它的二进制形式一定是:
1, 10, 100, 1000, ...
如果这个数是 2 的幂,那么这个数一定只存在一个 1,对最后一位 1 置 0 后,这个数就变成了 0
  1. void IOCtrl_WriteIO(uint16_t ioCtrl, uint16_t ioValue)
  2. {
  3.     while (ioCtrl)
  4.     {
  5.         // 找到 ioCtrl 最低的置位 bit
  6.         uint16_t i = __builtin_ctz(ioCtrl);
  7.         WriteIO(IO[i].port, IO[i].pin, (ioValue >> i) & 1);
  8.         ioCtrl &= ioCtrl - 1; // 将最后一位1置0
  9.     }
  10. }
复制代码
前面提过内联函数 __builtin_popcount()可以返回 1 的个数,使用此内联函数也可快速判断是否是 2 的幂
  1. bool isPowerOfTwo(uint32_t x)
  2. {
  3.     return x & (x - 1) == 0;
  4. }
复制代码
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册