Как правильно добавить 1 байт к указателю на C / C++?
Я использую этот код для перемещения указателя на 1 байт, но я чувствую что-то неясное..
int* a = (int*)malloc(sizeof(int));
void* b = ((char*)a)+1;
char
- 1 байт, но не определен для цели байтовой операции. Я считаю, что есть другой способ сделать это байт операции. Что за правильно способ байт операции?
PS. Я изменил пример кода, чтобы быть действительным. Теперь он скомпилирован как C++ с Clang.
6 ответов
Я думаю, вы смущены:
char
- 1 байт, но не определен для цели байтовой операции. Я считаю, что есть другой способ сделать это байт операции. Что за правильно способ байт операции?
что именно вы ожидаете byte
значит, если не то же самое, что char
означает?
В C и в C++, chars are байт. By определение. Что такое не дело в том, что байт обязательно октетов. Байт содержит не менее 8 бит. Есть нет гарантировать, что данная платформа даже делает это возможно для ссылки на блок памяти, который составляет ровно 8 бит.
в C99, у вас есть stdint.h
заголовок, который содержит int8_t
и uint8_t
типы, которые гарантированно будут 8 битами (и, как правило, просто typedefs для char). Помимо этого, нет реальной поддержки уровня языка для байтов в C или C++, на самом деле стандарт выходит из своего пути, чтобы сказать, что sizeof
например, в единицах char
(а не байт). Существует также CHAR_BIT
макрос, который сообщает вам количество бит в байте, на некоторых платформах char
был 9bits например. Из конечно, я предполагаю, что под байтом вы подразумеваете октет.
((char*)a)++
Это один из тех злых расширений Microsoft. Выражение приведения указателя является rvalue, но в соответствии с правилами языка C++ оператор инкремента работает только на lvalues. g++ отказывается компилировать это.
вы не должны делать этого. многие архитектуры имеют требования к выравниванию данных. Например, разыменование указателя, не выровненного по границе слова на машине SPARC, приведет к сбою программы с ошибкой шины (SIGBUS
).
портативный способ разделить ваши int
в байты с помощью побитовых операций (предполагая 8-битные байты):
uint8_t b3 = 0x12, b2 = 0x34, b1 = 0x56, b0 = 0x78;
uint32_t a;
a = (b3 << 24) | (b2 << 16) | (b1 << 8) | b0;
printf("%08X\r\n", a);
a = 0x89ABCDEF;
b3 = (a >> 24) & 0xFF;
b2 = (a >> 16) & 0xFF;
b1 = (a >> 8) & 0xFF;
b0 = a & 0xFF;
printf("%02X%02X%02X%02X\r\n", b3, b2, b1, b0);
то же самое можно не-переносимым достигнуто с тип каламбурно трюки с помощью union
s, например:
typedef union {
uint32_t val;
uint8_t bytes[4];
} DWORD_A;
typedef union {
uint32_t val;
struct {
unsigned b0:8;
unsigned b1:8;
unsigned b2:8;
unsigned b3:8;
};
} DWORD_B;
однако, этот метод приводит к реализации определенного поведения и, таким образом, составляет не рекомендуется:
- порядок байтов зависит от endianness хост-системы.
- упаковка битовых полей не переносится.
- добавлена сложность / накладные расходы из-за кода, генерируемого компилятором для предотвращения несоосного доступа.
- проблемы выравнивания, по реализациям, которые не мешайте им.
((char*&)a)++;
или:
a = (int*)((char*)a+1);
надеюсь, вы точно знаете, что делаете. Во - первых, вы заканчиваете - по определению-несогласованным указателем int. В зависимости от архитектуры и ОС это может быть проблемой.
plz, используйте void*
int g = 10;
int *a = &g;
printf("a : %p\n",a);
printf("a : %p\n", ++a);
printf("a : %p\n", (void*)((char*)a+1));
a: 0xbfae35dc a : 0xbfae35e0 a: 0xbfae35e1