【问题标题】:Test_bit macro in C/C++C/C++ 中的 Test_bit 宏
【发布时间】:2018-10-17 08:45:36
【问题描述】:

我正在尝试通过 ioctl() 读取 linux 设备的输入,我在许多带有“test_bit”宏的代码示例中看到,但我发现的唯一代码是:#define test_bit(bit, array) (array[bit / 8] & (1 << (bit % 8))),但它没有工作。我得出的结论是,为了拥有这个宏,我的课程中可能需要包含一些东西。 有没有人可以帮助我解决这个问题已经好几天了? 谢谢

编辑: 这是我正在运行的代码:

void test(){
uint8_t key_b[KEY_MAX/8 + 1];
/* the events (up to 64 at once) */
const char *keyboard = "/dev/input/keyboard0";
int keybrdToCapture;
int yalv;
keybrdToCapture = open(keyboard, O_RDONLY);

memset(key_b, 0, sizeof(key_b));
ioctl(keybrdToCapture, EVIOCGKEY(sizeof(key_b)), key_b);

for (yalv = 0; yalv < KEY_MAX; yalv++) {

    if (test_bit(yalv, key_b)) {
        switch ( yalv)
            {
            case 0x1c :
                dial->setMessage("Enter");
                dial->show();
                break;
            case 0x66 :
                dial->setMessage("Home");
                dial->show();
                break;
            case 0x3b :
                dial->setMessage("F1");
                dial->show();
                break;
            case 0x3c :
                dial->setMessage("F2");
                dial->show();
                break;
            default:
                dial->setMessage("Unknow for now");
                dial->show();
            }
    }
}

}

【问题讨论】:

  • #define test_bit(bit, array) (array[bit / 8] &amp; (1 &lt;&lt; (bit % 8))) 怎么不起作用?它似乎不是标准 C/C++ 库提供的任何东西。因此,您可以在自己的源代码中定义它。 (虽然,#defines 标识符全小写 - 不好。)通常,宏可能会被重新定义。在这种情况下,友好的编译器至少会提供一个警告。如有疑问,可将其附在#ifndef test_bit#endif // test_bit 中。
  • 感谢您的回答,我对 c++ 和 linux 还很陌生,据我了解,这段代码应该检查特定字节中的特定位是否设置为 1。 (也许我完全错了,从现在起我没有得到太多帮助来解释这个问题)。但是当我在调试中按下一个键时,我可以看到我的字节中的一位从 0 变为 1,但我从未输入我的 test_bit健康)状况。也许这只是一个误解的问题。
  • array[bit / 8] 将位数除以 8 以寻址 array 的正确字节。 bit % 8 计算除以 8 的余数(实际上,分隔存储 0 ... 7 的最后 3 位)。 1 &lt;&lt; (bit &amp; 8) 将 1 移到计算位的位置。最后,(array[bit / 8] &amp; (1 &lt;&lt; (bit % 8))) 屏蔽掉除寻址之外的所有位。如果设置了,则表达式变为非 0(被解释为非假,即真)。否则,表达式变为 0(被解释为假)。当然,这个宏专用于适当的if () 或类似的东西。
  • array 应该是数组类型为unsigned char 的变量(或表达式)。 (charsigned char 可能也可以,但签名可能会导致问题。)
  • 从你之前的评论中我改变了这个希望它更好:#ifndef test_bit #define test_bit(bit, array) (array[bit / 8] &amp; (1 &lt;&lt; (bit % 8))) #endif。从你告诉我它应该工作......在调试我的阵列看起来像: \000\000\000\010\000\ 是正常的吗?

标签: c++ linux keyboard


【解决方案1】:

这是一个小示例,展示了如何使用 test_bit。 (我将其重新命名为 TEST_BIT 并在下面解释原因。):

#include <iomanip>
#include <iostream>

#define TEST_BIT(bit, array) (array[bit / 8] & (1 << (bit % 8)))

int main()
{
  unsigned char data[] = {
    (unsigned char)0xde,
    (unsigned char)0xad,
    (unsigned char)0xbe,
    (unsigned char)0xef
  };
  enum { nData = sizeof data / sizeof *data };
  enum { nDataBits = 8 * nData };
  for (unsigned i = 0; i < nDataBits; ++i) {
    std::cout << "Bit " << std::setw(2) << i << ": "
      << (TEST_BIT(i, data) ? 1 : 0) << '\n';
  }
  return 0;
}

