счетчик сдвига влево> = ширина типа в макросе C - PullRequest
0 голосов
/ 26 ноября 2018

Я написал макрос 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. </p>

Если я использую определение макроса без макроса, он не выдает предупреждение.Поведение то же самое с 64-битной переменной.Я программирую STM32F7 с AC6 STM32 MCU GCC компилятором.Я не могу понять, почему это происходит.Кто-нибудь может мне помочь?

Ответы [ 3 ]

0 голосов
/ 26 ноября 2018

Видя поток, я хотел указать хороший (и, возможно, более понятный) способ установки, сброса и переключения состояния бита в случае двух целых чисел без знака, как в потоке.Этот код должен быть OT, потому что использует x, который должен быть unsigned int (или int), а не enum.

Я написал строку кода в конце этого ответа.

Код получает в качестве входных данных несколько пар параметров.Каждая пара параметров представляет собой букву и цифру.Буква может быть:

  • S для установки бита
  • R для сброса бита
  • T для переключения бита

Число должно быть битовым значением от 0 до 63. Макросы в коде отбрасывают каждое число больше 63, и ничего не изменяется в переменные.Отрицательные значения не были оценены, потому что мы предполагаем, что значение бита является значением без знака.

Например (если мы назовем программу bitman):

Выполнение: bitman S 0 S 1 T7 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;
}

Макросы разбиты более чем на одну строку, но каждый из них представляет собой однострочный код.

Основной код не имеет контроля ошибок, поэтому, если параметры заданы неправильно, программа может иметь неопределенное поведение.

0 голосов
/ 26 ноября 2018

Проблема:

В макросах вы различаете два случая, которые сами по себе являются нормальными.Предупреждение приходит от ветки, которая не выполняется, где сдвиг вне диапазона.(По-видимому, эта диагностика выдается до устранения мертвой ветви.) @ M Oehm

Решение

Убедитесь, что сдвиги находятся в диапазоне0-31 в обоих направлениях независимо от значения x и типа x.

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.

0 голосов
/ 26 ноября 2018

Вероятно, проблема в том, что компилятор не может правильно диагностировать, как утверждает 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, без учета вызова макроса, избегая UB вызова с отрицательным значением x.

Проверено в coliru

...