简体   繁体   English

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

[英]left shift count >= width of type in C macro

I have written a C Macro to set/unset Bits in a uint32 variable. 我写了一个C宏来设置/取消设置uint32变量中的位。 Here are the definitions of the macros: 这是宏的定义:

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)

I am passing a field of an enumeration, that looks like this: 我正在传递枚举字段,如下所示:

enum error_bits {
    error_chamber01_data = 0,
    error_port21_data,
    error_port22_data,
    error_port23_data,
    error_port24_data,
/*this goes on until 47*/
};

This warning is produced: 产生此警告:

left shift count >= width of type [-Wshift-count-overflow] 左移计数> =类型的宽度[-Wshift-count-overflow]

I am calling the Macros like this: 我这样称呼宏:

USART2->CR1 |= USART_CR1_RXNEIE;
SET_ERROR_BIT(error_usart2);
/*error_usart2 is 47 in the enum*/
return -1;

I get this warning with every macro, even with those where the left shift count is < 31. 我收到每个宏的警告,即使那些左移计数小于31的宏也是如此。

If I use the definition of the macro without the macro, it produces no warning. 如果我使用没有宏的宏定义,则不会产生警告。 The behaviour is the same with a 64 bit variable. 其行为与64位变量相同。 I am programming a STM32F7 with AC6 STM32 MCU GCC compiler. 我正在使用AC6 STM32 MCU GCC编译器对STM32F7进行编程。 I can't figure out why this happens. 我不知道为什么会这样。 Can anyone help me? 谁能帮我?

Probably a problem with the compiler not being able to diagnose correctly, as stated by M Oehm. 如M Oehm所说,可能是编译器无法正确诊断的问题。 A workaround could be, instead of using the minus operation, use the remainder operation: 一种解决方法是,不使用减号运算,而使用余数运算:

#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))

This way the compiler is finally smart enough to know that the value of x will never exceed 32. 这样,编译器终于足够聪明,可以知道x的值永远不会超过32。

The call to the "_" macro is used in order to force x to always be an uint32_t, inconditionally of the macro call, avoiding the UB of a call with a negative value of x . 使用“ _”宏的调用是为了强制x始终是uint32_t(无条件地使用宏调用),以避免x值为负的调用的UB。

Tested in coliru coliru中测试

Seeing the thread I wanted to indicate a nice (and perhaps cleaner) way to set, reset and toggle the status of a bit in the case of the two unsigned integers as in thread. 看到线程后,我想指出一种在线程中两个无符号整数的情况下设置,重置和切换位状态的好方法(也许更干净)。 This code should be OT because uses x that shall be an unsigned int (or an int ) and not a enum value. 该代码应为OT,因为使用x时应为unsigned int (或int )而不是enum值。

I've written the line of code at the end of this answer. 我已经在此答案的结尾处编写了代码行。

The code receives as input a number of parameter couples. 该代码接收许多参数对作为输入。 Each couple of parameter is a letter and a number. 每对参数是一个字母和一个数字。 The letter may be: 该信可能是:

  • S to set a bit S设置一点
  • R to reset a bit R重设一点
  • T to toggle a bit T切换一下

The number has to be a bit value from 0 to 63. The macros in the code discard each number greater than 63 and nothing is modified into the variables. 该数字必须是从0到63的位值。代码中的宏会丢弃每个大于63的数字,并且变量不会被修改。 The negative values haven't been evalued because we suppose a bit value is an unsigned value. 负值尚未评估,因为我们假设位值是无符号值。

For Example (if we name the program bitman): 例如(如果我们将程序命名为bitman):

Executing: 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 S 1 T 7 S 64 T 7 S 2 T 80 R 1 S 63 S 32 R 63 T 62

The output will be: 输出将是:

S 0 00000000-00000001 S 0 00000000-00000001
S 1 00000000-00000003 S 1 00000000-00000003
T 7 00000000-00000083 T 7 00000000-00000083
S 64 00000000-00000083 S 64 00000000-00000083
T 7 00000000-00000003 T 7 00000000-00000003
S 2 00000000-00000007 S 2 00000000-00000007
T 80 00000000-00000007 T 80 00000000-00000007
R 1 00000000-00000005 R 1 00000000-00000005
S 63 80000000-00000005 S 63 80000000-00000005
S 32 80000001-00000005 S 32 80000001-00000005
R 63 00000001-00000005 R 63 00000001-00000005
T 62 40000001-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;
}

The macros are splitted in more then one line, but they are each a one-line code. 宏被分成多行,但每个都是一行代码。

The code main has no error control then if the parameters are not correctly specified the program might be undefined behaviour. 代码主体没有错误控制,然后,如果未正确指定参数,则程序可能未定义行为。

Problem: 问题:

In the macros, you distinguish two cases, which, on their own, are okay. 在宏中,您可以区分两种情况,这两种情况都可以。 The warning comes from the branch that isn't executed, where the shift is out of range. 警告来自未执行的分支,该分支的移位超出范围。 (Apparently these diagnostics are issued before the dead branch is eliminated.) @M Oehm (显然,这些诊断是在消除死分支之前发出的。) @M Oehm

Solution

Insure shifts are in range 0-31 in both paths regardless of the x value and type of x . 无论x x类型如何,确保两条路径中的偏移都在0-31范围内。

x & 31 is a stronger insurance than x%32 or x%32u . x & 31是比x%32x%32u更强的保险。 % can result in negative remainders when x < 0 and with a wide enough type. 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)

As a general rule: good to use () around each usage of x . 作为一般规则:最好在每次使用x使用()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM