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

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

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

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

или

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

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

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

спасибо

12 ответов


пока 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*)

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

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


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

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

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
}

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


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

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

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


Если вы выделили с помощью 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));

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


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

он будет работать, только если выполнены оба условия:

  • dst является регулярным массивом, а не указателем
  • src и dst имеют одинаковый размер

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]) ) константа/переменная, чтобы вы не вычисляли каждый раз.. (Я не знаю, будут ли компиляторы всегда оптимизировать это)


Это зависит. Arr и указатель являются массивами, но sizeof () возвращает только правильный размер для arr, который объявляется во время компиляции.

int main() {
        int arr[10];
        int * pointer;
        pointer = (int *) malloc(10 * sizeof(int));
        printf("%d\n", sizeof(arr)); // 40
        printf("%d\n", sizeof(pointer)); // 4 or 8
        free(pointer);
}

Если dst был выделен из кучи (например, с помощью malloc), второе решение не будет работать. sizeof (dst) будет работать только тогда, когда он известен компилятору. Например, в следующем примере произойдет сбой, поскольку sizeof (dst) будет равен размеру указателя (4-8 байтов.)

#define ARRAY_LENGTH 10
int *dst;

dst = malloc(ARRAY_LENGTH*sizeof(int));
memcpy(dst, src, sizeof(dst)); // sizeof dst in this case would be 4 bytes on 32 bit system

этот фрагмент кода будет работать каждый раз:

#define ARRAY_LENGTH 10
int *dst;

dst = malloc(ARRAY_LENGTH*sizeof(int));
memcpy(dst, src, ARRAY_LENGTH*sizeof(int)); // sizeof would be 40 bytes

Как насчет?

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

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


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

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

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

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

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

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

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