memcpy (), каким должно быть значение параметра размера? - PullRequest
17 голосов
/ 21 апреля 2010

Я хочу скопировать массив int в другой массив int. Они используют одно и то же определение длины, поэтому они всегда будут одинаковой длины.

Каковы плюсы / минусы следующих двух альтернатив параметра размера для memcpy ()?

memcpy(dst, src, ARRAY_LENGTH*sizeof(int));

или

memcpy(dst, src, sizeof(dst));

Будет ли второй вариант работать всегда? Независимо от содержания?

Одна вещь, которая благоприятствует последнему, состоит в том, что, если массив будет изменен, будет некоторая домашняя работа по обновлению memcpy ().

Спасибо

Ответы [ 12 ]

27 голосов
/ 21 апреля 2010

Пока dst объявлен как массив с размером, sizeof будет возвращать размер этого массива в байтах:

int dst[ARRAY_LENGTH];

memcpy( dst, src, sizeof(dst) ); // Good, sizeof(dst) returns sizeof(int) * ARRAY_LENGTH

Если dst окажется указателем на первый элемент такого массива (того же типа, что и сам массив), он не будет работать:

int buffer[ARRAY_LENGTH];
int* dst = &buffer[0];

memcpy( dst, src, sizeof(dst) ); // Bad, sizeof(dst) returns sizeof(int*)
5 голосов
/ 21 апреля 2010

Если и когда у вас есть массив (настоящий), вы можете использовать трюк sizeof(array), но учтите, что если вы реорганизуете код и толкаете его куда-то, где массив превратился в указатель (или если память была изначально Выделенный в указателе (malloc / new) вам нужно будет передать известный размер.

Игнорирование относительных размеров источника и места назначения, то есть, если предположить, что они одинаковы для остальной части обсуждения, если вы используете C ++, я бы порекомендовал трюк метапрограммирования, который даст вам типоразмерный размер для массивов не скомпилируется, если вы попытаетесь использовать его с указателями:

template <typename T, int N>
inline int array_memory_size( T (&a)[N] ) { return sizeof a; }

Таким образом:

int main() {
   int array[10];
   int *ptr = array;
   int orig[10] = { 0 };
   memcpy( array, orig, array_memory_size(array) ); // ok
   //memcpy( ptr, orig, array_memory_size(ptr) ); // compilation error
}

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

5 голосов
/ 21 апреля 2010

sizeof(dst) корректно, только если dst - это массив, размер которого известен во время компиляции: например, int arr[ARRAY_LENGTH] или массив переменной длины C99; в противном случае он возвращает размер указателя, а не длину целевого массива.

Чтобы избежать ошибок в будущем, будьте последовательны и предпочитайте первую форму: размер типа * длина.

3 голосов
/ 21 апреля 2010

Будет ли второй вариант работать всегда? Независимо от содержания?

2-й вариант работает, только если вы добавили обратно отсутствующие ) и dst - статический массив (т. Е. Типа int[123]).

Если dst имеет неизвестный размер (т. Е. int[]), то sizeof dst возвращает только размер указателя, поскольку dst был уменьшен до указателя. В этом случае вам нужно использовать sizeof(*dst)*ARRAY_LENGTH.

2 голосов
/ 21 апреля 2010

Если вы разместили с помощью malloc, вы должны указать размер массива

int * src = malloc(ARRAY_LENGTH*sizeof(*src));
int * dst1 = malloc(ARRAY_LENGTH*sizeof(*dst));
memcpy(dst1,src,ARRAY_LENGTH*sizeof(*dst));

Если вы разместили статический массив, вы можете просто использовать sizeof

int dst2[ARRAY_LENGTH];
memcpy(dst2,src,sizeof(dst2));
1 голос
/ 08 марта 2013

sizeof (X) всегда дает вам количество байтов "X" если X является массивом uint16_t из 10, то sizeof (X) вернет 20

uint16_t X[10]={0};
cout<<"sizeof x: "<<sizeof(X);

$> sizeof x: 20

если вы хотите количество элементов, вы должны сделать немного байтовой арифметики:
8 бит = 1 байт
16 бит = 2 байта
32 бита = 4 байта
64 бита = 8 байтов

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

 numb_of_elements = ( sizeof(X)/sizeof(X[0]) );

в результате:

uint32_t source[100]={0};
memcpy((void*) dest, (void*) source, ( sizeof(source)/sizeof(source[0]) ));

конечно, вы, вероятно, захотите сделать (sizeof (X) / sizeof (X [0])) константой / переменной, чтобы вы не вычисляли каждый раз .. (Я не знаю, будут ли компиляторы всегда оптимизировать это)

1 голос
/ 21 апреля 2010

Будет ли второй вариант работать всегда? Независимо от содержания?

Это будет работать только при соблюдении обоих условий:

  • dst - это обычный массив, а не указатель
  • src и dst имеют одинаковый размер
1 голос
/ 21 апреля 2010

Предполагая, что dst имеет тип int *, sizeof (dst) вернет размер самого указателя (т.е. 4 в 32-битной системе, 8 в 64-битной системе), поэтому ваш второй пример будет содержать только каждую эту копию байт, тогда как первый будет правильно использовать фактический размер содержимого.

0 голосов
/ 20 ноября 2014

memcpy (), каким должно быть значение параметра size?

Это должен быть минимум между размером исходного буфера и размером целевого буфера.

Традиционно использовался размер исходного буфера. Это иногда переполняло целевой буфер ... Поэтому лучше использовать «более безопасную» версию функции: ту, которая указывает размер буфера источника и назначения.

У вас есть «более безопасные» функции, доступные через ISO / IEC TR24731 . Это намного больше, например, согласованные возвращаемые значения и согласованное поведение при обработке строк.

«Более безопасные» функции теперь являются частью стандарта C, поэтому он должен быть доступен везде. Поэтому вы должны использовать memcpy_s.

Вы не можете использовать его в Linux, потому что он не предоставляет функций (не верьте рекламному шумиху по поводу соответствия стандартам)). В Linux вы должны «свернуть свою» оболочку.

Не каждый является поклонником более безопасных функций. См., Например, Используете ли вы «безопасные» функции TR 24731? . Все, что я могу сказать по этому поводу: Многочисленные переполнения буфера libunp . Миллионы маршрутизаторов и шлюзов подвержены множеству уязвимостей, и многие остаются незащищенными. И они были из-за ошибок, которые были бы остановлены более безопасными функциями. +1 всем, кто говорит «не используйте эту ерунду от Microsoft».

0 голосов
/ 21 апреля 2010

Как насчет?

memcpy(dst, src, &src[ARRAY_LENGTH] - &src[0]);

Это должно работать, даже если размер отдельных элементов был меньше, чем размер каждого элемента в фактическом массиве.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...