Я портирую приложение на платформу ARM в C, приложение также работает на x86 процессоре и должно быть обратно совместимым.
У меня теперь есть некоторые проблемы с переменным выравниванием. Я прочитал gcc руководство для __attribute__((aligned(4),packed))
Я интерпретирую то, что говорится, поскольку запуск структуры выровненный к 4-байтовой границе, и внутренняя часть остается нетронутой из-за упакованного оператора.
первоначально у меня было это, но иногда это помещается невыровненное с 4-байтовой границей.
typedef struct
{
unsigned int code;
unsigned int length;
unsigned int seq;
unsigned int request;
unsigned char nonce[16];
unsigned short crc;
} __attribute__((packed)) CHALLENGE;
таким образом, я изменяю его на это.
typedef struct
{
unsigned int code;
unsigned int length;
unsigned int seq;
unsigned int request;
unsigned char nonce[16];
unsigned short crc;
} __attribute__((aligned(4),packed)) CHALLENGE;
Понимание, которое я заявил ранее, кажется, является неправильным, поскольку и структура теперь выровненная к 4-байтовой границе, и и внутренние данные теперь выровненные к четырехбайтовой границе, но из-за порядка байтов, размер структуры увеличился в размере с 42 до 44 байтов. Этот размер очень важен, поскольку у нас есть другие приложения, которые зависят от структуры, являющейся 42 байтами.
Могли некоторые описывать мне, как выполнить операцию, которой я требую. Любая справка очень ценится.
Если вы полагаетесь на то, что sizeof (yourstruct)
составляет 42 байта, вас вот-вот укусит мир не -портативные предположения. Вы не сказали, для чего это нужно, но похоже, что порядок байтов в содержимом структуры также имеет значение, так что у вас также может быть несоответствие с x86.
В этой ситуации я думаю, что единственный верный способ справиться - использовать unsigned char [42]
в тех частях, где это важно. Начните с написания точной спецификации того, какие именно поля находятся в этом 42-байтовом блоке и какой порядок байтов, затем используйте это определение, чтобы написать некоторый код для перевода между этим и структурой, с которой вы можете взаимодействовать. Код, скорее всего, будет либо кодом сериализации «все сразу» (он же маршаллинг), либо кучей геттеров и сеттеров.
Какова ваша истинная цель?
Если вы имеете дело с данными, находящимися в файле или на проводе в определенном формате, вам следует написать несколько процедур маршалинга / сериализации, которые перемещают данные между структурой компилятора который представляет, как вы хотите работать с данными внутри программы, и массив символов, который имеет дело с тем, как данные выглядят в проводе / файле.
Тогда все, с чем нужно разобраться и, возможно, иметь специфичный для платформы код, - это процедуры маршалинга. И вы можете написать несколько хороших и неприятных модульных тестов, чтобы гарантировать, что маршалированные данные попадают в структуру и из нее должным образом, независимо от того, на какую платформу вам, возможно, придется переносить сегодня и в будущем.
Это одна из причин, почему чтение целых структур, а не членов, не работает, и его следует избегать.
В данном случае упаковка плюс выравнивание по 4 означает, что будет два байта прокладки. Это происходит потому, что размер должен быть совместим для хранения типа в массиве со всеми элементами, выровненными по 4.
Я представляю, что у вас получилось что-то вроде:
read(fd, &obj, sizeof obj)
Поскольку вы не хотите читать эти 2 байта прокладки, которые принадлежат разным данным, вы должны явно указать размер:
read(fd, &obj, 42)
Что вы можете сделать для поддержания:
typedef struct {
//...
enum { read_size = 42 };
} __attribute__((aligned(4),packed)) CHALLENGE;
// ...
read(fd, &obj, obj.read_size)
Или, если вы не можете использовать некоторые особенности C++ в вашем C:
typedef struct {
//...
} __attribute__((aligned(4),packed)) CHALLENGE;
enum { CHALLENGE_read_size = 42 };
// ...
read(fd, &obj, CHALLENGE_read_size)
При следующей возможности рефакторинга, я бы настоятельно предложил вам начать читать каждый член по отдельности, что может быть легко инкапсулировано в функции.
Я предполагаю, что проблема в том, что 42 не делится на 4, и поэтому они выходят из выравнивания, если вы поместите несколько из этих структур друг за другом (например, выделите память для нескольких из них, определив размер с помощью размер
). В этих случаях размер 44 заставляет выравнивать, как вы и просили. Однако, если внутреннее смещение каждого члена структуры остается неизменным, вы можете рассматривать 44-байтовую структуру, как если бы она составляла 42 байта (если вы позаботитесь о выравнивании любых следующих данных по правильной границе).
Можно попробовать поместить обе этих структур в один тип объединения и использовать только 42-байтовую версию внутри каждого такого объединения.
Поскольку я использую linux, я обнаружил, что echo 3 > /proc/cpu/alignment
выдает предупреждение и исправляет проблему выравнивания. Это обходной путь, но он очень помогает найти место, где структуры не выравниваются.