我需要按
x
位移动 b
,其中 b
可以为正(左移)、零(nop)或负(右移)。
C 位移位不处理负移位。
我可以定义一个内联函数或宏来执行此操作吗?理想情况下,当我编写此内容时,我不需要知道
x
的类型 - 宏(或内联函数?)应该支持任何整数类型。
您可以使用三元运算符通过宏来完成此操作,然后它应该适用于任何整数类型。
#define SHIFT(val, amt) ((amt) >= 0 ? (val) << (amt) : (val) >> (amt))
唯一的问题是如果
amt
有副作用,它们会被执行两次。
您可以使用内联函数来避免对参数进行多次求值:
static inline uint32_t SHIFT(uint32_t val, int16_t amt)
{
return (amt >= 0) ? val << amt : val >> -amt;
}
选择适合您需求的参数类型 - 但偏移量可能小至
int8_t
,并且在可预见的将来不会遇到范围问题。
请注意,转换有符号类型会产生影响。 C11 标准§6.5.7 移位运算符 设置了规则:
对每个操作数执行整数提升。结果的类型是提升后的左操作数的类型。如果右操作数的值为负数或大于或等于提升的左操作数的宽度,则行为未定义。
的结果是E1 << E2
左移E1
位位置;空出的位用零填充。如果E2
具有无符号类型,则结果的值为 E1 x 2E2,比结果类型中可表示的最大值减少模一。如果E1
具有有符号类型和非负值,并且 E1 x 2E2 可在结果类型中表示,则这就是结果值;否则,行为是未定义的。E1
的结果是E1 >> E2
右移E1
位位置。如果E2
为无符号类型,或者E1
为有符号类型且非负值,则结果值为 E1 / 2E2 商的整数部分。如果E1
具有有符号类型和负值,则结果值是实现定义的。E1
如所写,您的想法引起了 GCC 关于转换的大量警告。
我回复:
正如我所说,您需要获得正确的类型 - 并且您很可能需要具有不同类型的多个函数作为返回值和移位值:
如上SHIFT32
;uint32_t
对于 64 位类型使用SHIFT64
;uint64_t
- 也许
对于 16 位类型使用SHIFT16
;uint16_t
- 也许(但可能不是)
对于 8 位类型使用SHIFT8
。uint8_t
如果需要,您可以为带符号类型添加变体,但是(正如我之前所说),转移带符号类型并不是一个特别好的主意,尽管您可以找到大量可以做到这一点的代码。
您可以使用宏来编写这些函数:
#define GENERATE_SHIFT_FUNCTION(name, type) \
static inline type name(size value, uint16_t amt) \
{ \
return (amt >= 0) ? val << amt : val >> -amt; \
}
GENERATE_SHIFT_FUNCTION(SHIFT64, uint64_t)
GENERATE_SHIFT_FUNCTION(SHIFT32, uint32_t)
GENERATE_SHIFT_FUNCTION(SHIFT16, uint16_t)
请注意,宏调用末尾没有分号。如果有人在场,就会在顶层引入一个空声明。
现在您可以像使用其他函数一样使用这些函数,并且函数的参数会被计算一次,从而避免了当使用宏而不是内联函数时对多次计算产生的担忧。