Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: >>
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
niktagor
Братцы, я с языком си знаком слабовато. Что означает вот эта запись? Подскажите пожалуйста.

*pDMA_DBP = (unsigned short)((unsigned int)sSPORT0_TX_Descriptor >> 16);
rezident
Переменная sSPORT0_TX_Descriptor приводится к типу unsigned int.
Над ее содержимым производится арифметический сдвиг вправо (в сторону младшего бита) на 16 бит.
Результат сдвига приводится к типу unsigned short и записывается по адресу, который содержит переменная указателя pDMA_DB.
defunct
>> - обозначает сдвиг вправо.

x >> n
сдвиг переменной x вправо на n разрядов.

пример:
1100 >> 1 = 0110

Цитата
арифметический сдвиг вправо

Всегда думал что над (unsigned int) может производиться только строго логический сдвиг. Арифметический же должен выполняться исключительно над (signed) переменными.

собсно не сложно в этом убедиться:

signed int x1 = 0x80000000;
unsigned int x2 = 0x80000000;

printf("x1 >> 3 = 0x%x\nx2 >> 3 = 0x%x", x1 >> 3, x2 >> 3);

результат:
x1 >> 3 = 0xF0000000 - арифметический сдвиг
x2 >> 3 = 0x10000000 - логический сдвиг
DpInRock
>> в Сях - это деление на степень двойки. Вот так проще всего описать.
Скажем, -4 сдвинуть вправо будет -2.
4 сдвинуть вправо будет 2.
(А побитнно если рассматривать эти числа будет очень существенная разница. Команда арифметического сдвига дублирует при сдвиге старший бит. И компилятор сам решает какую команду применить).

Посему, если работаем с битами, масками, управляющими словами, адресами регистров - не надо использовать ЗНАКОВЫЕ объявления.
Вернее, использвать можно, но только ТОЧНО зная, что делаешь и зачем тебе это нужно.

У меня вот такое правило. Переменные объявляю всегда unsigned. Если уж объявляю signed - то уж с конкретной явной целью. К примеру буду вычислять что=нибудь, что имеет знак, или потребуется сравнивать что-то с чем-то и потребуется значение разницы.

Короче, куда ни кинь - кругом надо всегда иметь ввиду знаковость переменной.
niktagor
Спасибо, все понял! biggrin.gif
demiurg_spb
То, что понял - это хорошо! Но очень полезно САМОМУ отыскивать информацию. И Википедия очень часто в этом деле помогает. Ну а Гугл - почти наверняка...
Почитай: WIKI-Битовый сдвиг
rezident
Цитата(defunct @ Jul 2 2009, 05:42) *
Всегда думал что над (unsigned int) может производиться только строго логический сдвиг. Арифметический же должен выполняться исключительно над (signed) переменными.
Да, вы правы. Я неправильно указал тип сдвига. Конечно же логический сдвиг, без учета знака.
777777
Цитата(defunct @ Jul 2 2009, 03:42) *
>> - обозначает сдвиг вправо.

x >> n
сдвиг переменной x вправо на n разрядов.

пример:
1100 >> 1 = 0110

Правильнее (чтобы не вводить людей в заблуждение) написать так:
00001100 >> 1 = 00000110


Потому что если число знаковое, то сдвиг распространяет знак:
1100 >> 1 = 1110

Правда, если говорить строго, то это implementation defined, но в большинстве нормальных компиляторов сделано именно так.

Цитата(DpInRock @ Jul 2 2009, 06:16) *
>> в Сях - это деление на степень двойки. Вот так проще всего описать.

Ни в коем случае. Это грубая, хотя и очень распространеная ошибка.

-3 / 2 = -1
-3 >> 1 = -2

Так понятно?
DpInRock
Цитата
Вот так проще всего описать.

Ключевое слово.

А начни вы объяснять с этого примера, то кранты бы пришли человеку.
Поэтому и прозвучал совет не делать неарифметических операций над знаковыми.
defunct
Цитата(777777 @ Jul 2 2009, 13:30) *
Правильнее (чтобы не вводить людей в заблуждение) написать так:
00001100 >> 1 = 00000110

А ну да, если допустить что бывают 4-х битовые знаковые.
Однако, вы не утруждаете себя читать посты до конца.

Цитата
Правда, если говорить строго, то это implementation defined, но в большинстве нормальных компиляторов сделано именно так.

Если говорить строго, то лучше сразу обратиться непосредственно к стандарту.

