Почему структуры скопированы с помощью memcpy во встроенном системном коде?

В области встроенного программного обеспечения для копирования структуры того же типа люди не используют прямое назначение и делают это с помощью функции memcpy() или каждого элемента копирования.

позволяет иметь, например,

struct tag
{

int a;

int b;
};

struct tag exmple1 = {10,20};

struct tag exmple2;

для копирования exmple1 в exmple2.. вместо того, чтобы писать прямые

exmple2=exmple1;

люди используют

memcpy(exmple2,exmple1,sizeof(struct tag));

или

exmple2.a=exmple1.a; 
exmple2.b=exmple1.b;

почему????

Ответ 1

В том или ином случае нет ничего особенного в встроенных системах, которые делают это опасным, семантика языка одинакова для всех платформ.

C использовался во встроенных системах в течение многих лет и для ранних компиляторов C, прежде чем стандартизация ANSI/ISO не поддерживала прямое назначение структуры. Многие практикующие либо с той эпохи, либо учат тех, которые были или используют устаревший код, написанный такими практикующими. Вероятно, это корень сомнения, но это не проблема в реализации, совместимой с ISO. По некоторым целевым ресурсам с ограниченным ресурсом доступный компилятор может быть не полностью совместим с ISO по ряду причин, но я сомневаюсь, что эта функция будет затронута.

Одна проблема (применительно к встроенным и не встроенным) заключается в том, что при назначении структуры реализация не должна дублировать значение каких-либо битов дополнения undefined, поэтому, если вы выполнили назначение структуры, а затем выполнили a memcmp(), а не по отдельности, для проверки равенства, нет гарантии, что они будут равны. Однако, если вы выполняете memcpy(), любые биты дополнений будут скопированы так, чтобы сравнение memcmp() и член-член обеспечивало равенство.

Таким образом, безопаснее использовать memcpy() во всех случаях (а не только встраивать), но улучшение является незначительным и не способствует удобочитаемости. Было бы странной реализацией, которая не использовала бы самый простой метод назначения структуры, и это простой memcpy(), поэтому маловероятно, что возникнет теоретическое несоответствие.

Ответ 2

В вашем коде нет проблем, даже если вы пишете:

example2 = example1;

Но предположим, что в будущем определение struct изменится на:

struct tag
{
  int a[1000];
  int b;
};

Теперь, если вы выполняете оператор присваивания, как указано выше, то (некоторый из) компилятор может встроить код для байта байтом (или int by int). то есть.

example1.a[0] = example.a[0];
example1.a[1] = example.a[1];
example1.a[2] = example.a[2];
...

что приведет к раздуванию кода в вашем сегменте кода. Подобных ошибок памяти нетрудно найти. Вот почему люди используют memcpy.

[Тем не менее, я слышал, что современные компиляторы достаточно способны использовать memcpy внутри, когда такая команда встречается специально для POD.]

Ответ 3

Копирование C-структур через memcpy() часто используется программистами, которые учились C десятилетиями назад и не следовали процессу стандартизации с тех пор. Они просто не знают, что C поддерживает назначение структур (прямое назначение структуры недоступно во всех компиляторах pre-ANSI-C89).

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

Структуры также являются memcpy() некоторыми программистами-новичками, потому что они либо путают назначение структуры с назначением указателя структуры, либо просто используют memcpy() (они часто также используют memcpy(), где strcpy() будет быть более уместным).

Существует также анти-шаблон сравнения структуры memcmp(), который иногда цитируется некоторыми программистами для использования memcpy() вместо назначения структуры. Обоснование этого заключается в следующем: поскольку C автоматически не генерирует оператор == для структур, и запись функции сравнения пользовательских структур является утомительной, memcmp() используется для сравнения структур. На следующем шаге - во избежание различий в padding бит сравниваемых структур - memset(...,0,...) используется для инициализации всех структур (вместо с использованием синтаксиса инициализатора C99 или инициализации всех полей отдельно) и memcpy() используется для копирования структур! Поскольку memcpy() также копирует содержимое битов дополнения...

Но учтите, что это рассуждение ошибочно по нескольким причинам:

  • использование memcpy()/memcmp()/memset() представляет новые возможности ошибок - например, поставка неправильного размера
  • когда структура содержит целочисленные поля, упорядочивание под memcmp() изменяется между большими и мало-endian архитектурами
  • a char Поле массива размером n, которое 0 -терминировано в позиции x, должно также иметь все элементы после того, как позиция x обнулена в любое время - else 2 в противном случае равные структуры сравниваются неравномерно
  • из регистров в поле может также устанавливать соседние биты дополнений к значениям неравномерным 0, таким образом, после сравнений с структурами, не имеющими равных равными, дает неравный результат

Последняя точка лучше всего проиллюстрирована небольшим примером (предполагая архитектуру X):

