OVH Cloud OVH Cloud

Impression de me compliquer ... (strcat/sprintf)

49 réponses
Avatar
Kinou
Bonsoir,

J'ai un petit problème avec strcat/sprintf notamment en C.

En fait je "parse" un char* caractere par caractere, et je concatene ces
caracteres dans une autre chaine (chaineDest) de caractere char*.
Le probleme est que ma chaine chaineDest, si au depart je lui mets une
taille de 20, et que j'ai besoin ensuite de lui concatener plus de 20
caracteres, comment je fais ?

La solution que j'aurais tendance à utiliser est que quand je vois que
la taille devient trop courte, je sauvegarde ma chaine de caractere
(avec strcpy) chaineDest, je lui realloue la taille qu'il faut avec un
malloc, et je recopie ensuite dans l'autre sens pour remettre le contenu
de ma chaine sauvegardé dans la nouvelle chaine de taille plus grande.

Mais ca me parait abérant de faire ça pour toute concaténation en C ...
J'aime croire que le C n'est pas si barbare qu'on le dit, mais que c'est
plutot parce que je suis plus que juste en C ...

Merci par avance pour vos réponses.

--
Cordialement,
Vincent

10 réponses

1 2 3 4 5
Avatar
Charlie Gordon
Maintenant, la pregunta del millón: un entier non signé, de taille «
supérieure ou égale » à unsigned int, quand on le compare avec un int, que
se passe-t'il ? Qui gagne, à ton avis ?


L'int est "converti" en unsigned qqc et la comparaison est non signée. Badaboum.

Le texte de la norme (6.3.1) est particulièment abstrus à ce sujet, et pas un
exemple ne vient éclaircir cette histoire de rank.
La section 6.5.8 sur les opérateurs relationnels est tout aussi sybilline : "If
both of the operands have arithmetic type, the usual arithmetic conversions are
performed.". Pas la moindre remarque sur cette cause de bugs introuvables
qu'est la comparaison signé/non-signé.

Chqrlie.

PS: Abstrus : abscons, mais en pire, car moins connu.

Avatar
Charlie Gordon
"Emmanuel Delahaye" wrote in message
news:
Charlie Gordon wrote on 15/11/04 :
Tu peux tenter d'analyser le contenu du header string.h de la lib C. C'est
particulièrement instructif.


Bof, en dehors de la définition standard, ça dépend totalement de
l'implémentation...

Petite collection glanée sur mon disque...

[...] ramassis d'extraits de headers standard d'environnements exotiques et

obsolètes
Rien de bien instructif à mon sens...



T'en as des vieilleries sur ton disque...
Tu devrais quand même jeter un coup d'oeil à gcc et la glibc, tu t'instruirais !

Chqrlie.


Avatar
Richard Delorme

La librairie standard glibc optimise memcpy a mort, remplaçant la plupart des
appels par du code généré directement en lieu et place de l'appel, et
particulierement efficace quant la taille de la copie est constante et
l'alignement des pointeurs connu.


1) C'est le compilateur qui peut générer du code en ligne, pas une
bibliothèque.
2) le memcpy de la glibc est plutôt lent.

--
Richard

Avatar
Charlie Gordon
"Richard Delorme" wrote in message
news:41990722$0$15747$

La librairie standard glibc optimise memcpy a mort, remplaçant la plupart
des


appels par du code généré directement en lieu et place de l'appel, et
particulierement efficace quant la taille de la copie est constante et
l'alignement des pointeurs connu.


1) C'est le compilateur qui peut générer du code en ligne, pas une
bibliothèque.
2) le memcpy de la glibc est plutôt lent.


Les headers font partie de la libc, ils contiennent des implementations en
ligneet des macros qui font que le compilateur genere du code en ligne. C'est
donc de la combinaison des deux que résult l'optimisation.
Quelle expérience as-tu du memcpy de la glibc (utilisée dans des programmes
compilés avec gcc) ?
Quelle alternative connais-tu qui soit plus rapide ?

Chqrlie.


Avatar
Emmanuel Delahaye
Richard Delorme wrote on 15/11/04 :
En dehors de quelques cas triviaux où memcpy est entièrement déroulé, memcpy
doit s'exécuter dans une boucle où l'on teste que le bon nombre de caractère
a été copié. C'est aussi long que de vérifier si le dernier caractère est
atteint. memcpy peut être plus rapide que strcpy parce qu'il est plus facile
de lui appliquer certaine optimisation tordue, comme la copie par bloc, mais
c'est tout.


Sur x86, il y a possibilité d'insérer du code machine très rapide qui
fait la copie en une ou deux instructions (sorte de DMA).

