在C

时间:2016-08-09 02:08:32

标签: 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)

这些是否始终保证得到宽度的值,其中所有位都为零,除了b中的低8之外?两个版本之间是否有任何区别,因为如果有必要,它们应该被签署?

标志

#define GEN_FLAG_16(x) ((0xFFFF) & ((unsigned) 1 << (x)))
#define GEN_FLAG_32(x) ((0xFFFFFFFF) & ((unsigned long) 1 << (x)))

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

两个

#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.
}

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

对于所有情况,假设:

  • C90或C99兼容编译器
  • a和b可以是本机C类型的任意组合,有符号或无符号
  • x始终计算为正整数类型
  • 任意原始字词大小

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

2 个答案:

答案 0 :(得分:3)

  

这些是否始终保证得到宽度值,其中除了b中的低8之外所有位都为零?

是。

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

  

两个版本之间是否存在差异

不,它们是等同的。

  

如果需要,他们应该签署延期

在签名类型上使用逐位运算符通常没有任何意义。

  

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

是的,但结果会有不同的类型,具体取决于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)) )

现在大多数潜在的int-int和隐式类型促销依赖都被删除了。

主要的可移植性问题包括:

  • 您的代码依赖于intlong的大小此类代码是不可移植的,因为这些类型可能有任何大小。使用stdint.h 中的固定宽度整数类型将解决许多这些问题。
  • 您的代码不知道默认类型和整数文字的签名。在许多情况下,你在已签名的操作数上使用逐位运算符,这总是一个坏主意。
  • 您似乎通常不知道隐式类型促销如何在C中起作用。

事实证明,MISRA-C可以解决所有这些问题。我建议购买MISRA-C:2012并将其用于教育目的。

作为旁注,a = OBSUCRE_MACRO(b);之类的代码远不如a = b & 0xFF;这样的代码。因为您总是可以假设读者是C程序员,因此知道C语言,但不知道您的私有秘密宏语言。

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

所以我首先质疑这些宏的优点。

答案 1 :(得分:-1)

你应该做的是使用<stdint.h>中定义的类型。您确切知道标志需要多少位,因此您可以选择适当的数据类型。例如:

#define GEN_FLAG_8(x) ((uint8_t)(1 << (x)))
#define GEN_FLAG_16(x) ((uint16_t)(1 << (x)))

您也可以选择相同的保证,但可能选择uint_fast16_t等更大的数据类型。

值得一提的是,你应该制作宏或函数来进行掩码,这样你就可以避免使用整数文字来编写代码。