簡體   English   中英

左移計數> = C宏中類型的寬度

[英]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值。

我已經在此答案的結尾處編寫了代碼行。

該代碼接收許多參數對作為輸入。 每對參數是一個字母和一個數字。 該信可能是:

  • 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
電話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%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使用()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM