OVH Cloud OVH Cloud

Concatenation de chaine de caractere

54 réponses
Avatar
Pascal
Comment concatener plusieur chaine de caractere, un nombre assez
important? je connais strcat, mais c'est pas tres pratique des que l'on
doit concatener plus de deux chaines... En gros je cherche un equivalent
a ce que l'on trouve sur java ou c++, un truc simple du style
"char1"+"char2"+char3" -> "char1char2char3".
--
Pascal

10 réponses

1 2 3 4 5
Avatar
Éric Lévénez
Le 23/11/03 13:46, dans <3fc0ac4a$0$26815$, « Pascal »
a écrit :

Comment concatener plusieur chaine de caractere, un nombre assez
important? je connais strcat, mais c'est pas tres pratique des que l'on
doit concatener plus de deux chaines... En gros je cherche un equivalent
a ce que l'on trouve sur java ou c++, un truc simple du style
"char1"+"char2"+char3" -> "char1char2char3".


Tu peux faire :

n = snprintf(buf, sizeof buf, "%s%s%s", chaine1, chain2, chaine3);

"buf" étant un buffer que tu as préalablement alloué avec une taille
suffisante.

--
Éric Lévénez -- <http://www.levenez.com/>
Unix is not only an OS, it's a way of life.

Avatar
Anthony Fleury
Comment concatener plusieur chaine de caractere, un nombre assez
important? je connais strcat, mais c'est pas tres pratique des que
l'on doit concatener plus de deux chaines... En gros je cherche un
equivalent a ce que l'on trouve sur java ou c++, un truc simple du
style "char1"+"char2"+char3" -> "char1char2char3".


En C il n'y a malheureusement rien de simple pour faire ca (pas de surcharge
d'opérateur ou autre) donc tu ne peux pas.
Pour un nombre connu de chaines à concaténer, il y a bien sprintf ou
snprintf en C99 aui vérifie les débordement donc est plus conseillée. Par
exemple :

char* concat = malloc(CONCATMAX);
snprintf(concat, CONCATMAX, "%s %s %s %s", chaine1, chaine2, chaine3,
chaine4);

Dans le cas où tu veux faire autre chose, quelque chose qui est un peu plus
spécialisé, tu peux toujours faire ta fonction à l'aide d'une fonction à
nombre d'arguments variables dans le genre :
char* concat(int narg, char* chaine1, ...)
ou alors en imposant que le dernier paramètre soit fixé (quitte à s'amuser
avec une macro) qu'il soit à NULL par exemple :
char* concat(char* chaine1, ...)

Mais sinon à ma connaissance il n'y a rien d'aussi simple que ce que fait le
C++.

Anthony
--
"On est bien sûr de soi quand on est grand, avec notre savoir, notre
morale, tous ces principes auxquels on tient tant.
Mais c'est souvent trop tard que l'on comprend, que le bonheur
était simple comme un jeu d'enfant." -- Sinsemilia

Avatar
kilobug

Comment concatener plusieur chaine de caractere, un nombre assez
important? je connais strcat, mais c'est pas tres pratique des que
l'on doit concatener plus de deux chaines... En gros je cherche un
equivalent a ce que l'on trouve sur java ou c++, un truc simple du
style "char1"+"char2"+char3" -> "char1char2char3".


Pour les solutions standards, comme les autres l'ont dit, il n'y a que
snprintf qui soit utilisable sans trop de risque (strncat aussi, mais
plus délicate à manipuler).

En utilisant un bibliothèque, il y a la GLib, qui apporte deux méchanismes:
- g_strconcat ("char1", "char2", "char3", NULL)
http://developer.gnome.org/doc/API/2.0/glib/glib-String-Utility-Functions.html#g-strconcat
- un type de données "string" similaire aux strings C++:
http://developer.gnome.org/doc/API/2.0/glib/glib-Strings.html

Enfin, il y a la solution utilise asprintf (libc GNU) ou
g_strdup_printf (GLib) qui fonctionne comme snprintf mais alloue
automatiquement la taille nécessaire.

Dans tous les cas, si ton programme sera diffusé sous une license
compatible avec la LGPL, tu peux prendre le code de g_strconcat ou
g_strdup_printf ou asprintf et le mettre dans ton programme.

--
Gael Le Mignot "Kilobug" - - http://kilobug.free.fr
GSM : 06.71.47.18.22 (in France) ICQ UIN : 7299959
Fingerprint : 1F2C 9804 7505 79DF 95E6 7323 B66B F67B 7103 C5DA

Member of HurdFr: http://hurdfr.org - The GNU Hurd: http://hurd.gnu.org

Avatar
DINH Viêt Hoà

Comment concatener plusieur chaine de caractere, un nombre assez
important? je connais strcat, mais c'est pas tres pratique des que
l'on doit concatener plus de deux chaines... En gros je cherche un
equivalent a ce que l'on trouve sur java ou c++, un truc simple du
style "char1"+"char2"+char3" -> "char1char2char3".


char* concat = malloc(CONCATMAX);
snprintf(concat, CONCATMAX, "%s %s %s %s", chaine1, chaine2, chaine3,
chaine4);


Peut-être vaut-il mieux faire quelque chose de plus rigoureux :

char * concat(char * chaine1, char * chaine2, char * chaine3)
{
char * resultat;
int variante;
int len;

variante = 1;

len = strlen(chaine1) + strlen(chaine2) + strlen(chaine3);
resultat = malloc(len + 1);
if (resultat == NULL)
return NULL;

switch (variante) {
case 1:
sprintf(resultat, "%s%s%", chaine1, chaine2, chaine3);
break;
case 2:
/* snprintf() ne sert pas à grand chose */
snprintf(resultat, len + 1, "%s%s%s", chaine1, chaine2, chaine3);
break;
case 3:
strcpy(resultat, chaine1);
strcat(resultat, chaine2);
strcat(resultat, chaine3);
break;
}

return resultat;
}


--
DINH V. Hoa,

"monde de merde" -- Erwan David


Avatar
DINH Viêt Hoà

Enfin, il y a la solution utilise asprintf (libc GNU) ou
g_strdup_printf (GLib) qui fonctionne comme snprintf mais alloue
automatiquement la taille nécessaire.


Ce serait pas mal d'essayer d'éviter de donner de mauvaises habitudes en
conseillant l'utilisation de fonctions (vraiment) non-standards. Je sais
bien que ces fonctions sont pratiques mais vu qu'elles n'existent
souvent que sur une plateforme unique (Linux et GNU), ce n'est pas
l'idéal.

--
DINH V. Hoa,

"monde de merde" -- Erwan David

Avatar
Horst Kraemer
On Sun, 23 Nov 2003 13:46:31 +0100, Pascal wrote:

Comment concatener plusieur chaine de caractere, un nombre assez
important? je connais strcat, mais c'est pas tres pratique des que l'on
doit concatener plus de deux chaines... En gros je cherche un equivalent
a ce que l'on trouve sur java ou c++, un truc simple du style
"char1"+"char2"+char3" -> "char1char2char3".


C'est impossible en C parce qu'en C il n'existe pas de type "string"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>


char *concat(char *s,...)
{
char *p;
va_list ap;

va_start(ap,s);
while ((p=va_arg(ap,char*))!=0) strcat(s,p);
va_end(ap);
return s;
}

int main()
{
char *s = malloc(100);

*s=0;
concat(s,"|Hel","lo, ","world","!|",0);
puts(s);

/* ou bien */

sprintf(s,"%s%s%s%s","|Hel","lo, ","world","!|");
puts(s);


return 0;
}


--
Horst

Avatar
kilobug

Enfin, il y a la solution utilise asprintf (libc GNU) ou
g_strdup_printf (GLib) qui fonctionne comme snprintf mais alloue
automatiquement la taille nécessaire.



Ce serait pas mal d'essayer d'éviter de donner de mauvaises habitudes en
conseillant l'utilisation de fonctions (vraiment) non-standards. Je sais
bien que ces fonctions sont pratiques mais vu qu'elles n'existent
souvent que sur une plateforme unique (Linux et GNU), ce n'est pas
l'idéal.


