![](/img/trans.png)
[英]right shift count >= width of type or left shift count >= width of type
[英]left shift count >= width of type in C macro
我写了一个C宏来设置/取消设置uint32变量中的位。 这是宏的定义:
extern uint32_t error_field, error_field2;
#define SET_ERROR_BIT(x) do{\
if(x < 0 || x >63){\
break;\
}\
if(((uint32_t)x)<32U){\
(error_field |= ((uint32_t)1U << ((uint32_t)x)));\
break;\
} else if(((uint32_t)x)<64U){\
(error_field2 |= ((uint32_t)1U<<(((uint32_t)x)-32U)));\
}\
}while(0)
#define RESET_ERROR_BIT(x) do{\
if(((uint32_t)x)<32U){\
(error_field &= ~((uint32_t)1U<<((uint32_t)x)));\
break;\
} else if(((uint32_t)x) < 64U){\
(error_field2 &= ~((uint32_t)1U<<(((uint32_t)x)-32U)));\
}\
} while(0)
我正在传递枚举字段,如下所示:
enum error_bits {
error_chamber01_data = 0,
error_port21_data,
error_port22_data,
error_port23_data,
error_port24_data,
/*this goes on until 47*/
};
产生此警告:
左移计数> =类型的宽度[-Wshift-count-overflow]
我这样称呼宏:
USART2->CR1 |= USART_CR1_RXNEIE;
SET_ERROR_BIT(error_usart2);
/*error_usart2 is 47 in the enum*/
return -1;
我收到每个宏的警告,即使那些左移计数小于31的宏也是如此。
如果我使用没有宏的宏定义,则不会产生警告。 其行为与64位变量相同。 我正在使用AC6 STM32 MCU GCC编译器对STM32F7进行编程。 我不知道为什么会这样。 谁能帮我?
如M Oehm所说,可能是编译器无法正确诊断的问题。 一种解决方法是,不使用减号运算,而使用余数运算:
#define _SET_BIT(x, bit) (x) |= 1U<<((bit) % 32U)
#define SET_BIT(x, bit) _SET_BIT(x, (uint32_t)(bit))
#define _SET_ERROR_BIT(x) do{\
if((x)<32U){\
SET_BIT(error_field, x);\
} else if((x)<64U){\
SET_BIT(error_field2, x);\
}\
}while(0)
#define SET_ERROR_BIT(x) _SET_ERROR_BIT((uint32_t)(x))
这样,编译器终于足够聪明,可以知道x
的值永远不会超过32。
使用“ _”宏的调用是为了强制x
始终是uint32_t(无条件地使用宏调用),以避免x
值为负的调用的UB。
在coliru中测试
看到线程后,我想指出一种在线程中两个无符号整数的情况下设置,重置和切换位状态的好方法(也许更干净)。 该代码应为OT,因为使用x时应为unsigned int
(或int
)而不是enum
值。
我已经在此答案的结尾处编写了代码行。
该代码接收许多参数对作为输入。 每对参数是一个字母和一个数字。 该信可能是:
该数字必须是从0到63的位值。代码中的宏会丢弃每个大于63的数字,并且变量不会被修改。 负值尚未评估,因为我们假设位值是无符号值。
例如(如果我们将程序命名为bitman):
执行中:比特人S 0 S 1 T 7 S 64 T 7 S 2 T 80 R 1 S 63 S 32 R 63 T 62
输出将是:
S 0 00000000-00000001
S 1 00000000-00000003
T 7 00000000-00000083
S 64 00000000-00000083
T 7 00000000-00000003
S 2 00000000-00000007
T 80 00000000-00000007
R 1 00000000-00000005
S 63 80000000-00000005
S 32 80000001-00000005
R 63 00000001-00000005
电话62 40000001-00000005
#include <unistd.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
static uint32_t err1 = 0;
static uint32_t err2 = 0;
#define SET_ERROR_BIT(x) (\
((unsigned)(x)>63)?err1=err1:((x)<32)?\
(err1 |= (1U<<(x))):\
(err2 |= (1U<<((x)-32)))\
)
#define RESET_ERROR_BIT(x) (\
((unsigned)(x)>63)?err1=err1:((x)<32)?\
(err1 &= ~(1U<<(x))):\
(err2 &= ~(1U<<((x)-32)))\
)
#define TOGGLE_ERROR_BIT(x) (\
((unsigned)(x)>63)?err1=err1:((x)<32)?\
(err1 ^= (1U<<(x))):\
(err2 ^= (1U<<((x)-32)))\
)
int main(int argc, char *argv[])
{
int i;
unsigned int x;
for(i=1;i<argc;i+=2) {
x=strtoul(argv[i+1],NULL,0);
switch (argv[i][0]) {
case 'S':
SET_ERROR_BIT(x);
break;
case 'T':
TOGGLE_ERROR_BIT(x);
break;
case 'R':
RESET_ERROR_BIT(x);
break;
default:
break;
}
printf("%c %2d %08X-%08X\n",argv[i][0], x, err2, err1);
}
return 0;
}
宏被分成多行,但每个都是一行代码。
代码主体没有错误控制,然后,如果未正确指定参数,则程序可能未定义行为。
问题:
在宏中,您可以区分两种情况,这两种情况都可以。 警告来自未执行的分支,该分支的移位超出范围。 (显然,这些诊断是在消除死分支之前发出的。) @M Oehm
解
无论x
值和 x
类型如何,确保两条路径中的偏移都在0-31范围内。
x & 31
是比x%32
或x%32u
更强的保险。 当x < 0
且具有足够宽的类型时, %
会导致负余数。
#define SET_ERROR_BIT(x) do{\
if((x) < 0 || (x) >63){\
break;\
}\
if(((uint32_t)x)<32U){\
(error_field |= ((uint32_t)1U << ( (x)&31 )));\
break;\
} else if(((uint32_t)x)<64U){\
(error_field2 |= ((uint32_t)1U<<( (x)&31 )));\
}\
}while(0)
作为一般规则:最好在每次使用x
使用()
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.