Vous pouvez également considérer cette utilisation de memmove
vu dans Git 2.14.x (Q3 2017)
Véase commettre 168e635 (16 juillet 2017), et commit 1773664 , commettre f331ab9 , commettre 5783980 (15 juillet 2017) par René Scharfe ( rscharfe
) .
(fusionné par Junio C Hamano -- gitster
-- en commit 32f9025 , 11 août 2017)
Il utilise un macro auxiliaire MOVE_ARRAY
qui calcule la taille sur la base du nombre d'éléments spécifiés pour nous et supporte NULL
lorsque ce nombre est égal à zéro.
Brut memmove(3)
appels avec NULL
peut faire en sorte que le compilateur optimise (avec trop d'empressement) les éléments suivants NULL
des contrôles.
MOVE_ARRAY
ajoute une aide sûre et pratique pour déplacer des plages d'entrées de tableaux qui peuvent se chevaucher.
Il déduit la taille de l'élément, multiplie automatiquement et en toute sécurité pour obtenir la taille en octets, effectue un contrôle de sécurité de type de base en comparant les tailles des éléments et les différences entre les éléments. memmove(3)
il soutient NULL
les pointeurs si 0 élément doit être déplacé.
#define MOVE_ARRAY(dst, src, n) move_array((dst), (src), (n), sizeof(*(dst)) + \
BUILD_ASSERT_OR_ZERO(sizeof(*(dst)) == sizeof(*(src))))
static inline void move_array(void *dst, const void *src, size_t n, size_t size)
{
if (n)
memmove(dst, src, st_mult(size, n));
}
Exemples :
- memmove(dst, src, (n) * sizeof(*dst));
+ MOVE_ARRAY(dst, src, n);
Il utilise le macro BUILD_ASSERT_OR_ZERO
qui affirme une dépendance au moment de la construction, comme une expression (avec @cond
étant la condition de compilation qui doit être vraie).
La compilation échouera si la condition n'est pas vraie, ou ne peut pas être évaluée par le compilateur.
#define BUILD_ASSERT_OR_ZERO(cond) \
(sizeof(char [1 - 2*!(cond)]) - 1)
Exemple :
#define foo_to_char(foo) \
((char *)(foo) \
+ BUILD_ASSERT_OR_ZERO(offsetof(struct foo, string) == 0))
7 votes
Mathématiquement, l'intersection de deux ensembles vides est vide.
0 votes
Je voulais vérifier pour vous ce que (x)libC fait pour vous, mais comme c'est l'asm (elibc/glibc ici), c'est un peu trop compliqué pour un petit matin :)
0 votes
Por qué serait vous faites ça ? À propos, le chevauchement des régions de mémoire n'est pas la seule raison pour laquelle UB avec
memcpy
.17 votes
+1 J'aime cette question à la fois parce que c'est un cas limite si étrange et parce que je pense que
memcpy(0,0,0)
est l'un des plus étranges morceaux de code C que j'ai vu.2 votes
@eq Voulez-vous vraiment savoir, ou sous-entendez-vous qu'il n'y a pas de situations où vous le voudriez ? Avez-vous envisagé que l'appel réel pourrait être, disons,
memcpy(outp, inp, len)
? Et que cela peut se produire dans un code oùoutp
yinp
sont alloués dynamiquement et sont initialement0
? Cela fonctionne, par exemple, avecp = realloc(p, len+n)
quandp
ylen
sont0
. J'ai moi-même utilisé un telmemcpy
bien que ce soit techniquement UB, je n'ai jamais rencontré une implémentation où ce n'était pas un no-op et je ne m'attends pas à le faire.7 votes
@templateetypedef
memcpy(0, 0, 0)
est très probablement destinée à représenter une invocation dynamique et non statique... c'est-à-dire que les valeurs des paramètres ne doivent pas nécessairement être des littéraux.0 votes
@eq, @templatetypedef : Ce ne sont pas des valeurs littérales mais dynamiques... dans un logiciel tiers :/
0 votes
@Jim Balter : Bien sûr que oui (j'y ai pensé). Chaque fois que j'utilise le C, j'essaie de m'en tenir à l'écriture d'un C portable (c'est-à-dire bien défini), même si je dois écrire quelques conditionnels supplémentaires - il est peu probable qu'ils constituent un goulot d'étranglement pour les performances (et je ne les supprimerais que si c'était le cas, les écrire n'est pas si difficile), et la seule façon de le savoir est eh bien, de le savoir. Le fait qu'elles puissent un jour "sauver la situation" n'est pas vraiment pertinent.
0 votes
Contrairement au C++, le C ne traite pas null comme un pointeur vers un tableau. (C'est très étrange.)