struct S {
  int a;  // on X: sizeof(int) == 4
  char b; // on X: 24 padding bits are inserted after b
  int c;
};
typedef struct S S;
S s1;
memset(&s1, 0, sizeof(S));
s1.a = 0;
s1.b = 'a';
s1.c = 0;
S s2;
memcpy(&s2, &s1, sizeof(S));
assert(memcmp(&s1, &s2, sizeof(S)==0); // assertion is always true
s2.b = 'x';
assert(memcmp(&s1, &s2, sizeof(S)!=0); // assertion is always true
// some computation
char x = 'x'; // on X: 'x' is stored in a 32 bit register
              // as least significant byte
              // the other bytes contain previous data
s1.b = x;     // the complete register is copied
              // i.e. the higher 3 register bytes are the new
              // padding bits in s1
assert(memcmp(&s1, &s2, sizeof(S)==0); // assertion is not always true

Сбой последнего утверждения может зависеть от переупорядочения кода, изменения компилятора, изменения параметров компилятора и т.д.

Заключение

Как правило: для повышения корректности и переносимости кода используйте прямое назначение структуры (вместо memcpy()), синтаксис инициализации структуры C99 (вместо memset) и пользовательскую функцию сравнения (вместо memcmp()).

Ответ 4

В C люди, вероятно, это делают, потому что считают, что memcpy будет быстрее. Но я не думаю, что это правда. Оптимизация компилятора позаботится об этом.

В С++ он может также иметь различную семантику из-за пользовательского оператора присваивания и конструкторов копирования.

Ответ 5

В дополнение к тому, что другие написали несколько дополнительных точек:

  • Использование memcpy вместо простого назначения дает подсказку тому, кто поддерживает код, который может быть дорогостоящим. Использование memcpy в этих случаях улучшит понимание кода.

  • Встроенные системы часто записываются с учетом переносимости и производительности. Переносимость важна, потому что вы можете повторно использовать свой код, даже если CPU в оригинальном дизайне недоступен или если более дешевый микроконтроллер может выполнять ту же работу.

    В наши дни младшие микроконтроллеры приходят и уходят быстрее, чем разработчики компилятора могут наверстать упущенное, поэтому нередко работать с компиляторами, которые используют простой цикл байтовой копии вместо того, что оптимизировано для назначения структуры. С переходом на 32-битные ARM-ядра это не так для значительной части встроенных разработчиков. Тем не менее, есть много людей, которые строят продукты, которые нацелены на неясные 8 и 16-битные микроконтроллеры.

  • Memcpy, настроенная для конкретной платформы, может быть более оптимальной, чем то, что может генерировать компилятор. Например, на встроенных платформах, имеющих структуры во флэш-памяти, является общим. Чтение со вспышки происходит не так медленно, как запись на него, но она все еще намного медленнее обычной копии из ОЗУ в оперативную память. Оптимизированная функция memcpy может использовать DMA или специальные функции контроллера вспышки для ускорения процесса копирования.

Ответ 6

Это полная чушь. Используйте то, что вы предпочитаете. Самый простой способ:

exmple2=exmple1;

Ответ 7

Что бы вы ни делали, не делайте этого:

exmple2.a=exmple1.a; 
exmple2.b=exmple1.b;

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

Ответ 8

В некоторых реализациях способ, которым выполняется memcpy(), может отличаться от способа, которым будет выполняться "нормальное" назначение структуры, таким образом, что это может быть важно в некоторых узких контекстах. Например, один или другой структурный операнд может быть неровным, и компилятор может не знать об этом (например, одна область памяти может иметь внешнюю связь и быть определена в модуле, написанном на другом языке, который не имеет возможности обеспечить выравнивание). Использование объявления __packed было бы лучше, если бы компилятор поддерживал такие, но не все компиляторы.

Другая причина использования чего-то другого, кроме назначения структуры, может заключаться в том, что конкретная реализация memcpy может получить доступ к своим операндам в последовательности, которая будет корректно работать с определенными типами источника или назначения volatile, тогда как это назначение структуры реализации может использовать другая последовательность, которая не сработает. Это, как правило, не является веской причиной для использования memcpy, однако, поскольку помимо проблемы выравнивания (для которой memcpy требуется правильно обрабатывать в любом случае), спецификации для memcpy не обещают многого о том, как операция будут выполнены. Было бы лучше использовать специально написанную процедуру, которая выполняла бы операции точно по мере необходимости (например, если цель - это часть аппаратного обеспечения, которая должна иметь 4 байта данных структуры, записанных с использованием четырех 8-битных записей, а не 32 -битная запись, нужно написать процедуру, которая делает это, а не надеяться, что никакая будущая версия memcpy не решит "оптимизировать" операцию).

Третья причина использования memcpy в некоторых случаях будет заключаться в том, что компиляторы часто выполняют небольшие структурные назначения, используя прямую последовательность нагрузок и хранилищ, а не используя библиотечную процедуру. На некоторых контроллерах количество требуемого кода может варьироваться в зависимости от того, где структуры расположены в памяти, до такой степени, что последовательность загрузки/хранения может оказаться больше, чем вызов memcpy. Например, на контроллере PICmicro с 1K словами с кодовым пространством и 192 байтами ОЗУ обработка 4-байтной структуры из банка 1 в банк 0 будет принимать 16 команд. Для вызова memcpy потребуется восемь или девять (в зависимости от того, является ли count unsigned char или int [всего 192 байта общей памяти, unsigned char должно быть более чем достаточно!] Обратите внимание, однако, что вызов Процедура memcpy-ish, которая предполагала размер жесткого кодирования и требовала, чтобы оба операнда были в ОЗУ, а не в кодовом пространстве, требовали бы только пять команд для вызова, и это могло быть уменьшено до четырех с использованием глобальной переменной.

Ответ 9

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