Lorsque les fonctions sont ddans une bibliothèque diffusé sous une
license libre, et que tu comptes diffuser le programme sous une
license compatibel, tu peux reprendre le code déjà fait, testé, validé
et optimisé de la libc GNU ou de la GLib, plutôt que de réinventer la
roue à chaque fois.

La mauvaise habitude c'est de toujours tout refaire au lieu de
réutiliser ce qui a déjà été fait. Utiliser une fonction de la libc
GNU, cela implique uniquement de l'inclure dans le programme si on
veut le porter.

Et une autre mauvaise habitude consiste à travailler avec des buffers
de taille fixe (pire, des char buf[SIZE] et bonjour l'exploit au
moindre bug) qui sont la plupart du temps du gachis de mémoire (buffer
trop grand) et le reste du temps des limitations (buffer trop petit).
Prendre l'habitude d'utiliser des fonctions propres (comme celle de
libc GNU), quitte à les inclure ensuite dans le programme c'est
beaucoup plus positif. (et je rappelle à tout le monde que, par
exemple, un nom de fichier ou une URL n'ont _pas_ de taille limite,
pas 512, pas 4096, pas PATHMAX, pas même fpathconf).

--
Gael Le Mignot "Kilobug" - - http://kilobug.free.fr
GSM : 06.71.47.18.22 (in France) ICQ UIN : 7299959
Fingerprint : 1F2C 9804 7505 79DF 95E6 7323 B66B F67B 7103 C5DA

Member of HurdFr: http://hurdfr.org - The GNU Hurd: http://hurd.gnu.org


Avatar
DINH Viêt Hoà

Lorsque les fonctions sont ddans une bibliothèque diffusé sous une
license libre, et que tu comptes diffuser le programme sous une
license compatibel, tu peux reprendre le code déjà fait, testé, validé
et optimisé de la libc GNU ou de la GLib, plutôt que de réinventer la
roue à chaque fois.


enfin vu qu'ici, des solutions _standards_ ont déjà été données.

La mauvaise habitude c'est de toujours tout refaire au lieu de
réutiliser ce qui a déjà été fait. Utiliser une fonction de la libc
GNU, cela implique uniquement de l'inclure dans le programme si on
veut le porter.


Effectivement, mais tu m'expliqueras sans doute un jour comment extraire
le code d'une fonction de la GNU libc sans extraire la GNU libc
intégralement.

Et une autre mauvaise habitude consiste à travailler avec des buffers
de taille fixe (pire, des char buf[SIZE] et bonjour l'exploit au
moindre bug) qui sont la plupart du temps du gachis de mémoire (buffer
trop grand) et le reste du temps des limitations (buffer trop petit).
Prendre l'habitude d'utiliser des fonctions propres (comme celle de
libc GNU), quitte à les inclure ensuite dans le programme c'est
beaucoup plus positif. (et je rappelle à tout le monde que, par
exemple, un nom de fichier ou une URL n'ont _pas_ de taille limite,
pas 512, pas 4096, pas PATHMAX, pas même fpathconf).


Hum ... il faut aussi savoir qu'on peut, tout comme pour les buffers de
taille fixe, avoir des problèmes d'exploit sur les buffers de taille
variable : par exemple (de très mauvaise foi) :

char * copier_nom(char * nom)
{
static char * buffer = NULL;

if (buffer == NULL)
buffer = strdup(nom);
else
strcpy(buffer, nom);

return buffer;
}

Bref, il y a une façon assez évidente de faire planter ce code.

En ce qui concerne les limites de taille de nom de fichier définies par
POSIX, elle est réellement PATH_MAX. Allez, un petit peu de
documentation pour aider : (il suffit de s'inscrire pour accéder aux
standards POSIX)

http://www.unix-systems.org/single_unix_specification/

Un 3ème point est qu'accumuler des allocations mémoires pour obtenir une
"meilleure" "sécurité" ne fait qu'aggraver les performances de ton
programmes. Et oui, la présence de malloc() tout le long de l'exécution
n'est pas signe de performance. Par exemple, en embarqué, ils ne
travaillent que sur des données allouées statiquement, devine pourquoi ?
(des indices ont été données dans la première partie de ce paragraphe).

