Как преобразовать структуру в массив символов в C

Я пытаюсь преобразовать структуру в массив символов для отправки по сети. Однако при этом я получаю странный вывод из массива char.

#include <stdio.h>

struct x
{
   int x;
} __attribute__((packed));


int main()
{
   struct x a;
   a.x=127;
   char *b = (char *)&a;
   int i;
   for (i=0; i<4; i++)
      printf("%02x ", b[i]);
   printf("\n");
   for (i=0; i<4; i++)
      printf("%d ", b[i]);
   printf("\n");
   return 0;
}

Вот результат для различных значений a.x (на X86 с использованием gcc):
127:
7f 00 00 00
127 0 0 0

128:
ffffff80 00 00 00
-128 0 0 0

255:
ffffffff 00 00 00
-1 0 0 0

256:
00 01 00 00
0 1 0 0

Я понимаю значения 127 и 256, но почему числа меняются при переходе на 128? Почему бы просто не быть: 80 00 00 00 128 0 0 0

Я что-то забываю сделать в процессе преобразования или что-то забываю о целочисленном представлении?

* Примечание: это всего лишь небольшая тестовая программа. В реальной программе у меня больше структуры, лучшие имена переменных, и я конвертирую их в little-endian.
* Обновлено: форматирование

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
9
0
40 027
10
Перейти к ответу Данный вопрос помечен как решенный

Ответы 10

char - это тип со знаком; поэтому с дополнением до двух 0x80 равно -128 для 8-битного целого числа (т. е. байта)

char - это подписанный тип, поэтому то, что вы видите, является представлением с двумя комплиментами, приведение к (unsigned char *) исправит это (Роуленд просто победил меня).

Кстати, вы можете изменить

for (i=0; i<4; i++) {
//...
}

к

for (i=0; i<sizeof(x); i++) {
//...
}

char не всегда подписывается. подписанный символ подписан. знак char зависит от компилятора. в любом случае char, signed char и unsigned char - это три разных типа.

Johannes Schaub - litb 12.01.2009 00:12

"char", очевидно, подписан в этом контексте, потому что расширение знака происходит, когда параметр передается в printf в стеке.

dreamlax 12.01.2009 00:16

dreamlax, действительно, его ответ в порядке :) просто хотел сказать им, что в другой системе результат вполне может быть другим (неотрицательным), потому что char также может быть беззнаковым. это зависит от компилятора.

Johannes Schaub - litb 12.01.2009 00:24

Вы можете преобразовать в массив беззнаковых символов.

Обработка вашей структуры, как если бы это был массив символов, - это поведение undefined. Чтобы отправить его по сети, используйте вместо этого правильную сериализацию. Это неприятно для C++ и тем более для C, но это единственный способ, которым ваше приложение будет работать независимо от машин, читающих и пишущих.

http://en.wikipedia.org/wiki/Serialization#C

Ответ принят как подходящий

Спецификатор формата x сам по себе говорит, что аргумент - это int, а поскольку число отрицательное, printf требует восемь символов, чтобы показать все четыре ненулевых байта значения размера int. Модификатор 0 указывает заполнить вывод нулями, а модификатор 2 говорит, что вывод минимум должен состоять из двух символов. Насколько я могу судить, printf не позволяет указать ширину максимум, за исключением строк.

Итак, вы передаете только char, поэтому голый x сообщает функции использовать полный int, который был передан вместо этого - из-за повышения аргумента по умолчанию для параметров «...». Попробуйте использовать модификатор hh, чтобы функция воспринимала аргумент как просто char:

printf("%02hhx", b[i]);

Вы видите знак, сохраняющий преобразование из char в int. Такое поведение связано с тем, что в вашей системе char подписан (Примечание: char не подписан во всех системах). Это приведет к отрицательным значениям, если битовый шаблон уступит место отрицательному значению для char. Повышение такого char до int сохранит знак, и int тоже будет отрицательным. Обратите внимание, что даже если вы не укажете (int) явно, компилятор автоматически переведет этот символ в int при переходе в printf. Решение состоит в том, чтобы сначала преобразовать ваше значение в unsigned char:

