Арифметика указателя с точностью до байта в C, когда sizeof(char) != 1
-
16-09-2019 - |
Вопрос
Как можно портативно выполнять арифметику указателей с точностью до одного байта?
Имейте в виду, что:
char
не равен 1 байту на всех платформахsizeof(void) == 1
доступен только как расширение в GCC- Хотя на некоторых платформах могут быть ограничения на выравнивание указателя по разыменованию указателя, арифметика все равно может требовать более тонкой детализации, чем размер наименьшего фундаментального типа POD.
Решение
Ваше предположение ошибочно - sizeof(char)
является определенный быть 1 везде.
Из Стандарт C99 (TC3), в разделе 6.5.3.4 («Оператор sizeof»):
(пункт 2)
Оператор Size Of дает размер (в байтах) своего операнда, который может быть выражением или в скобках названия типа.
(пункт 3)
При применении к операнду, который имеет тип char, unsigned char или подписанный char, (или квалифицированная версия), результат составляет 1.
Когда они взяты вместе, становится ясно, что в C, каким бы ни был размер символа, этот размер является «байтом» (даже если он превышает 8 бит на какой-то конкретной платформе).
А char
следовательно, это наименьший адресуемый тип.Если вам нужно адресовать в единицах меньше, чем char
, ваш единственный выбор - прочитать char
одновременно и используйте побитовые операторы для маскировки частей char
что ты хочешь.
Другие советы
По стандарту char
— это наименьший адресуемый фрагмент данных.С большей точностью вы просто не сможете адресоваться — придется делать упаковку/распаковку вручную.
sizeof(char)
гарантированно будет 1
по стандарту C.Даже если char
использует 9 бит или более.
Итак, вы можете сделать:
type *pt;
unsigned char *pc = (unsigned char *)pt;
И используйте pc
для арифметики. Назначение pc
к pt
Однако использование приведенного выше приведения является неопределенным поведением по стандарту C.
Если char
имеет ширину более 8 бит, вы не можете выполнять арифметику указателей с точностью до байта в портативном (ANSI/ISO) C.Здесь, по байт, Я имею в виду 8 бит.Это связано с тем, что сам фундаментальный тип больше 8 бит.
Наведите указатель на uintptr_t
.Это будет целое число без знака размером с указатель.Теперь выполните арифметические действия, а затем приведите результат обратно к указателю того типа, который вы хотите разыменовать.
(Обратите внимание, что intptr_t
подписано, а это обычно НЕ то, что вам нужно!Безопаснее придерживаться uintptr_t
если у вас нет веских причин не делать этого!)
Я не понимаю, что ты пытаешься сказать sizeof(void)
быть 1 в GCC.Пока печатаете char
теоретически может состоять из более чем 1 базового машинного байта на языке C sizeof(char)
равно 1 и всегда ровно 1.Другими словами, с точки зрения языка C, char
всегда равен 1 «байту» (C-байт, а не машинный байт).Как только вы это поймете, вы поймете и это. sizeof(void)
Быть 1 в GCC вам никак не поможет.В GCC арифметика указателей на void *
указатели работают точно так же, как арифметика указателей на char *
указатели, а это значит, что если на какой-то платформе char *
тогда тебе не подходит void *
у тебя тоже не пойдет.
Если на какой-то платформе char
объекты состоят из нескольких машинных байтов, и это единственный способ получить доступ к меньшим единицам памяти, чем полная char
Целью было бы использование побитовых операций для «извлечения» и «изменения» необходимых частей полного char
объект.Язык C не предлагает возможности напрямую обращаться к чему-либо меньшему, чем char
.Снова char
всегда является C-байтом.
Стандарт C99 определяет uint8_t длиной в один байт.Если компилятор не поддерживает этот тип, вы можете определить его с помощью typedef.Конечно, вам понадобится другое определение, в зависимости от платформы и/или компилятора.Объедините все в заголовочный файл и используйте везде.