web-dev-qa-db-fra.com

memcpy (), quelle devrait être la valeur du paramètre de taille?

Je veux copier un tableau int sur un autre tableau int. Ils utilisent la même définition pour la longueur afin qu'ils soient toujours de la même longueur.

Quels sont les avantages/inconvénients des deux alternatives suivantes du paramètre de taille à memcpy ()?

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

ou

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

La deuxième option fonctionnera-t-elle toujours? Indépendamment du contenu?

Une chose qui favorise le dernier est que si le tableau devait changer, ce serait un peu de ménage pour mettre à jour le memcpy ().

Merci

16
Tomas

Tant que dst est déclaré en tant que tableau avec une taille, sizeof renverra la taille de ce tableau en octets:

int dst[ARRAY_LENGTH];

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

Si dst est juste un pointeur sur le premier élément d'un tel tableau (qui est du même type que le tableau lui-même), cela ne fonctionnera pas:

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

memcpy( dst, src, sizeof(dst) ); // Bad, sizeof(dst) returns sizeof(int*)
27
Timbo

Si et quand vous avez un tableau (réel), vous pouvez utiliser l’astuce sizeof(array), mais notez que si vous refactorisez le code et que vous le poussez à un endroit où le tableau a été décomposé en un pointeur (ou si la mémoire a été initialement allouée dans un pointeur ( malloc/new), vous devrez passer une taille connue.

En ignorant les tailles relatives de la source et de la destination, c'est-à-dire en supposant qu'elles sont identiques pour le reste de la discussion, si vous utilisez le C++, je recommanderais une astuce de métaprogrammation qui vous donnera un nombre de tailles de type sécurisé pour les tableaux et échouera. compiler si vous essayez de l’utiliser avec des pointeurs:

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

De cette façon:

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
}

Si, à un moment quelconque, vous refactorisez et que le code se déplace vers un emplacement où le tableau s'est décomposé (ou si vous remplacez un tableau statique par un autre alloué dynamiquement), le compilateur vous indiquera que vous devez corriger le calcul de la taille.

sizeof(dst) est correct uniquement si dst est un tableau de taille connue au moment de la compilation: comme int arr[ARRAY_LENGTH] ou un tableau de longueur variable C99; sinon, il retourne la taille d'un pointeur, pas la longueur du tableau de destination.

Pour éviter les bogues futurs, soyez cohérent et préférez la première forme: taille de type * longueur.

5
Gregory Pakosz

La deuxième option fonctionnera-t-elle toujours? Indépendamment du contenu? 

La 2ème option ne fonctionne que si vous rajoutez le ) manquant et dst est un tableau statique (c'est-à-dire de type int[123]).

Si dst a une taille inconnue (c'est-à-dire int[]), alors sizeof dst ne renvoie que la taille du pointeur, car dst a été décomposé en pointeur. Dans ce cas, vous devez utiliser sizeof(*dst)*ARRAY_LENGTH.

3
kennytm

Si vous avez alloué en utilisant malloc, vous devez indiquer la taille du tableau

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

Si vous avez alloué un tableau statique, vous pouvez simplement utiliser sizeof

int dst2[ARRAY_LENGTH];
memcpy(dst2,src,sizeof(dst2));
2
Scott Wales

La deuxième option fonctionnera-t-elle toujours? Indépendamment du contenu? 

Cela ne fonctionnera que si les deux conditions sont remplies:

  • dst est un tableau normal, pas un pointeur
  • src et dst ont la même taille
1
el.pescado

sizeof (X) vous donne toujours le nombre de bytes de "X" si X est un tableau uint16_t de 10, alors sizeof (X) retournera 20

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

$> sizeof x: 20

si vous voulez le nombre d'éléments, vous devez faire un peu d'arithmétique sur les octets:
8bit = 1 octet
16bit = 2 octets
32bit = 4 octets
64bit = 8 octets

pour avoir le nombre d’éléments que vous pouvez faire:

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

résultant en:

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

bien sûr, vous voudrez probablement faire (sizeof (X)/sizeof (X [0])) une constante/variable pour que vous ne calculiez pas à chaque fois .. (je ne sais pas si les compilateurs optimiseront toujours cela)

1
AlexC_JEng

En supposant que dst soit de type int *, sizeof (dst) retournera la taille du pointeur lui-même (c'est-à-dire 4 sur un système 32 bits, 8 sur un système 64 bits). le premier utilisera correctement la taille réelle du contenu.

1
Mark_Carrington

Ça dépend. Arr et le pointeur sont des tableaux, mais sizeof () ne renvoie que la taille correcte pour arr, qui est déclaré à la compilation.

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);
}
0
Sjoerd

Si dst a été alloué depuis le tas (en utilisant malloc par exemple), la deuxième solution ne fonctionnera pas. sizeof (dst) ne fonctionnera que s’il est connu du compilateur. Par exemple, l'exemple suivant échouera car sizeof (dst) sera égal à la taille d'un pointeur (4-8 octets).

#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

Ce segment de code fonctionnera à chaque fois:

#define ARRAY_LENGTH 10
int *dst;

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

Que diriez-vous?

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

Cela devrait fonctionner même si la taille des éléments individuels était inférieure à la taille prise par chaque élément du tableau actuel.

0
squelart

memcpy (), quelle devrait être la valeur du paramètre de taille?

Ce doit être le minimum entre la taille du tampon source et la taille du tampon de destination.

Traditionnellement, la taille du tampon source a été utilisée. Cela a occasionnellement débordé du tampon de destination ... Il est donc préférable d’utiliser une version "plus sûre" de la fonction: une version qui spécifie à la fois la taille des tampons source et de destination.

Vous avez des fonctions "plus sûres" disponibles via ISO/IEC TR24731 . Il y a beaucoup plus que cela, comme des valeurs de retour cohérentes et un comportement cohérent de traitement des chaînes.

Les fonctions "plus sûres" font maintenant partie de la norme C, donc supposées être disponibles partout. Donc, vous devriez utiliser memcpy_s.

Vous ne pouvez pas l'utiliser sous Linux, car il ne fournit pas les fonctions (ne croyez pas le battage publicitaire concernant les normes conformes)). Sous Linux, vous devriez "rouler votre propre" wrapper.

Tout le monde n'est pas fan des fonctions plus sûres. Voir, par exemple, Utilisez-vous les fonctions «safe» du TR 24731? . Tout ce que je peux dire à ce sujet est: Plusieurs débordements de tampon libunp . Des millions de routeurs et de passerelles sont soumis à de multiples vulnérabilités et beaucoup restent non corrigés. Et ils étaient dus à des bugs qui auraient été arrêtés par les fonctions plus sûres. +1 à tous ceux qui disent "n'utilisez pas cette merde de Microsoft".

0
jww