C вопрос: добавление битов в целые числа без знака и побитовые операции (C89)
У меня есть много кода, который выполняет побитовые операции с целыми целыми знаками. Я написал свой код с предположением, что эти операции были на целых числах с фиксированной шириной без каких-либо битов заполнения. Например, массив из 32-битных беззнаковых целых чисел, из которых все 32 бита доступны для каждого целого.
Я хочу сделать мой код более портативным, и я сосредоточен на том, чтобы убедиться, что я совместим с C89 (в данном случае). Одна из проблем, с которыми я столкнулась, - это возможные пронумерованные целые числа. Возьмите этот крайний пример, взятый из руководства GMP:
Однако на векторных системах Cray можно отметить, что short и int всегда хранятся в 8 байтах (и с указанием sizeof, указывающим это), но используют только 32 или 46 бит. Функция ногтей может учитывать это, передавая, например, 8 * sizeof (int) -INT_BIT.
Я также читал об этом типе дополнений в других местах. Я на самом деле прочитал сообщение о SO прошлой ночью (простите меня, у меня нет ссылки, и я собираюсь привести что-то похожее из памяти), где, если у вас есть, скажем, двойник с 60 полезными битами, остальные 4 могут можно использовать для заполнения, и эти биты дополнений могут служить некоторым внутренним целям, поэтому они не могут быть изменены.
Итак, скажем, например, мой код компилируется на платформе, где тип unsigned int имеет размер 4 байта, каждый байт составляет 8 бит, однако наиболее значимые 2 бита являются битами заполнения. Будет ли UINT_MAX в этом случае 0x3FFFFFFF (1073741823)?
#include <stdio.h>
#include <stdlib.h>
/* padding bits represented by underscores */
int main( int argc, char **argv )
{
unsigned int a = 0x2AAAAAAA; /* __101010101010101010101010101010 */
unsigned int b = 0x15555555; /* __010101010101010101010101010101 */
unsigned int c = a ^ b; /* ?? __111111111111111111111111111111 */
unsigned int d = c << 5; /* ?? __111111111111111111111111100000 */
unsigned int e = d >> 5; /* ?? __000001111111111111111111111111 */
printf( "a: %X\nb: %X\nc: %X\nd: %X\ne: %X\n", a, b, c, d, e );
return 0;
}
безопасно ли XOR два целых числа с битами заполнения?
не будет ли я XOR любыми битами заполнения?
Я не могу найти это поведение, описанное в C89.
кроме того, c var гарантированно будет 0x3FFFFFFF, или если, например, два бита дополнений были включены в или b, то c будет 0xFFFFFFFF?
тот же вопрос с d и e. Могу ли я манипулировать битами заполнения путем смещения?
Я ожидал бы увидеть это ниже, предполагая 32 бита с двумя наиболее значимыми битами, используемыми для заполнения, но я хочу знать, гарантировано ли что-то вроде этого:
a: 2AAAAAAA
b: 15555555
c: 3FFFFFFF
d: 3FFFFFE0
e: 01FFFFFF
Также биты заполнения всегда являются самыми значимыми битами или могут быть наименее значимыми битами?
Спасибо, ребята,
EDIT 12/19/2010 5 вечера EST: Кристоф ответил на мой вопрос. Спасибо!
Я также спросил (выше), являются ли биты заполнения самыми важными битами. Это цитируется в обосновании стандарта C99, а ответ - нет. Я играю в это безопасно и предполагаю то же самое для C89. Вот конкретно то, что объясняет C99 для §6.2.6.2 (представление целых типов):
Биты заполнения являются доступными для пользователя в неподписанном целочисленном типе. Например, предположим, что машина использует пару 16-разрядных шорт (каждая со своим собственным битом знака) для создания 32-битного int, а бит знака младшего короткого замыкания игнорируется при использовании в этом 32-битном int. Затем, как 32-битный подписанный int, есть бит заполнения (в середине 32 бит), который игнорируется при определении значения 32-разрядного подписанного int. Но если этот 32-битный элемент рассматривается как 32-разрядный беззнаковый int, то этот бит заполнения отображается для программы пользователя. Комитету С было сказано, что есть машина, которая работает таким образом, и это одна из причин того, что биты дополнений были добавлены в C99.
В сносках 44 и 45 упоминаются, что биты четности могут быть битами заполнения. Комитет не знает о каких-либо машинах с доступными пользователем битами четности в пределах целого числа. Поэтому комитет не знает о каких-либо машинах, которые обрабатывают биты четности как биты заполнения.
EDIT 12/28/2010 3PM EST. Я нашел интересную дискуссию о comp.lang.c несколько месяцев назад.
Побитовые эффекты оператора на битах заполнения (читатель VelocityReviews)
Побитовые эффекты оператора на битах заполнения (альтернативная ссылка Google Groups)
Один момент, сделанный Dietmar, который я нашел интересным:
Отметим, что биты заполнения не нужны для существования ловушных представлений; также будут выполняться комбинации битов значений, которые не представляют собой значение типа объекта.
Ответы
Ответ 1
Побитовые операции (например, арифметические операции) работают с значениями и игнорируют заполнение. Реализация может или не может изменять биты заполнения (или использовать их внутренне, например, как биты четности), но портативный код C никогда не сможет обнаружить это. Любое значение (включая UINT_MAX
) не будет включать дополнение.
Если целочисленное дополнение может привести к проблемам, это если вы используете такие вещи, как sizeof (int) * CHAR_BIT
, а затем попробуйте использовать сдвиги для доступа ко всем этим битам. Если вы хотите быть переносимым, используйте только (unsigned
) char
, целые числа фиксированного размера (дополнение C99) или программно определить количество битов значений. Это можно сделать во время компиляции с препроцессором, сравнив UINT_MAX
с полномочиями 2 или во время выполнения с помощью бит-операций.
изменить
В C90 не упоминается целочисленное дополнение, но, насколько я могу судить, "невидимые" предшествующие или завершающие биты целочисленного бита не должны нарушать стандарт (я не прошел через все соответствующие разделы, чтобы убедиться, что это действительно, случай); там probaby - проблемы со смешанными дополнениями и битами значения, упомянутые в обосновании C99, поскольку в противном случае стандарт не нужно было бы изменять.
Что касается значения доступного для пользователя: биты заполнения доступны, поскольку вы можете в любом случае получить бит foo
(включая дополнение) с помощью бит-операций на ((unsigned char *)&foo)[…]
. Однако будьте осторожны при изменении битов дополнений: результат не изменит значение целого числа, но, тем не менее, может создать ловушку-представление. В случае C90 это неявно неопределено (как и вообще не упоминается), в случае C99, оно определяется реализацией.
Это не то, о чем говорилось в цитате с обоснованием: цитированная архитектура представляет 32-битные целые числа через два 16-битных целых числа. В случае неподписанных типов результирующее целое число имеет 32 бита значений и точность 32; в случае знаковых целых чисел он имеет только 31 бита значений и точность 30: один из знаковых битов 16-разрядных целых чисел используется в качестве знакового бита 32-разрядного целого числа, другой игнорируется, тем самым создавая бит заполнения, окруженный битами значения. Теперь, если вы получаете доступ к 32-разрядному значению целого числа как целое без знака (которое явно разрешено и не нарушает правила псевдонимов C99), бит дополнений становится битом значения, доступным для пользователя.