Мне нужно выполнить арифметику по модулю 256 на C. Итак, могу ли я просто сделать
unsigned char i;
i++;
вместо
int i;
i=(i+1)%256;
Мне нужно выполнить арифметику по модулю 256 на C. Итак, могу ли я просто сделать
unsigned char i;
i++;
вместо
int i;
i=(i+1)%256;
Нет. Нет ничего, что могло бы гарантировать, что unsigned char
имеет восемь бит. Используйте uint8_t
из <stdint.h>
, и все будет в порядке. Для этого требуется реализация, поддерживающая stdint.h
: любой компилятор, совместимый с C99, поддерживает, но старые компиляторы могут не предоставлять этого.
Примечание: беззнаковая арифметика никогда не переполняется и ведет себя как «по модулю 2 ^ n». Подписанные арифметические переполнения с неопределенным поведением.
stdint.h
, когда они пытаются реализовать C ++ 11.
- person Alexandre C.; 06.02.2014
char
.
- person Alexandre C.; 06.02.2014
C
, действительно действительно старые реализации, например, довольно популярны во встраиваемом мире.
- person user2485710; 06.02.2014
intN_t
и uintN_t
являются необязательными, и их доступность не гарантируется. В любой системе, в которой нет 8-битных байтов (что, по общему признанию, вероятно, необычно), они, скорее всего, будут недоступны.
- person Crowman; 06.02.2014
sizeof(char) == sizeof(short) == sizef(int) == 1 == 16 bits
. Предположение, предложенное в этом ответе, там не сработает.
- person Jason R; 07.02.2014
Да, поведение обоих ваших примеров одинаково. См. C99 6.2.5 §9:
Вычисление с использованием беззнаковых операндов никогда не может быть переполнено, потому что результат, который не может быть представлен результирующим целочисленным типом без знака, уменьшается по модулю числа, которое на единицу больше наибольшего значения, которое может быть представлено результирующим типом. em >
unsigned char c = UCHAR_MAX;
c++;
В основном да, переполнения нет, но не потому, что c
имеет беззнаковый тип. Здесь есть скрытое преобразование c
в int
и целочисленное преобразование из int
в unsigned char
, и это прекрасно определено.
Например,
signed char c = SCHAR_MAX;
c++;
также не является неопределенным поведением, поскольку фактически эквивалентно:
c = (int) c + 1;
и преобразование из int
в signed char
определяется здесь реализацией (см. c99, 6.3.1.3p3 о преобразованиях целых чисел). Для упрощения предполагается CHAR_BIT == 8
.
Для получения дополнительной информации о приведенном выше примере я предлагаю прочитать этот пост:
"Маленькая функция C из ада"
http://blog.regehr.org/archives/482
unsigned char
не будет переполняться, но на машинах, где sizeof (int)
равно двум, произведение двух значений unsigned char
может переполниться. Исторически тот факт, что стандарт позволял компилятору на 16-битной машине делать с unsigned char x=255; x*=255;
все, что ему заблагорассудится, не рассматривался как дефект, потому что на практике даже компиляторы, которые использовали 16-битный int
, вели себя разумно. Тот факт, что такой код приводит к неопределенному поведению, рассматривался как теоретическая проблема, а не как возможность для компиляторов отрицать законы времени и причинности.
- person supercat; 25.07.2015
Очень вероятно, что да, но причины этого в данном случае на самом деле довольно сложные.
unsigned char i = 255;
i++;
i++
эквивалентно i = i + 1
.
(Ну, почти. i++
дает значение i
до, которое было увеличено, так что оно действительно эквивалентно (tmp=i; i = i + 1; tmp)
. Но поскольку в этом случае результат отбрасывается, это не вызывает никаких дополнительных проблем.)
Поскольку unsigned char
является узким типом, операнд unsigned char
оператора +
повышается до int
(при условии, что int
может содержать все возможные значения в диапазоне unsigned char
). Таким образом, если i == 255
и UCHAR_MAX == 255
, то результатом сложения будет 256
и имеет тип (подписанный) int
.
Присваивание неявно преобразует значение 256
из int
обратно в unsigned char
. Преобразование в беззнаковый тип четко определено; результат уменьшается по модулю MAX+1
, где MAX
- максимальное значение целевого беззнакового типа.
Если i
был объявлен как unsigned int
:
unsigned int i = UINT_MAX;
i++;
преобразования типов не будет, но семантика оператора +
для беззнаковых типов также определяет модуль сокращения MAX+1
.
Имейте в виду, что значение, присвоенное i
, математически эквивалентно (i+1) % UCHAR_MAX
. UCHAR_MAX
обычно 255
и гарантированно не менее 255
, но по закону может быть больше.
Может быть экзотическая система, в которой UCHAR_MAX
тоже нельзя хранить в подписанном int
объекте. Для этого потребуется UCHAR_MAX > INT_MAX
, что означает, что в системе должно быть не менее 16-битных байтов. В такой системе продвижение будет с unsigned char
до unsigned int
. Окончательный результат будет таким же. Вы вряд ли встретите такую систему. Я думаю, что есть реализации C для некоторых DSP с байтами больше 8 бит. Количество битов в байте определяется параметром CHAR_BIT
, определенным в <limits.h>
.
CHAR_BIT > 8
не обязательно означает UCHAR_MAX > INT_MAX
. Например, у вас могут быть CHAR_BIT == 16
и sizeof (int) == 2
, т.е. 16-битные байты и 32-битные int
s).
i++
эквивалентно созданию копии i
перед его приращением и использованию старой копии в выражении, в котором произошло пост-приращение. ++i
будет истинным эквивалентом _4 _ / _ 5_.
- person JAB; 06.02.2014
Есть еще одна альтернатива, о которой не упоминалось, если вы не хотите использовать другой тип данных.
unsigned int i;
// ...
i = (i+1) & 0xFF; // 0xFF == 255
Это работает, потому что элемент modulo == 2^n
, что означает, что диапазон будет [0, 2^n-1]
, и, таким образом, битовая маска легко сохранит значение в желаемом диапазоне. Возможно, этот метод будет не намного или менее эффективен, чем версия _4 _ / _ 5_, в зависимости от того, какую магию делает ваш компилятор за кулисами и как целевая система обрабатывает загрузки без слов (например, некоторые архитектуры RISC требуют дополнительных операции для загрузки значений, не превышающих размер слова). Это также предполагает, что ваш компилятор не обнаружит использование арифметики по модулю степени двойки для значений без знака и, конечно же, не заменит вам битовую маску, поскольку в подобных случаях использование модуля по модулю будет иметь большее семантическое значение (хотя с использованием этого как основа для вашего решения конечно не совсем портативная).
Преимущество этого метода заключается в том, что вы можете использовать его для степеней двойки, которые также не являются размером типа данных, например
i = (i+1) & 0x1FF; // i %= 512
i = (i+1) & 0x3FF; // i %= 1024
// etc.
Это должно работать нормально, потому что оно должно просто переполниться обратно до 0. Как было указано в комментарии к другому ответу, вы должны делать это только тогда, когда значение беззнаковое, так как вы можете получить неопределенное поведение со значением со знаком.
Однако, вероятно, лучше всего оставить это, используя модуль по модулю, потому что код будет лучше понят другими людьми, обслуживающими код, и умный компилятор может в любом случае выполнять эту оптимизацию, что в первую очередь может сделать ее бессмысленной. К тому же разница в производительности, вероятно, будет настолько мала, что вообще не будет иметь значения.
Он будет работать, если количество бит, которые вы используете для представления числа, равно количеству бит в двоичном (беззнаковом) представлении (100000000) делителя -1, что в данном случае: 9-1 = 8 (char)
stdint.h
, и вы можете просто использоватьuintXX_t
cplusplus.com/reference/cstdint - person user2485710   schedule 06.02.2014%
- это остаток, а не по модулю! хотя по модулю == остаток для положительных чисел. Обе части кода не эквивалентны, потому что первая дает вам модуль, тогда как вторая дает остаток (вашi
равенint
, а неunsigned int
). - person Grijesh Chauhan   schedule 06.02.2014%256
, поэтому вам не нужно выполнять микрооптимизацию. О написании понятного кода можно сказать МНОГОЕ - особенно, если это лабораторная работа, и она получает оценки! Спасибо за интересное обсуждение. - person Floris   schedule 06.02.2014//trick to eval mod 256
. Хорошая программа - это тот, кто знает более одного пути. код вроде iitians :) - person Grijesh Chauhan   schedule 07.02.2014