在C中应用便携式位掩码和标志

ohi*_*ick 5 c embedded microcontroller

我使用各种微控制器架构/字大小为嵌入式系统开发固件,并尽可能地强调可移植性.我的问题是关于生成/应用安全和可移植的位掩码和标志,对于任何非通用代码,最坏的情况是什么.

位掩码

#define MASK_8_V1(x) ((x) & 0xFF)
#define MASK_8_V2(x) ((x) & 0x00FF)

a = MASK_8_V1(b)
a = MASK_8_V2(b)
Run Code Online (Sandbox Code Playgroud)

这些总是保证获得宽度的值,除了b中的低8之外,所有位都为零.两个版本之间是否有任何区别,因为如果有必要,它们应该签名延长?

#define GEN_FLAG_16(x) ((0xFFFF) & ((unsigned) 1 << (x)))
#define GEN_FLAG_32(x) ((0xFFFFFFFF) & ((unsigned long) 1 << (x)))
Run Code Online (Sandbox Code Playgroud)

如果我需要一个通用的宏来生成标志常量,这总是会导致列出的宽度的标志常量?

#define CHECK_FLAG_16(x, y) ((x) & GEN_FLAG_16(y))
#define CHECK_FLAG_32(x, y) ((x) & GEN_FLAG_32(y))

if(CHECK_FLAG_16(a, b))
{
    // Do something.
}
Run Code Online (Sandbox Code Playgroud)

结合以前的场景,如果设置了b原始值中的所需位,它是否总是执行内码?

对于所有情况,假设:

  • 符合C90或C99标准的编译器
  • a和b可以是本机C类型的任意组合,有符号或无符号
  • x始终求值为正整数类型
  • 任意原生单词大小

对那些提到使用的人进行编辑stdint.h:我最近遇到了一个问题,我需要将一个串行协议处理程序移植到另一个微控制器系列,只发现RAM不是字节可寻址的.我们最终删除了uint8_t的所有用法,并进行了修改以使用16位可寻址内存.这让我想知道我是否可以使用本机C类型以不同方式实现它以避免以后的修改.我的问题来自这个问题.

Lun*_*din 3

这些是否总是保证获得 a 的宽度值,其中除了 b 的低 8 位之外所有位都清零?

是的。

不过,宏的结果类型可能会有所不同,具体取决于类型b。这可能会导致可移植性问题。因此,最好将结果转换为预期类型,例如uint32_t

2个版本有什么区别吗

不,它们是等价的。

如有必要,应签署延长期限

在有符号类型上使用按位运算符通常没有任何意义。

如果我需要一个通用宏来生成标志常量,这是否总是会产生列出宽度的标志常量?

是的,但是根据 int 或 long 的大小,结果将具有不同的类型。

我最近遇到一个问题,我们需要将我编写的串行协议处理程序移植到另一个微控制器系列,却发现 RAM 无法进行字节寻址。

这主要是编译器的问题。此外,尚不清楚uint8_t在此类系统上会出现什么问题,总是存在隐式整数提升。听起来像是你遇到了一些算法问题,可能是使用uint8_t*指针或类似问题的代码。


迂腐地说,完全可移植的代码看起来像这样:

#define MASK8_32(x) ((uint32_t)(x) & 0xFFul)

#define GEN_FLAG_16(x) (uint16_t)( 0xFFFFu & (1u << (x)) )
#define GEN_FLAG_32(x) (uint32_t)( 0xFFFFFFFFu & (1ul << (x)) )
Run Code Online (Sandbox Code Playgroud)

现在,大多数潜在的 int 大小和隐式类型提升依赖已被删除。


这里主要的可移植性问题是:

  • 您的代码依赖于 的大小int此类long代码是不可移植的,因为这些类型可能具有任意大小。使用 stdint.h 中的固定宽度整数类型解决很多此类问题。
  • 您的代码不知道默认类型和整数文字的符号。在许多情况下,您对有符号操作数使用按位运算符,这始终是一个坏主意。
  • 您似乎通常不知道隐式类型提升在 C 中是如何工作的。

事实证明,所有这些问题都可以通过MISRA-C来解决。我建议购买 MISRA-C:2012 并出于教育目的阅读它。


附带说明一下,像这样的代码的可读性远远a = OBSUCRE_MACRO(b);低于像. 因为您始终可以假设读者是 C 程序员,因此了解 C 语言,但不知道您的私有宏语言。a = b & 0xFF;

此外,类似函数的宏是不安全的,应尽可能避免。

所以我首先质疑这些宏有什么用处。