Le problème du buffer overflow n'est pas que le buffer soit fixe, c'est
savoir comment utiliser judicieusement le buffer qu'on a prévu (par
exemple buffer de taille PATH_MAX fixe) en tenant compte des paramètres
du système (PATH_MAX par exemple).

Au final, si on veut obtenir de la sécurité, il vaut mieux s'attacher à
faire les choses soigneusement (vérifier les codes de retour des appels
susceptibles d'être en faute, par exemple, malloc(), fopen())
qu'utiliser des faux biais de "sécurité" comme ceux que tu viens de
citer.

Un petit exemple extrême pour finir : dans IMAP Toolkit de l'Université
de Washington (http://www.washington.edu/imap/). Aucun snprintf() n'est
utilisé, que du sprintf() partout. Malgré tout les bugs de buffer
overflow sont difficiles à trouver.

--
DINH V. Hoa,

etPan! - newsreader, mail user agent -- http://libetpan.sf.net/etpan

Avatar
Erwan David
DINH Viêt Hoà écrivait :

char * copier_nom(char * nom)
{
static char * buffer = NULL;

if (buffer == NULL)
buffer = strdup(nom);
else
strcpy(buffer, nom);

return buffer;
}

Bref, il y a une façon assez évidente de faire planter ce code.


Bah oui, ne pas vérifier le retour de strdup c'est pas malin du
tout...

Avatar
kilobug

Lorsque les fonctions sont ddans une bibliothèque diffusé sous une
license libre, et que tu comptes diffuser le programme sous une
license compatibel, tu peux reprendre le code déjà fait, testé, validé
et optimisé de la libc GNU ou de la GLib, plutôt que de réinventer la
roue à chaque fois.



enfin vu qu'ici, des solutions _standards_ ont déjà été données.


Ah ? Moi je n'en ai vu aucune qui soit flexible (utilisable avec un
nombre qeulconque de chaines) et qui n'impose pas de limite statique à
la taille du résultat.

La mauvaise habitude c'est de toujours tout refaire au lieu de
réutiliser ce qui a déjà été fait. Utiliser une fonction de la libc
GNU, cela implique uniquement de l'inclure dans le programme si on
veut le porter.



Effectivement, mais tu m'expliqueras sans doute un jour comment extraire
le code d'une fonction de la GNU libc sans extraire la GNU libc
intégralement.


Ça dépend des fonctions, pour asprintf c'est assez délicat en effet,
mais si tu regardes info libc, tru verras même parfois une version
(moins optimale, certes) mais une version utilisant les fonctions
standards et qui reproduisent les effets de la fonction de la libc
(après , un petit check autoconf ou un #ifdef _GNU_SOURCE, et on
utilise soit l 'implémentation du système soit celle moins optimale
mais fonctionnelle que l'on fournit)

Je donne aussi souvent des ref à la GLib, et la GLib elle permet
facilement l'extraction de fonctions (et la GLib est portée un peu
partout, de NetBSD à Solaris en passant par Windows et BeOS, avec même
un port pour la PS2 et MacOS X)

Et une autre mauvaise habitude consiste à travailler avec des buffers
de taille fixe (pire, des char buf[SIZE] et bonjour l'exploit au
moindre bug) qui sont la plupart du temps du gachis de mémoire (buffer
trop grand) et le reste du temps des limitations (buffer trop petit).
Prendre l'habitude d'utiliser des fonctions propres (comme celle de
libc GNU), quitte à les inclure ensuite dans le programme c'est
beaucoup plus positif. (et je rappelle à tout le monde que, par
exemple, un nom de fichier ou une URL n'ont _pas_ de taille limite,
pas 512, pas 4096, pas PATHMAX, pas même fpathconf).



Hum ... il faut aussi savoir qu'on peut, tout comme pour les buffers de
taille fixe, avoir des problèmes d'exploit sur les buffers de taille
variable : par exemple (de très mauvaise foi) :


Certes, il ya toujours moyen de faire un trou de sécurité, mais c'est
plus ou moins facile (et plus ou moins facile à exploiter)

char * copier_nom(char * nom)
{
static char * buffer = NULL;


if (buffer == NULL)
buffer = strdup(nom);
else
strcpy(buffer, nom);


return buffer;
}


Bref, il y a une façon assez évidente de faire planter ce code.


Le faire planter oui, le contraindre à exécuter du code arbitraire,
beaucoup plus difficilement, vu que le débordement ne se fait pas sur
la piule, on ne peut pas écraser l'addresse de retour de la fonction

En ce qui concerne les limites de taille de nom de fichier définies
par POSIX, elle est réellement PATH_MAX. Allez, un petit peu de
documentation pour aider : (il suffit de s'inscrire pour accéder
aux standards POSIX)


http://www.unix-systems.org/single_unix_specification/


Merci, je connais les standrads POSIX.

Et tu verras si tu les lis, ces standrads, que PATH_MAX est
_optionnel_, un système POSIX n'est PAS obligé de définir PATH_MAX. Ce
que dit POSIX c'est "si le système définit PATH_MAX, alors aucun
chemin ne peut dépasser PATH_MAX", mais certains systèmes POSIX (comme
GNU/Hurd) n'ont pas de limite.

