宏在计算其位后不会将预期值分配给变量

Macro doesn't assign intended value to a variable after counting its bits

我需要编写一个宏 maxBitsOff 来计算两个变量中的所有零,然后确定哪个变量有更多的零并将其分配给结果变量。如果要比较的两个变量具有相同数量的零,则第一个变量将分配给结果。 例如给定 maxBitsOff(a, b, result, z) 如果 a = 1b = 30 a 肯定会赢,因为 1 中的零比 30 中的零更多。所以我们将 a 分配给 result,将零的数量分配给 z。 经过几个小时的调试,我无法让宏工作。这是 C:

中的代码
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <limits.h>
#include "math.h"

#define ARR_LEN 8
#define maxBitsOff(a, b, result, z) { \
    int acount = 0, bcount = 0, i; \
    printf("sizeof(a) = %d\n", sizeof(a)); \
    for(i = 0; i < 8 * sizeof(a); i++) { \
        if(!(a & (1 << i))) { \
            acount++; \
        } \
        if(!(b & (1 << i))) { \
            bcount++; \
        } \
    } \
    if(acount >= bcount) { \
        result = a; \
        z = acount; \
    } else { \
        result = b; \
        z = bcount; \
    } \
    printf("result = %d\n", result); \
    printf("z = %d\n", z); \
}

int main() {
    int arr[ARR_LEN] = {120, 30, 40, 50, 60, 70, 80, 1};
    int z, i, max = 0;
    int result;

    for(i = 0; i < ARR_LEN - 1; i++) {
        maxBitsOff(arr[i], arr[i + 1], result, z);
        if(max < result)
            max = result;
    }
    return 0;
}

在我 运行 这段代码之后,我得到了这些显然很糟糕的结果:

result = 32767

z = 23

由于 ARR_LEN8,并且您的 for 循环从 0 迭代到 31,您正在阅读输入的末尾数组。

为什么?因为你在使用宏。

考虑展开宏时会发生什么:

for (i = 0; i < ARR_LEN - 1; i++)
{
    // expanded maxBitsOff(arr[i], arr[i + 1], result, z):

    ...

    for (i = 0; i < 8 * sizeof(arr[i]); i++)
    {
        if (!(arr[i] & (1 << i)))   // <-- i goes to 31 here

宏替换是文本替换

您在宏中使用变量 i,并将 arr[i] 作为参数传入。尝试在隐藏变量名称中使用下划线。此外,将 A 和 B args 存储在块内的临时存储中。

由于我假设您将其设为宏以避免函数调用的开销,因此这里有一个非常快速的宏,用于计算单个值中的位数。

我想这可以作为你想要的比较依据:

#define bitsOffMacro(x, cnt) do {     \
    int _x = x;                        \
    cnt = 32;                           \
    while(_x & -_x){--cnt;_x&=~(_x&-_x);}\
    } while(0)

流程概要:

  • 从 32 个零开始计数器(假设 4 字节 int)
  • 虽然在值中仍有一点
  • 减少计数器,因为我们找到了一位。
  • 关闭那个位
  • 重复直到所有位都关闭。

然后使用该宏创建:

#define getHigherCount(x,y,cnt) do {           \
    int _Xcnt;             int _Ycnt;           \
    bitsOffMacro(x,_Xcnt); bitsOffMacro(y,_Ycnt);\
    cnt = (_Xcnt > _Ycnt)?_Xcnt:_Ycnt;            \
} while(0)

示例用法

int cnt;
getHigherCount(23, 4, cnt);
printf("Answer is %d\n", cnt); // expected value 31.

// 23 has 4 bits set, and 28 off.
// 4  has 1 bit set, and 31 off.

P.S.

我将宏封装在 do-while(0) 中有两个原因:

  1. 因此它们 必须 在使用时用 semi-colon 调用;否则你在 while(0).
  2. 后面少了一个分号
  3. 它创建了一个 "scope",我可以在其中创建新变量,例如 _x_Xcnt_Ycnt

这主要是为了解决此处的另一个答案,但这里有一种获取位数的更快方法:

#define BC_OffBitCount(in, out)                                \
  {                                                            \
    out = ~in;                                                 \
    switch (sizeof(in)) {                                      \
      case 4:                                                  \
        out = (out & 0x55555555) + ((out >> 1) & 0x55555555);  \
        out = (out & 0x33333333) + ((out >> 2) & 0x33333333);  \
        out = (out & 0x0F0F0F0F) + ((out >> 4) & 0x0F0F0F0F);  \
        out = (out & 0x00FF00FF) + ((out >> 8) & 0x00FF00FF);  \
        out = (out & 0x0000FFFF) + ((out >> 16) & 0x0000FFFF); \
        break;                                                 \
      case 2:                                                  \
        out = (out & 0x5555) + ((out >> 1) & 0x5555);          \
        out = (out & 0x3333) + ((out >> 2) & 0x3333);          \
        out = (out & 0x0F0F) + ((out >> 4) & 0x0F0F);          \
        out = (out & 0x00FF) + ((out >> 8) & 0x00FF);          \
        break;                                                 \
      case 1:                                                  \
        out = (out & 0x55) + ((out >> 1) & 0x55);              \
        out = (out & 0x33) + ((out >> 2) & 0x33);              \
        out = (out & 0x0F) + ((out >> 4) & 0x0F);              \
        break;                                                 \
    }                                                          \
  }

#define BC_OffCompareGreater(a, b, out)  // Left as an exercise to the reader

和测试:

void test_uint8_t_of_0x00_should_return_8(void) {
  uint8_t value = 0;
  uint8_t result;
  BC_OffBitCount(value, result);
  TEST_ASSERT_EQUAL(8, result);
}

void test_uint8_t_of_0x55_should_return_4(void) {
  uint8_t value = 0x55;
  uint8_t result;
  BC_OffBitCount(value, result);
  TEST_ASSERT_EQUAL(4, result);
}

void test_uint16_t_of_0xAA_should_return_12(void) {
  uint16_t value = 0xAA;
  uint16_t result;
  BC_OffBitCount(value, result);
  TEST_ASSERT_EQUAL(12, result);
}

void test_uint32_t_of_0xDEADBEEF_should_return_8(void){
    uint32_t value = 0xDEADBEEF;
    uint32_t result;
    BC_OffBitCount(value, result);
    TEST_ASSERT_EQUAL(8, result);
}

我相信有一种巧妙的方法可以消除重复,但这也可以留作 reader 的练习。

编辑:我复制并修改了 hacker's delight 中的代码。