Копирование C-структур с помощью memcpy()
часто используется программистами, которые изучили C десятилетия назад и с тех пор не следовали процессу стандартизации.Они просто не знают, что C поддерживает назначение структур (прямое назначение структуры не было доступно во всех компиляторах до ANSI-C89).
Когда они узнают об этой функции, некоторые все еще придерживаются пути memcpy()
потому что это их обычай.Есть также мотивы, которые возникают в программировании культа груза , например, утверждается, что memcpy
просто быстрее - конечно - без возможности подкрепить это контрольным тестом.
Структуры также memcpy()
используются некоторыми начинающими программистами, потому что они либо путают присвоение структуры с назначением указателя структуры - либо они просто чрезмерно используют memcpy()
(они часто также используют memcpy()
, где strcpy()
будет большецелесообразно).
Существует также шаблон сравнения структур memcmp()
, который иногда цитируется некоторыми программистами для использования memcpy()
вместо назначения структуры.Причина этого заключается в следующем: поскольку C не генерирует автоматически оператор ==
для структур, а написание пользовательской функции сравнения структур утомительно, memcmp()
используется для сравнения структур.На следующем шаге - чтобы избежать различий в заполнении битов сравниваемых структур - memset(...,0,...)
используется для инициализации всех структур (вместо использования синтаксиса инициализатора C99 или инициализации всех полей отдельно), а memcpy()
-используется для копирования структур!Потому что memcpy()
также копирует содержимое битов заполнения ...
Но учтите, что эта аргументация ошибочна по нескольким причинам:
- использование
memcpy()
/ memcmp()
/ memset()
вводит новые возможности ошибок - например, предоставляет неправильный размер - , когда структура содержит целочисленные поля, порядок в
memcmp()
изменяется между архитектурами с большим и младшим порядком байтов - charПоле массива размером
n
, определяемое 0
в позиции x
, также должно иметь все элементы после позиции x
, обнуляемой в любое время, иначе 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
Ошибка последнего утверждения может зависеть от переупорядочения кода, изменения компиляцииr, изменение параметров компилятора и тому подобное.
Заключение
Как правило: для повышения корректности и переносимости кода используйте прямое присвоение структуры (вместо memcpy()
), инициализация структуры C99синтаксис (вместо memset
) и пользовательская функция сравнения (вместо memcmp()
).