左移计数> = C宏中类型的宽度

时间:2018-11-26 11:03:02

标签: c gcc macros stm32

我写了一个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进行编程。 我不知道为什么会这样。谁能帮我吗?

3 个答案:

答案 0 :(得分:2)

如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

的测试

答案 1 :(得分:0)

看到线程,我想指出一种在线程中两个无符号整数的情况下设置,重置和切换位状态的好方法(也许更干净)。该代码应为OT,因为使用x时应为unsigned int(或int)而不是enum值。

我在此答案的末尾写了代码行。

代码接收许多参数对作为输入。每对参数是一个字母和一个数字。这封信可能是:

  • S进行设置
  • R重设位
  • T切换一下

该数字必须是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
T 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;
}

这些宏被分成多行,但每个都是一行代码。

代码主体没有错误控制,因此,如果未正确指定参数,则程序可能无法定义行为。

答案 2 :(得分:0)

问题:

  

在宏中,您可以区分两种情况,这两种情况都可以。警告来自未执行的分支,该分支的移位超出范围。 (显然,这些诊断是在消除死分支之前发出的。)     @M Oehm

解决方案

无论x值和x类型如何,两条路径的偏移均在0-31范围内。

x & 31x%32x%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