宏在计算其位后不会将预期值分配给变量
Macro doesn't assign intended value to a variable after counting its bits
我需要编写一个宏 maxBitsOff
来计算两个变量中的所有零,然后确定哪个变量有更多的零并将其分配给结果变量。如果要比较的两个变量具有相同数量的零,则第一个变量将分配给结果。
例如给定 maxBitsOff(a, b, result, z)
如果 a = 1
和 b = 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_LEN
是 8
,并且您的 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)
中有两个原因:
- 因此它们 必须 在使用时用 semi-colon 调用;否则你在
while(0)
. 后面少了一个分号
- 它创建了一个 "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 中的代码。
我需要编写一个宏 maxBitsOff
来计算两个变量中的所有零,然后确定哪个变量有更多的零并将其分配给结果变量。如果要比较的两个变量具有相同数量的零,则第一个变量将分配给结果。
例如给定 maxBitsOff(a, b, result, z)
如果 a = 1
和 b = 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_LEN
是 8
,并且您的 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)
中有两个原因:
- 因此它们 必须 在使用时用 semi-colon 调用;否则你在
while(0)
. 后面少了一个分号
- 它创建了一个 "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 中的代码。