--
Emmanuel
The C-FAQ: http://www.eskimo.com/~scs/C-faq/faq.html
The C-library: http://www.dinkumware.com/refxc.html

"C is a sharp tool"

Avatar
Richard Delorme
Merci ! memcpy est plus efficace que strcpy ?



Oui, memcpy est plus efficace que strcpy : en règle générale, strcpy doit tester
la valeur de chaque caractère de la chaine source pour determiner quand
s'arreter de copier, alors que memcpy en connait le nombre dès le départ, voire
même dès la compilation.


En dehors de quelques cas triviaux où memcpy est entièrement déroulé,
memcpy doit s'exécuter dans une boucle où l'on teste que le bon nombre
de caractère a été copié. C'est aussi long que de vérifier si le dernier
caractère est atteint. memcpy peut être plus rapide que strcpy parce
qu'il est plus facile de lui appliquer certaine optimisation tordue,
comme la copie par bloc, mais c'est tout.
Utiliser strlen() + memcpy() est sans doute plus lent que d'utiliser
strcpy directement.

--
Richard


Avatar
Emmanuel Delahaye
Charlie Gordon wrote on 15/11/04 :
"Emmanuel Delahaye" wrote in message
news:
Charlie Gordon wrote on 15/11/04 :
Tu peux tenter d'analyser le contenu du header string.h de la lib C. C'est
particulièrement instructif.


Bof, en dehors de la définition standard, ça dépend totalement de
l'implémentation...

Petite collection glanée sur mon disque...

[...] ramassis d'extraits de headers standard d'environnements exotiques et

obsolètes
Rien de bien instructif à mon sens...



T'en as des vieilleries sur ton disque...
Tu devrais quand même jeter un coup d'oeil à gcc et la glibc, tu
t'instruirais !


TU parles beaucoup, mais tu ne montres rien... Une partie des exemples
que j'ai donné vient de gcc (djgpp ou mingw).

--
Emmanuel
The C-FAQ: http://www.eskimo.com/~scs/C-faq/faq.html
The C-library: http://www.dinkumware.com/refxc.html

"C is a sharp tool"



Avatar
Charlie Gordon
"Richard Delorme" wrote in message
news:41990b5e$0$15747$
Merci ! memcpy est plus efficace que strcpy ?



Oui, memcpy est plus efficace que strcpy : en règle générale, strcpy doit
tester


la valeur de chaque caractère de la chaine source pour determiner quand
s'arreter de copier, alors que memcpy en connait le nombre dès le départ,
voire


même dès la compilation.


En dehors de quelques cas triviaux où memcpy est entièrement déroulé,
memcpy doit s'exécuter dans une boucle où l'on teste que le bon nombre
de caractère a été copié. C'est aussi long que de vérifier si le dernier
caractère est atteint. memcpy peut être plus rapide que strcpy parce
qu'il est plus facile de lui appliquer certaine optimisation tordue,
comme la copie par bloc, mais c'est tout.


Oui entre autres, mais ça peut faire une différence monstrueuse, un facteur
entier, supérieur à 10 même dans certains cas.

Utiliser strlen() + memcpy() est sans doute plus lent que d'utiliser
strcpy directement.


Nous sommes d'accord.

Chqrlie.



Avatar
Charlie Gordon
TU parles beaucoup, mais tu ne montres rien... Une partie des exemples
que j'ai donné vient de gcc (djgpp ou mingw).


Les headers livrés avec ces packages sont simplistes, c'est aussi le cas de
cygwin.
C'est sous unix que tu peux réellement juger de la qualité de la glibc.

Les optimisations en question sont dans bits/string.h et bits/string2.h,
incluses uniquement en cas d'optimisation et si le compilateur est gcc 2 ou
mieux. La glibc est assez compliquée à suivre pour cela. Quant à
l'optimisation, c'est parfois assez fumeux.

Je vous livre ici la version pour i386 (il s'agit d'un extrait d'un des headers
inclus par string.h Le code est sous licence LGPL)

/* The ix86 processors can access unaligned multi-byte variables. */
#define _STRING_ARCH_unaligned 1


/* We only provide optimizations if the user selects them and if
GNU CC is used. */
#if !defined __NO_STRING_INLINES && defined __USE_STRING_INLINES
&& defined __GNUC__ && __GNUC__ >= 2 && !__BOUNDED_POINTERS__

#ifndef __STRING_INLINE
# ifdef __cplusplus
# define __STRING_INLINE inline
# else
# define __STRING_INLINE extern __inline
# endif
#endif