for (i=0; i<4; i++)
   printf("%02x ", (unsigned char)b[i]);

В качестве альтернативы вы можете использовать unsigned char* с самого начала:

unsigned char *b = (unsigned char *)&a;

И тогда вам не нужно никакого приведения во время печати с помощью printf.

Преобразование вашей структуры в символы или байты так, как вы это делаете, приведет к проблемам, когда вы попытаетесь сделать ее нейтральной в сети. Почему бы не решить эту проблему сейчас? Вы можете использовать множество различных техник, и все они, вероятно, будут более «переносимыми», чем то, что вы пытаетесь сделать. Например:

  • Отправка числовых данных по сети машинно-нейтральным способом уже давно рассматривается в мире POSIX / Unix с помощью функций htonl, htons, ntohl и ntohs. См., Например, страницу руководства Байтердер (3) в системе FreeBSD или Linux.
  • Также вполне приемлемо преобразование данных в полностью нейтральное представление и обратно, такое как JSON. Количество времени, которое ваши программы тратят на преобразование данных между JSON и собственными формами, скорее всего, бледнеет по сравнению с задержками передачи данных по сети.

Если у вас нет убедительных измерений очень, показывающих, что каждый октет драгоценен, не делай этого. Используйте читаемый протокол ASCII, такой как SMTP, NNTP, или один из многих других прекрасных Интернет-протоколов, кодифицированных IETF.

Если вам действительно нужен двоичный формат, по-прежнему небезопасно просто выталкивать байты в структуру, потому что порядок байтов, базовые размеры или ограничения выравнивания могут отличаться от хоста к хосту. Вы должны спроектировать свой протокол связи с использованием четко определенных размеров и использования четко определенного порядка байтов. Для вашей реализации либо используйте макросы, такие как ntohl(3), либо используйте сдвиг и маскирование, чтобы поместить байты в ваш поток. Что бы вы ни делали, убедитесь, что ваш код дает одинаковые результаты на хостах с прямым и обратным порядком байтов.

Проголосовал против и пометил, это не ответ на его вопрос; даже близко к ответу. Если люди спрашивают, как что-то делать правильно, вам действительно следует воздержаться от их внушения, стоит ли это делать или нет, особенно я категорически не согласен с вашими рекомендациями в первом абзаце, особенно если вы понятия не имеете, кто такой программист. действительно здесь делаешь.

Mecki 05.09.2011 17:16

Мы не можем запретить людям стрелять себе в ногу. Однако здесь сделан пункт относительно порядка байтов было, поэтому я отказываюсь от флагов. Хотя этот ответ не дал OP того, что они хотели, его полезно оставить. Возможно, частично неверное, не требует удаления. Если вы считаете, что это неправильно, используйте свои голоса. Здесь нет причин для вмешательства модератора.

Tim Post 05.09.2011 19:50

Подпись массива char не является корнем проблемы! (Это -a- проблема, но не единственная.)

Выравнивание! Это ключевое слово здесь. Вот почему вам НИКОГДА не следует обращаться со структурами как с необработанной памятью. Компиляторы (и различные флаги оптимизации), операционные системы и фазы луны - все это делает странные и захватывающие вещи с фактическим местоположением в памяти «соседних» полей в структуре. Например, если у вас есть структура с символом, за которым следует int, вся структура будет занимать ВОСЕМЬ байтов в памяти - char, 3 пустых, бесполезных байта, а затем 4 байта для int. Машина любит делать такие вещи, чтобы структуры могли аккуратно умещаться на страницах памяти и тому подобное.

Пройдите вводный курс машинной архитектуры в местном колледже. Между тем, сериализуйте правильно. Никогда не относитесь к структурам как к массивам символов.

Когда вы собираетесь отправить его, просто используйте:

(символ *) и CustomPacket

конвертировать. Работает для меня.

Другие вопросы по теме