输出:

Bit  0: 0
Bit  1: 1
Bit  2: 1
Bit  3: 1
Bit  4: 1
Bit  5: 0
Bit  6: 1
Bit  7: 1
Bit  8: 1
Bit  9: 0
Bit 10: 1
Bit 11: 1
Bit 12: 0
Bit 13: 1
Bit 14: 0
Bit 15: 1
Bit 16: 0
Bit 17: 1
Bit 18: 1
Bit 19: 1
Bit 20: 1
Bit 21: 1
Bit 22: 0
Bit 23: 1
Bit 24: 1
Bit 25: 1
Bit 26: 1
Bit 27: 1
Bit 28: 0
Bit 29: 1
Bit 30: 1
Bit 31: 1

Life demo on coliru

请注意:

  1. 类函数宏在 C++ 中是一个糟糕的选择。内联函数要好得多,因为宏不是类型安全的,但函数是。在这种情况下,适当的替代方案可能是例如:

    inline bool test_bit(unsigned bit, unsigned char *array)
    {
      return array[bit / 8] & (1 << (bit % 8)) != 0;
    }
  2. 宏应仅以大写命名。在实际的 C++ 编译开始之前处理宏。因此,它们完全超出了任何定义的命名空间。这可能会产生令人惊讶的效果。因此,大写命名使它们有点排他性。 (或者,最好尽可能使用内联函数。)

  3. 宏从字节数组中分离出一个位。因此,隔离值可能是 1、2、4、8、16、32、64、128,具体取决于测试的位(1 &lt;&lt; (bit % 8) 的结果),如果分别。位已设置。只要需要真值,这不是问题,因为一切!= 0 都被视为真。如果打算使用 0 或 1,则应将 test_bit()TEST_BIT() 的结果另外与 0 进行比较。

【讨论】:

  • 感谢您的详细解释。我接受您的建议,作为初学者它非常有用!我更了解我现在在做什么。
  • 你能解释一下这两行到底在做什么吗? enum { nData = sizeof data / sizeof *data }; enum { nDataBits = 8 * nData };
  • sizeof data / sizeof *data 将数组的大小除以第一个元素的大小。结果是数组data 中的元素数。将其分配给枚举器(匿名 enum)使其成为真正的编译时间常量(C/C++ 中唯一支持的真正命名常量 - 在这些新奇特 constexpr 事物之前)。在这种情况下,我也可以使用const size_t nData = sizeof data / sizeof *data;,但在其他情况下则不然。例如。数组维度必须是编译时间常数。 enum 可以在那里工作,但 const size_t nData 不行。
  • 实际上,sizeof data 会返回数组的大小(以字节为单位)。你可能会想:为什么这么复杂?为什么这个/ sizeof *data 虽然不会改变结果?我认为相反:sizeof data / sizeof *data 是一种获取数组元素数量的方法,而不管元素类型如何——这就是我总是这样做的原因。 (我已经习惯了。)不要忘记:这适用于数组,但如果数组作为函数参数传递,那么它们会衰减为指针。这个技巧不适用于指针...
  • 你可以这样做:char a[10]; - 这很简单。你也可以做char b[5 * 2]; 但你不能做int a = 5; char c[a * 2]; 这是因为5 * 2 可以(并且是)由编译器“在编译时”计算。查看机器代码,您会发现结果 10。a * 2 可能不会由编译器计算,因为 a 是一个变量(并且可能在运行时更改)。因此,编译器必须在运行时生成完整的计算代码。 C++ 中不允许在编译时使用未知大小的数组(不是标准)。
【解决方案2】:

聚会有点晚了,但是...我没有详细查找 EVIOCGKEY,所以这只是一个猜测,但这可能是字节顺序问题吗?也许这些位不是您期望它们在数组中的位置。像这样的东西怎么样:

uint64_t key_b = 0;
...
ioctl(keybrdToCapture, EVIOCGKEY(sizeof(key_b)), &key_b);

并使用类似的东西进行测试:

#define test_bit(bit, array) ((array) & ((uint64_t)1 << (bit)))

(或适当的 C++ 函数,任何适合您的需要)

无论如何,你能比“它不起作用”更具体一点吗?

【讨论】:

    猜你喜欢
    • 2011-11-20
    • 2016-09-05
    • 2012-11-30
    • 2012-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多