/* Copy N bytes of SRC to DEST. */
#define _HAVE_STRING_ARCH_memcpy 1
#define memcpy(dest, src, n)
(__extension__ (__builtin_constant_p (n)
? __memcpy_c (dest, src, n)
: memcpy (dest, src, n)))
/* This looks horribly ugly, but the compiler can optimize it totally,
as the count is constant. */
__STRING_INLINE void *__memcpy_c (void *__dest, __const void *__src,
size_t __n);

__STRING_INLINE void *
__memcpy_c (void *__dest, __const void *__src, size_t __n)
{
register unsigned long int __d0, __d1, __d2;
union {
unsigned int __ui;
unsigned short int __usi;
unsigned char __uc;
} *__u = __dest;
switch (__n)
{
case 0:
return __dest;
case 1:
__u->__uc = *(const unsigned char *) __src;
return __dest;
case 2:
__u->__usi = *(const unsigned short int *) __src;
return __dest;
case 3:
__u->__usi = *(const unsigned short int *) __src;
__u = (void *) __u + 2;
__u->__uc = *(2 + (const unsigned char *) __src);
return __dest;
case 4:
__u->__ui = *(const unsigned int *) __src;
return __dest;
case 6:
__u->__ui = *(const unsigned int *) __src;
__u = (void *) __u + 4;
__u->__usi = *(2 + (const unsigned short int *) __src);
return __dest;
case 8:
__u->__ui = *(const unsigned int *) __src;
__u = (void *) __u + 4;
__u->__ui = *(1 + (const unsigned int *) __src);
return __dest;
case 12:
__u->__ui = *(const unsigned int *) __src;
__u = (void *) __u + 4;
__u->__ui = *(1 + (const unsigned int *) __src);
__u = (void *) __u + 4;
__u->__ui = *(2 + (const unsigned int *) __src);
return __dest;
case 16:
__u->__ui = *(const unsigned int *) __src;
__u = (void *) __u + 4;
__u->__ui = *(1 + (const unsigned int *) __src);
__u = (void *) __u + 4;
__u->__ui = *(2 + (const unsigned int *) __src);
__u = (void *) __u + 4;
__u->__ui = *(3 + (const unsigned int *) __src);
return __dest;
case 20:
__u->__ui = *(const unsigned int *) __src;
__u = (void *) __u + 4;
__u->__ui = *(1 + (const unsigned int *) __src);
__u = (void *) __u + 4;
__u->__ui = *(2 + (const unsigned int *) __src);
__u = (void *) __u + 4;
__u->__ui = *(3 + (const unsigned int *) __src);
__u = (void *) __u + 4;
__u->__ui = *(4 + (const unsigned int *) __src);
return __dest;
}
#define __COMMON_CODE(x)
__asm__ __volatile__
("cldnt"
"rep; movsl"
x
: "=&c" (__d0), "=&D" (__d1), "=&S" (__d2)
: "0" (__n / 4), "1" (&__u->__uc), "2" (__src)
: "memory");

switch (__n % 4)
{
case 0:
__COMMON_CODE ("");
break;
case 1:
__COMMON_CODE ("ntmovsb");
break;
case 2:
__COMMON_CODE ("ntmovsw");
break;
case 3:
__COMMON_CODE ("ntmovswntmovsb");
break;
}
return __dest;
#undef __COMMON_CODE
}

#endif

Le code effectivement utilisé dépend de la configuration de gcc et d'autres
optimisations sont utilisées pour les processeurs plus récents.

Bon, si tu t'instruis en lisant cela, c'est que tu connais déjà pas mal de
choses ;-)

Chqrlie.

Avatar
Emmanuel Delahaye
Richard Delorme wrote on 15/11/04 :
Sur x86, il y a possibilité d'insérer du code machine très rapide qui fait
la copie en une ou deux instructions (sorte de DMA).


Si tu penses à REP MOVS, c'est généralement peu efficace sur les processeurs
x86 actuels. Par exemple, pour le processeur que j'utilise actuellement
(athlon-xp), on peut lire ici (p 68) :
http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/22007.pdf

« The REP MOVS instructions are often not as efficient as an explicit loop
which uses simple "RISC" instructions. »
Les pages suivantes donnent plusieurs exemples de code assembleur pour
réaliser un memcpy, le plus rapide étant 2 à 3 fois plus rapide que ceux
basés sur de simple REP MOVS.


Effectivement, mes connaissances en assembleur x86 sont anciennes et
probablement dépassées.

--
Emmanuel
The C-FAQ: http://www.eskimo.com/~scs/C-faq/faq.html
The C-library: http://www.dinkumware.com/refxc.html

"C is a sharp tool"


1 2 3 4 5