Un 3ème point est qu'accumuler des allocations mémoires pour obtenir une
"meilleure" "sécurité" ne fait qu'aggraver les performances de ton
programmes. Et oui, la présence de malloc() tout le long de l'exécution
n'est pas signe de performance.


Mauvaise libc, changer libc ? ;)

malloc est peu coûteux, plus coûteux que rien c'est sur, mais pas tant
que ça non plus, c'est très rarement le facteur limitant.

Par exemple, en embarqué, ils ne travaillent que sur des données
allouées statiquement, devine pourquoi ? (des indices ont été
données dans la première partie de ce paragraphe).


Parce qu'ils n'ont pas forcément d'implémentation de malloc ? ou
d'implémentation efficace de malloc ? Avoir un malloc efficace, ça
demande une infrastructure assez complexe. De plus, malloc n'est pas
déterliniste en temps d'exécution (si unb appel moyen à malloc est
assez rapide, certains appels à malloc peuvent être long). Mais bon,
on parle d'un cas très particulier là.

Le problème du buffer overflow n'est pas que le buffer soit fixe, c'est
savoir comment utiliser judicieusement le buffer qu'on a prévu (par
exemple buffer de taille PATH_MAX fixe) en tenant compte des paramètres
du système (PATH_MAX par exemple).


C'est aussi qu'il est sur la pile, et donc beaucoup plus dangereux
(car si on déborde, on tape sur la sauvegarde de EIP ou
équivalent). Les bugs il y en a, tout le monde fait des erreurs. La
sécurité passe bien sûr par éviter au maximum ces erreurs, mais aussi
en limiter les conséquences éevntuelles (en utilisant le moins de
privilèges possibles, en utilisant des données dans le tas plutôt que
dans la pile, ...)

Au final, si on veut obtenir de la sécurité, il vaut mieux
s'attacher à faire les choses soigneusement (vérifier les codes de
retour des appels susceptibles d'être en faute, par exemple,
malloc(), fopen()) qu'utiliser des faux biais de "sécurité" comme
ceux que tu viens de citer.


L'un n'empêche pas l'autre. La sécurité absolue n'existe pas, on ne
peut qu'augmenter la difficulté de trouver et d'exploiter une faille,
ainsi que diminuer les conséquences de ces failles. Oui, la sécurité
passe par faire les choses soigneusement. Mais ça passe aussi par
diminuer les risques au niveau de l'architecture du programme (en
particulier pour un OS...) et savoir que certaines choses (buffer dans
la pile) sont plus dangereuses que d'autres (buffer dans le tas), même
si un buffer dans le tas est dangereux aussi.

--
Gael Le Mignot "Kilobug" - - http://kilobug.free.fr
GSM : 06.71.47.18.22 (in France) ICQ UIN : 7299959
Fingerprint : 1F2C 9804 7505 79DF 95E6 7323 B66B F67B 7103 C5DA

Member of HurdFr: http://hurdfr.org - The GNU Hurd: http://hurd.gnu.org


1 2 3 4 5