Разница в выполнении сдвига влево между литеральной константой и переменной с одинаковым значением - PullRequest
0 голосов
/ 04 октября 2018

Я пишу функцию C, которая принимает параметр n и возвращает int с битовым представлением n 1, за которым следует 0, чтобы заполнить тип данных (всего 32 бита).Мой код в настоящее время выглядит следующим образом:

int upperBits(int n) {
    int retval = 0 - 1;
    int shift = 32 - n;
    retval = retval << shift;
    return retval;
}

Этот код завершается ошибкой, когда n = 0, давая возвращаемое значение -1, представленное 32 1, а не 0. Когда, однако, я заменяю shift налитерал:

int upperBits(int n) {
    int retval = 0 - 1;
    int shift = 32 - n;
    retval = retval << 32;
    return retval;
}

Код работает правильно, возвращая 0. Я использовал операторы print, чтобы проверить, что shift = 32, когда функция вызывается с n = 0, поэтому я не понимаю, почему они ведут себя по-разному.Чем обусловлено это различие и как я могу его обойти?

Если это уместно, код выполняется на компьютере с Linux и компилируется с помощью gcc.Я должен использовать прямой код только с этими операторами: ! ˜ & ˆ | + << >>

РЕДАКТИРОВАТЬ: Я все еще не знаю точнов чем проблема, или элегантное решение, но этот обходной путь эффективен:

int upperBits(int n) {
        int retval = 0 - 1;
        int shift = 32 - n;
        int isnull = !(n);
        printf ("%x %x %x \n", retval, shift, n);
        retval = retval << (shift - isnull);
        retval = retval << isnull;
        printf ("%x %x %x \n", retval, shift, n);
        return retval;
}

1 Ответ

0 голосов
/ 04 октября 2018

Вы выполняете недопустимое смещение влево.

Сдвиг влево отрицательного числа вызывает неопределенное поведение , так же как и смещение на величину, большую или равную битовой шириневведите вопрос.

Раздел 6.5.7 стандарта C относительно операторов побитового сдвига:

3 Целочисленные продвижениявыполняется на каждом из операндов.Тип результата - тип повышенного левого операнда. Если значение правого операнда отрицательно или больше или равно ширине повышенного левого операнда, поведение не определено.

4 РезультатE1 << E2 - E1 сдвинутые влево битовые позиции E2;освобожденные биты заполнены нулями.Если E1 имеет тип без знака, значение результата равно E1 × 2 E2, уменьшенное по модулю на единицу больше, чем максимальное значение, представляемое в типе результата.<strong> Если E1 имеет тип со знаком и неотрицательное значение, а E1 × 2 E2 представимо в типе результата, то это результирующее значение;в противном случае поведение не определено.

Вы можете исправить это, используя типы без знака и проверив размер смещения:

uint32_t upperBits(int n) {
    uint32_t retval = 0xffffffff;
    if (n <= 0 || n > 32) {
        return 0;
    } else {
        int shift = 32 - n;
        retval = retval << shift;
        return retval;
    }
}
...