[#5] The result of E1 >> E2 is E1 right-shifted E2 bit
positions. If E1 has an unsigned type or if E1 has a signed
type and a nonnegative value, the value of the result is the
integral part of the quotient of E1 divided by the quantity,
2 raised to the power E2. If E1 has a signed type and a
negative value, the resulting value is implementation-
defined.

Чтобы было ясно, что именно является implementation defined.

Cобсно определение которое предложил DpInRock, совпадает со стандартом.
а не
Цитата
Это грубая, хотя и очень распространеная ошибка.

как вы говорите.
777777
Цитата(defunct @ Jul 2 2009, 16:12) *
Cобсно определение которое предложил DpInRock, совпадает со стандартом.

Правда он не уточнил, что это справедливо только для беззнаковых. Ну да, он же написал что использует только их.

Цитата(defunct @ Jul 2 2009, 16:12) *
а не
Цитата
Это грубая, хотя и очень распространеная ошибка.

как вы говорите.

Если следовать стандарту, то получается, что отрицательные вообще нельзя сдвигать, так как результат непредсказуем. Тем не менее все компиляторы, имеющиеся у меня под рукой, выдали результат:

-3 / 2 = -1
-3 >> 1 = -2

Так что сдвиг - это вовсе не то же самое, что и целочисленное деление.
defunct
Цитата(777777 @ Jul 3 2009, 13:44) *
Так что сдвиг - это вовсе не то же самое, что и целочисленное деление.

Разница то с целочисленным делением в округлении только, которое при >> (как для положительных так и для отрицательных) делается всегда к меньшему.
777777
Цитата(defunct @ Jul 3 2009, 18:29) *
Разница то с целочисленным делением в округлении только,

При целочисленном делении не выполняется округления. Это строгая математическая операция, которая дает в результате частное и остаток.

Цитата(defunct @ Jul 3 2009, 18:29) *
которое при >> (как для положительных так и для отрицательных) делается всегда к меньшему.

При сдвиге тем более нет смысла говорить об округлении. Это - лишь сдвиг битов. И использовать его нужно только если требуются какие-то манипуляции с битами.

Впрочем, важно понимать, что деление и сдвиг не одно и то же и не использовать сдвиг для деления - оптимизирующий компилятор сам заменит его на сдвиг если это можно. А если нельзя - то не заменит!
Dreamer
Цитата
Если следовать стандарту, то получается, что отрицательные вообще нельзя сдвигать, так как результат непредсказуем. Тем не менее все компиляторы, имеющиеся у меня под рукой, выдали результат:

Результат предсказуем как грабли и прописан в стандарте wink.gif

В языке С для операции правого сдвига для знаковых типов есть определенное правило.
Если старший бит равен нулю, то левые поля заполняются нулями.
Если старший бит равен единице, то поля заполняются единицами.

Отсюда следствие - положительные числа сдвигаются "как есть", а отрицательные числа, поскольку имеют в старшем бите единицу, сдвигаются вправо с заполнением единицей.
Пример:
0х40 >> 2 == 0x10;
0xC0 >> 2 == 0xF0;

0хС0 в десятичном представлении для знакового типа - это -64, а 0xF0 - это -16.
Xenia
Цитата(Dreamer @ Jul 24 2009, 10:38) *
Отсюда следствие - положительные числа сдвигаются "как есть", а отрицательные числа, поскольку имеют в старшем бите единицу, сдвигаются вправо с заполнением единицей.
Пример:
0х40 >> 2 == 0x10;
0xC0 >> 2 == 0xF0;

С чего бы вдруг 0xC0 быть байтом? Запись 0xC0 означает лишь шестандцатеричную форму записи, но никак не ограничение одним байтом. Думаете, что 0x00C0 другое число? - Нет, то же самое!
Константы такого рода считаются, как int-значения, а не char. А int это обычно 2 байта, а не один. Причем даже для 8-битных микропроцессоров (AVR). Поэтому старший бит у этого числа нулевой. А потому
0xC0 >> 2 == 0x30;
Сергей Борщ
Цитата(Xenia @ Jul 24 2009, 10:53) *
Думаете, что 0x00C0 другое число? - Нет, то же самое!
Если речь шла о знаковой переменной размером 1 байт, то 0x00C0 - действительно другое число. Да, при выполнении арифметических операций стандарт требует приведения меньших типов к размерности (unsigned)int. Но правило приведения знаковых типов как раз требует расширения знакового бита на добавляемые старшие разряды. Так что "тем же" будет число 0xFFC0.
Dreamer
Цитата
С чего бы вдруг 0xC0 быть байтом?


С того, что его можно объявить как signed char. Мой пример именно для signed char.

Цитата
Константы такого рода считаются, как int-значения, а не char. А int это обычно 2 байта, а не один.

Не-а. Обычно длина типа int соответствует разрядности процессора, для которого сделан компилятор.
Для 32разрядных int будет 4 байта, для 16разрядных - 2 байта.

Константы, явно заданные в выражении, действительно являются типом int.
Я оговорился и не написал, что пример придназначен для типа signed char.

Цитата
А потому 0xC0 >> 2 == 0x30

Хорошо. Тогда пример специально для Вас: wink.gif

0хС000 >> 2 == 0xF000 (Конкретно для знакового 2байтового типа).




Вот вам еще пример кода:
Код
    char c = 0xC0;
    int i = c;
    i>>=2;

char и int в настройках компилятора стоят как знаковые.
Как вы думаете, чему будет равно i после выполнения кода? Думаете, 0x0030?
Не-а. Результат будет равен 0хFFF0.
wink.gif
Xenia
Если это char, да еще и знаковый, то возражений у меня нет. Я же имела в виду, что выражение
if (0xC0 >> 2 == 0xF0)
было бы расценено IAR-компилятором как ложное, а
if (0xC0 >> 2 == 0x30)
как истинное.

P.S. IAR-компилятор для AVR8 понимает int, как двухбайтное знаковое целое, несмотря на то, что регистры имеют разрядность один байт.
Dreamer
Цитата
Если это char, да еще и знаковый, то возражений у меня нет. Я же имела в виду, что выражение
if (0xC0 >> 2 == 0xF0)
было бы расценено IAR-компилятором как ложное


Ну у меня возражений тоже нет wink.gif
Я написал-то пример не к словам о приведению типов, а к высказанному в этой ветке мнению, что сдвиг вправо у отрицательных чисел непредсказуем. А правило тем-не менее есть и оно стандартное.

Цитата
P.S. IAR-компилятор для AVR8 понимает int, как двухбайтное знаковое целое, несмотря на то, что регистры имеют разрядность один байт.

Да, для Keil51 у int тоже два байта, хотя процессор там восьмиразрядный.
Это не вяжется с тем правилом, котрое я процетировал, но я его видел в литературе очень много раз.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.