Структурное выравнивание безопасного использования

Я новичок в компании, где используется следующее использование структуры:

#include <stdio.h>
#include <string.h>

typedef unsigned char uint8;
typedef signed char int8;
typedef unsigned short int uint16;
typedef signed short int int16;

typedef struct padded_t {
    int8 array0[11];
    int8 array1[4];
    uint16 len2;
    int8 array2[25];
    uint16 len3;
    int8 array3[6];
    // many more len/arrays follow
} padded_t;

int main(int argc, char** argv)
{
    padded_t foo;
    memset((void*)&foo, 0, sizeof(padded_t));

    int8* str = "foobar";
    int16 size = (int16)strlen(str);

    int8* ptr = (int8*)&foo.len2;

    // please note that the memcpy references only the pointer to len
    // are the following couple of lines safe?
    memcpy ((void*)ptr, &size, 2);
    memcpy ((void*)&ptr[2], (void*)str, size);

    printf("%d\n", foo.len2);
    printf("%s\n", foo.array2);

    return 0;
}

Я знаю кое-что о выравнивании и заполнении, и я предполагаю, что компилятор (gnu C99 для устройства ARM9) добавит некоторые отступы для выравнивания структуры.

Но безопасен ли этот код?
Насколько я понимаю, это будет безопасно, пока uint16 len за переменными сразу же следуют int8 array[] переменные независимо от других членов структуры.

Будет ли он только добавлять отступы перед uint16, если размер перед ним нечетный?
Это использование правильно? И что более важно, это безопасно?

2 ответа

Решение

Но безопасен ли этот код?

Насколько я понимаю, это будет безопасно, пока uint16 len за переменными сразу же следуют int8 array[] переменные независимо от других членов структуры.

Это небезопасно в том смысле, что компиляторам разрешено свободно вводить любое количество отступов между или после элементов структуры, поэтому вы не можете быть уверены, что &ptr[2] указывает на первый байт foo.array2, При условии, что uint16 на самом деле он имеет ширину в два байта, но (это никак не гарантируется языком) вы можете быть уверены, что если size меньше 25, то

memcpy ((void*)&ptr[2], (void*)str, size);

не будет изменять ни один из байтов foo.len2 ни последний байт foo.array2, поскольку foo ранее был заполнен нулями, это уйдет foo.array2 как правильно завершенная строка C. Таким образом, можно безопасно распечатать его printf(), С другой стороны, C не гарантирует, что результат этого будет таким же, как результат печати str,

Будет ли он только добавлять отступы перед uint16, если размер перед ним нечетный?

Это на усмотрение компилятора. На это могут повлиять прагмы, параметры командной строки, параметры конфигурации, языковые расширения (хотя ни один из них не используется в примере), целевая архитектура или что-либо еще, что компилятор хочет использовать для принятия таких решений.

Это использование правильно?

Насколько я могу судить, программа соответствует, если вы это имеете в виду.

И что более важно, это безопасно?

Вывод программы не предсказуем только из ее кода, поэтому в этом смысле нет, он небезопасен.

Вам не нужно писать код, который работает в каждой системе. Что вы должны сделать, это написать код, который имеет предсказуемое поведение. Либо он работает так, как задумано, либо, если ваши предположения не выполняются, статическое утверждение прерывает компиляцию.

Второй memcpy линия не может этого сделать. Предполагается, что offsetof(struct padded_t, len2) + 2 == offsetof(struct padded_t, array2), Предположение, которое часто будет иметь место, но совершенно глупо.

Почему бы просто не написать

foo.len2 = strlen(str);
memcpy (foo.array2, str, foo.len2);
//possibly, foo.array2[foo.len2] = '\0';

Код читабелен. Нет ненужных переменных. Нет ненужных бросков. Нет непредсказуемого поведения. Оригинал не похож на код, из которого вы многому научитесь, беспорядок не похож на то, что я ожидаю, что кто-то опытный в C напишет.

Чтобы ответить на ваш комментарий, упаковывать их - неправильное решение. Потому что это сместит членов и просто откроет еще одну банку глистов.

Также будьте осторожны с пользовательскими определениями типов фиксированного размера. Однажды я получил удовольствие от отладки typedef char int8; в системе, которая имела chars unsigned...

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