Пропустить / избежать байтов выравнивания при вычислении контрольной суммы - PullRequest
3 голосов
/ 03 июля 2019

Есть ли общий способ пропустить / избежать байтов заполнения выравнивания при вычислении контрольной суммы структуры C?

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

Примечание. В основном меня интересует удобство обслуживания (добавление / удаление / изменение полей без необходимости обновления кода) и возможность повторного использования, а не переносимость (платформа очень специфична и вряд ли изменится).

В настоящее время я нашел несколько решений, но все они имеют недостатки:

  1. Упакуйте структуру (например, #pragma pack (1)). Недостаток: я предпочитаю избегать упаковкидля повышения производительности.
  2. Вычисление контрольной суммы по полю. Недостаток: при изменении структуры потребуется обновить код, и для него требуется больше кода (в зависимости от количества полей).
  3. Обнулить все байты структуры перед установкой значений. Недостаток: я не могу полностью гарантировать, что все структуры были изначально обнулены.
  4. Расположить поля структуры, чтобы избежать заполнения ивозможно добавьте фиктивные поля для заполнения отступов. Недостаток: Не является универсальным, структура должна быть тщательно перестроена при изменении структуры.

Есть ли лучший общий способ?

Пример расчета контрольной суммы:

unsigned int calcCheckSum(MyStruct* myStruct)
{
    unsigned int checkSum = 0; 
    unsigned char* bytes = (unsigned char*)myStruct;
    unsigned int byteCount = sizeof(MyStruct);
    for(int i = 0; i < byteCount; i++)
    {
        checkSum += bytes[i];
    }
    return checkSum;
}

Ответы [ 2 ]

5 голосов
/ 03 июля 2019

Существует ли общий способ пропустить / избежать байтов заполнения выравнивания при вычислении контрольной суммы структуры C?

Не существует такого механизма, на который может опираться строго соответствующая программа.Это следует из

  1. того факта, что реализациям C разрешено размещать структуры с произвольным заполнением после любого элемента или элементов по любой причине или без такового, а также

  2. тот факт, что

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

    ( C2011, 6.2.6.1/6)

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

На практике вполне вероятно, что любой из подходов, которые вы упомянулиn в вопросе выполнит работу, в которой позволяет реализация C и характер данных.Но только (2), вычисляющий элемент контрольной суммы по элементу, может использоваться строго соответствующей программой, и эта программа не является «общей», как я понимаю, для обозначения этого термина. Это то, что я выбрал бы .Если у вас есть много различных структур, требующих контрольных сумм, то, возможно, стоит развернуть генератор кода или макрокоманду, чтобы помочь вам в поддержании вещей.

С другой стороны, ваш самый надежный способ обеспечить общее контрольное суммированиеэто реализовать расширение для конкретной реализации, которое позволяет вам избегать структур, содержащих любые дополнения (your (1)).Обратите внимание, что это свяжет вас с конкретной реализацией C или реализациями, которые реализуют такое расширение совместимо, что оно может вообще не работать в некоторых системах (например, в тех, где неправильный доступ является серьезной ошибкой), и что это может снизить производительность надругие системы.

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

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

3 голосов
/ 03 июля 2019

Это звучит как проблема XY.Вычисление контрольной суммы для объекта C в памяти обычно не имеет смысла;результат зависит от реализации C (arch / ABI, если не от конкретного компилятора), и C не допускает отказоустойчивой модели программирования, способной обрабатывать возможность изменения значений объекта из-под вас из-за аппаратных сбоев памяти.ошибки безопасности.Контрольные суммы имеют смысл, главным образом, для сериализованных данных на диске или при передаче по сети, где вы хотите защититься от повреждения данных при хранении / передаче.И структуры C не предназначены для сериализации (хотя обычно за это злоупотребляют).Если вы напишите правильные процедуры сериализации, вы можете просто выполнить контрольную сумму в сериализованном потоке байтов.

...