"char *" с необычным размером слова памяти (архитектура Knuth MIX)
Оригинальная архитектура MIX содержит 6-битные байты, а память адресуется как 31-битные слова (5 байтов и бит знака). В качестве мысленного упражнения мне интересно, как язык C может функционировать в этой среде, учитывая:
- char имеет не менее 8 бит (приложение E спецификации C99).
- В разделе 8.2 раздела "6.2.2.3" ( "Указатели" ) говорится: "Когда указатель на объект преобразуется в указатель на тип символа, результат указывает на младший адресный байт объекта. Последовательные приращения результат, вплоть до размера объекта, выводит указатели на оставшиеся байты объекта". Моя интерпретация этого требования заключается в том, что он поддерживает "memcpy (& dst_obj, & src_obj, sizeof (src_obj))".
Подходы, о которых я могу думать:
- Сделайте char 31 бит, поэтому косвенное обращение через "char *" - это простой доступ к памяти. Но это делает строки расточительными (и означает, что они не совместимы с POSIX, поскольку, по-видимому, требуется 8-битные символы).
- Упакуйте три 8-битных символа в одно слово, с 7 игнорируемыми битами: "char *" может состоять из слова и индекса char внутри него. Однако это, похоже, нарушает 6.3.2.3, то есть memcpy() обязательно пропускает игнорируемые биты (которые, вероятно, имеют смысл для реального типа объекта)
- Полностью упаковать символы в слова, например. четвертый 8 бит char будет иметь 7 бит в слове 0 и один бит в слове 1. Однако это, по-видимому, требует, чтобы все объекты имели размер в 8-битных символах, например. "uint31_t" не может быть объявлен в соответствии с длиной слова, так как это снова имеет проблему memcpy().
Таким образом, похоже, что первый (расточительный) вариант использования 31-битных символов со всеми объектами, размер которых равен кратному char - правильно ли я читаю его таким образом?
Ответы
Ответ 1
Я согласен с тем, что C на архитектуре MIX может быть болью для реализации, и, хотя он и не является юристом по языку, мне кажется, что вы правы, чтобы указать свой подход 1. как единственный соответствующий.
Во всяком случае, космические отходы для струн - это наименьшая из ваших проблем: вы можете обойти это, обратившись к решению, более старому, чем C: каждый char
представляет несколько букв. Для архитектуры MIX вы могли бы, например, разработать 7-битное кодирование и упаковать 4 буквы в каждый char:
char hi[4];
hi[0] = 'hell';
hi[1] = 'o, w';
hi[2] = 'orld';
hi[3] = '\0';
printf("%s", hi);
// Whoops, we forgot the exclamation mark
putchar('!\n');
Эта реализация кажется странной, но, согласно Википедии, она использовалась в первой программе Hello World. Я взглянул на Стандарт и не нашел ничего, что помешало бы ему, даже в C11. Особенно в разделе 6.4.4.4 допускается кодирование буквенных символов и строк конкретными способами реализации.
EDIT:
Это не помогает в случае других трудностей, главным из которых является невозможность использовать большую часть вашей машины возможных инструкций, поскольку вы не можете адресовать одиночные байты с родными типами C. Однако вы можете использовать битовые поля следующим образом:
typedef struct _bytes {
unsigned int sign : 1;
unsigned int byte1 : 6; // EDIT: bitfields must be
unsigned int byte2 : 6; // declared as ints in standard C
unsigned int byte3 : 6;
unsigned int byte4 : 6;
unsigned int byte5 : 6;
} bytes;
typedef union _native_type {
char as_word;
int as_int; // int = char; useful for standard library functions, etc.
bytes as_bytes;
} native_type;
Обратите внимание, что в С++ из-за предложения в правилах строгого сглаживания вы должны быть осторожны, чтобы всегда обращаться к элементу char
между доступом к int
и одним к bytes
, так как этот фрагмент:
native_type a, b;
a.as_int = 0xC11BABE;
b.as_bytes.byte4 = a.as_bytes.byte4; // Whoops
приведет к поведению undefined: подробнее см. здесь.
Ответ 2
Самый практичный подход, вероятно, заключался бы в том, чтобы сделать int
равным 30 битам и иметь char
равным 10 или 15. Использование десяти бит для char
позволило бы упаковать текст ASCII более плотно, но увеличит стоимость индексации в массивы char
из-за требуемого разделения на три. Хранение текста в Юникоде может быть довольно эффективным с 10- или 15-байтным char
. С 15-байтовым char
около 30720 кодовых точек будет занимать 15 бит, а остаток займет 30. С 10-байтовым char
128 кодовых точек будут принимать 10 бит, а 65408 - 20, а остальные будут принимать 30.
Чтобы уменьшить стоимость деления на 3, может оказаться полезным, чтобы каждый char*
содержал два слова; один будет идентифицировать слово, содержащее символ, а другой будет идентифицировать смещение в символах с самого начала этого слова. Добавление постоянного смещения к указателю, который, как было известно, нормирован, может использовать код, например:
p += 5; // Becomes...
if (p.offset) { p.offset=2; p.base+=1; }
else { p.offset--; p.base+=2; }
Неплохо, но это позволит избежать необходимости "деления".