OVH Cloud OVH Cloud

les tableux de char

9 réponses
Avatar
Tommy Boivin
je suis new et je voudraissavoir quelque chose:

Admettons que je crée un tableau de char de 250 elements et que je
n'initialise que les 10 premiers (avec un cin et une commande du string.h
(dont je ne me rapelle plus le nom)) et que le 11e char est le "\0".

Ce tableau va prendre la place en memoire de 10 char + "\0" ou de 250 char
(meme si j'en ai initialisé que 11) ???

Merci de me répondre!!!

- Tommy

9 réponses

Avatar
Loïc Joly
Tommy Boivin wrote:

je suis new et je voudraissavoir quelque chose:

Admettons que je crée un tableau de char de 250 elements et que je
n'initialise que les 10 premiers (avec un cin et une commande du string.h
(dont je ne me rapelle plus le nom)) et que le 11e char est le "".

Ce tableau va prendre la place en memoire de 10 char + "" ou de 250 char
(meme si j'en ai initialisé que 11) ???


250.
Au fait, pourquoi la valeur 250 et pas 42 ou 12345679 ?

Et réciproquement, si tu veux lui mettre plus de 250 caractères, boom
(c'est ce problème qui est à l'origine d'un certain nombre de trous de
sécurité qu'on peut trouver dans du code).

Tu peux géréer dynamiquement la mémoire de façon à allouer une zone
mémoire à la fois suffisante et de taille raisonnable. Mais c'est
relativement complexe. C'est pourquoi le C++ propose une classe qui gère
ça tout seul, la classe std::string.

Je dirais qu'en général (surtout dans du code de débutant), utiliser des
tableaux de caractères pour gérer des chaines de caractères est une
erreur de design, qui va probablement être à l'origine de nombreuses
erreurs de codage.

--
Loïc

Avatar
phneveu

Je dirais qu'en général (surtout dans du code de débutant), utiliser des
tableaux de caractères pour gérer des chaines de caractères est une erreur de
design, qui va probablement être à l'origine de nombreuses erreurs de codage.


Votre affirmation m'interpelle, comme on dit... Pourriez-vous me dire
sur quoi se fonde votre opinion, et ce que vous proposez comme
alternative?
Bien à vous.
--

--
phneveu

Pour une réponse directe, retirez RETIRER

Avatar
Benoit Rousseau
phneveu wrote:


Je dirais qu'en général (surtout dans du code de débutant), utiliser
des tableaux de caractères pour gérer des chaines de caractères est
une erreur de design, qui va probablement être à l'origine de
nombreuses erreurs de codage.



Votre affirmation m'interpelle, comme on dit... Pourriez-vous me dire
sur quoi se fonde votre opinion, et ce que vous proposez comme alternative?
Bien à vous.


Tout simplement parcequ'il n'y a pas de vérification sur l'indice d'un
tableau de caractères.
On peut très bien écrire
char my_string[64];
my_string[100] = 'a';
Tu ecrasses donc une partie de tas (?) et il se peut qu'à l'adresse
my_string[100] se trouve le pointeur de retour de fonction (pointeur qui
indique au programme où "sauter" quand le fonction se termine). On peut
donc changer cette valeur et lancer un autre bout de programme
(typiquement, lancer un shell sous Unix)
Il y a plein de programmes qui se font avoir, et ce sont des erreurs
assez dures à trouver.
sprintf( my_string, "une chaine de plus de 100 char..." );...
Cherche sur Google pour avoir plus d'info sur le buffer overflow...

Il faut éviter les fonctions strcpy(3), strcat(3), sprintf(3), scanf,
gets(3) et d'autres... strncpy, strncat, snprintf (?) résolvent le problème.

Mais le plus simple est encore d'utiliser les std::string qui gérent les
accès mémoires d'elle-même.


--
--------------------------------------------
Benoît Rousseau : roussebe at spray dot se
Jouez en programmant : http://realtimebattle.sourceforge.net/


Avatar
Loïc Joly
phneveu wrote:



Je dirais qu'en général (surtout dans du code de débutant), utiliser
des tableaux de caractères pour gérer des chaines de caractères est
une erreur de design, qui va probablement être à l'origine de
nombreuses erreurs de codage.



Votre affirmation m'interpelle, comme on dit... Pourriez-vous me dire
sur quoi se fonde votre opinion,


En général, l'utilisation de tableaux de char pour gérér des chaînes de
caractères peut avoir plusieurs raisons :

- Pour implémenter une classe chaîne de caractère ou assimilée -> C'est
une bonne raison d'utiliser ces tableaux, mais ce n'est pas du code
qu'on écrit tous les jours.

- Parce que le développeur ne sait pas que de meilleures alternatives
existent -> Mauvais choix

- Parce que le développeur a l'habitude de les utiliser, et les utilises
sans erreurs (ou du moins le croit) -> Ca reste un mauvais choix, ne
serait-ce que parce que les autres développeurs sur le même projet ne
sont pas tous à son niveau

- Pour des raisons de performance -> Je n'ai pas encore vu de cas où
c'était critique, et même si c'est le cas, comme on ne le sait qu'après
profiling, je commencerait quand même par coder avec les alternatives
avant que de remplacer ponctuellement par des tableaux de caractères

- Pour s'interfacer avec du code existant qui demande des tableaux de
char -> C'est aussi une utilisation légitime, mais dans ce cas, il faut
limiter l'usage de cette construction à l'interface, mais c'est tout.

Dans tout les cas, l'utilisation de tableaux de char à la main est très
sujette à erreur (personnellement, le temps de débugage de mes
programmes a été grandement diminué quand je suis passé à std::string),
et conduit à écrire beaucoup de code pour dire peu de choses.

A titre d'exemple, quelle est l'équivalent avec des tableaux de char du
code suivant :

std::string f()
{
std::string userInput;
std::getline(std::cin, userInput);
std::string line(2+userInput.size()+2, '*');
line += 'n';
return line + "* " + userInput + " *n" + line;
}

et ce que vous proposez comme alternative?


Il y en a plusieurs, std::string, qui est dans le standard et est donc
celle de choix, mais aussi toute une série de classe du même genre, pour
la plupart crées à une époque où std::string n'existait pas ou n'était
pas encore répandue, CString (MFC), QString (QT), AnsiString (BC++)...

--
Loïc


Avatar
phneveu
"Benoit Rousseau" a formulé la demande :
phneveu wrote:


Je dirais qu'en général (surtout dans du code de débutant), utiliser des
tableaux de caractères pour gérer des chaines de caractères est une erreur
de design, qui va probablement être à l'origine de nombreuses erreurs de
codage.



Votre affirmation m'interpelle, comme on dit... Pourriez-vous me dire sur
quoi se fonde votre opinion, et ce que vous proposez comme alternative?
Bien à vous.


Tout simplement parcequ'il n'y a pas de vérification sur l'indice d'un
tableau de caractères.
On peut très bien écrire
char my_string[64];
my_string[100] = 'a';
(...)



Tout à fait d'accord. Mais je n'imaginais pas que l'on pouvait écrire
dans un tableau de caractères (ou autre) sans se préoccuper des
indices, et il ne m'est pas venu à l'idée que la réticence de Loïc Joly
se limitait à cet aspect-là. Je croyais qu'il avait qqchose contre les
tableaux de char en eux-mêmes. D'où mon intérêt...

Il faut éviter les fonctions strcpy(3), strcat(3), sprintf(3), scanf, gets(3)
et d'autres... strncpy, strncat, snprintf (?) résolvent le problème.


strcpy(3)? strcat(3)? Voulez-vous dire "strcpy(), strcat() avec 3
arguments"? Je croyais qu'elles n'en prenaient que 2...
D'accord en ce qui concerne strncpy et strncat, bien que je leur
préfère memcpy, qui fait la même chose et a le mérite d'être
intrinsèque (mais peut-être ai-je tort...). Quant aux routines I/O,
j'avoue ma méconnaissance quasi totale du sujet. :/

Mais le plus simple est encore d'utiliser les std::string qui gérent les
accès mémoires d'elle-même.


Oui, mais comment résister à la tentation de les "wrapper" pour éviter
de
réecrire X fois la même chose? Et à partir de ce moment-là, comment ne
pas glisser dans l'implémentation d'une classe de service, bien que ce
soit fortement déconseillé par Loic Joly? Travaillant sous VisualC je
ne connais ni QString ni AnsiString, mais je connais CString, sa
lourdeur et sa gourmandise... Une classe écrite "à la main", pourvu
qu'elle le soit correctement, peut s'avérer plus intéressante tout en
offrant les mêmes services et en évitant même certaines erreurs
internes. Mais dans ce cas, on ne fait pas l'économie des contraintes
qu'exigent les tableaux de char, entre autres la vérification des
indices (je dis bien : entre autres!), même si on utilise des fonctions
comme strcpy ou memcpy, qui sont pourtant basées sur std::string.
Je croyais que Loïc Joly connaissait une recette qui permettait de
contourner ces écueils. D'où mon "post".

Amicalement à vous et Loïc Joly.

--
phneveu

Pour une réponse directe, retirez RETIRER



Avatar
Benoit Rousseau
phneveu wrote:
Tout simplement parcequ'il n'y a pas de vérification sur l'indice d'un
tableau de caractères.
On peut très bien écrire
char my_string[64];
my_string[100] = 'a';
(...)



Tout à fait d'accord. Mais je n'imaginais pas que l'on pouvait écrire
dans un tableau de caractères (ou autre) sans se préoccuper des indices,
Ici c'est vraiment très con, mais l'erreur peut être plus subtile...


et il ne m'est pas venu à l'idée que la réticence de Loïc Joly se
limitait à cet aspect-là. Je croyais qu'il avait qqchose contre les
tableaux de char en eux-mêmes. D'où mon intérêt...
Non, il donne d'autres raisons dans un autre post


strcpy(3)? strcat(3)? Voulez-vous dire "strcpy(), strcat() avec 3
arguments"? Je croyais qu'elles n'en prenaient que 2...
c'est le numéro des man pages sur Unix, c'est de là que je tirais les

noms, parceque je ne m'en souvenais plus tellement je ne les utilise
pas... ;-)

Mais le plus simple est encore d'utiliser les std::string qui gérent
les accès mémoires d'elle-même.


Oui, mais comment résister à la tentation de les "wrapper" pour éviter de
réecrire X fois la même chose? Et à partir de ce moment-là, comment ne
pas glisser dans l'implémentation d'une classe de service, bien que ce
soit fortement déconseillé par Loic Joly? Travaillant sous VisualC je ne
connais ni QString ni AnsiString, mais je connais CString, sa lourdeur
et sa gourmandise...


std::string est de ce type de "wrapper" : Il s'agit d'une classe de la
STL, décrite pas la norme C++ et qui devrait se trouver sur tout bon
compilateur C++ digne de ce nom (enfin, pas les vieux...)
Le nombre de fonctions est assez réduit, mais elle est souple et on peut
faire beaucoup avec. Pour les manipulations plus compliquées (comme la
conversion d'entier en chaines) j'utilise les stringstreams.


--------------------------------------------
Benoît Rousseau : roussebe at spray dot se
Jouez en programmant : http://realtimebattle.sourceforge.net/


Avatar
kanze
Loïc Joly wrote in message
news:<bqvd39$dcd$...

[...]

En général, l'utilisation de tableaux de char pour gérér des chaînes de
caractères peut avoir plusieurs raisons :


[...]

Et tu en as oublié la plus importante. L'initialisation d'un tableau du
genre char[] est statique -- c-à-d qu'elle aura lieu avant
l'initialisation dynamique.

Et qu'évidemment, une constante de chaîne est un tableau du type char
const[] ; bien des fois, ça ne vaut pas la peine de le convertir. (Donc,
par example, j'écrirais plutôt :

std::cout << "La valeur est : " << value << 'n' ;

et non :

std::cout << std::string( "La valeur est : " ) << value << 'n' ;

Mais j'imagine que toi aussi.)

[...]
Dans tout les cas, l'utilisation de tableaux de char à la main est
très sujette à erreur (personnellement, le temps de débugage de mes
programmes a été grandement diminué quand je suis passé à
std::string), et conduit à écrire beaucoup de code pour dire peu de
choses.


Tiens, chez moi, c'est l'inverse. Le temps de mise au point a largement
diminué quand j'ai passé du C en C++ (et donc de char*/char[] à
GB_String), le passage de GB_String à std::string en revanche l'a
légèrement augmenté (probablement parce que je n'avais pas une bonne
implémentation -- il existe aussi des implémentations où une erreur
d'indice provoque l'échec d'une assertion, comme dans GB_String.)

--
James Kanze GABI Software mailto:
Conseils en informatique orientée objet/ http://www.gabi-soft.fr
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France, +33 (0)1 30 23 45 16

Avatar
phneveu
"" a exposé le 08/12/2003 :
Loïc Joly wrote in message
news:<bqvd39$dcd$...

[...]

En général, l'utilisation de tableaux de char pour gérér des chaînes de
caractères peut avoir plusieurs raisons :


[...]

Et tu en as oublié la plus importante. L'initialisation d'un tableau du
genre char[] est statique -- c-à-d qu'elle aura lieu avant
l'initialisation dynamique.

Et qu'évidemment, une constante de chaîne est un tableau du type char
const[] ; bien des fois, ça ne vaut pas la peine de le convertir. (Donc,
par example, j'écrirais plutôt :

std::cout << "La valeur est : " << value << 'n' ;

et non :

std::cout << std::string( "La valeur est : " ) << value << 'n' ;

Mais j'imagine que toi aussi.)

[...]
Dans tout les cas, l'utilisation de tableaux de char à la main est
très sujette à erreur (personnellement, le temps de débugage de mes
programmes a été grandement diminué quand je suis passé à
std::string), et conduit à écrire beaucoup de code pour dire peu de
choses.


Tiens, chez moi, c'est l'inverse. Le temps de mise au point a largement
diminué quand j'ai passé du C en C++ (et donc de char*/char[] à
GB_String), le passage de GB_String à std::string en revanche l'a
légèrement augmenté (probablement parce que je n'avais pas une bonne
implémentation -- il existe aussi des implémentations où une erreur
d'indice provoque l'échec d'une assertion, comme dans GB_String.)


Loïc Joly a raison de dire que des tableaux de char "à la main" sont
source d'erreurs probables, pour ne pas dire certaines. Et qu'en tout
cas ils "conduisent à écrire beaucoup de code". On pourrait donc en
déduire que les alternatives présentées, "industrielles", sont un gage
de sécurité. Mais on peut légitimement se poser la question. Si elles
sont certainement plus sûres que la plupart des classes implémentant
des strings que l'on trouve sur les sites consacrés au développement,
elles n'en présentent pas moins, elles aussi, leurs péchés mignons.
Pour preuve ce fix de Microsoft concernant la fameuse std::string dont
on fait grand cas ici :

"When you assign a shorter string to an existing string that
originally contained a longer string, the assignment corrupts the
heap. [...] This problem is due to a bug in the Standard C++ Library
basic_string class implementation. When assigning a shorter string to
an existing string that originally contained a longer string, the heap
is corrupted. The assignment can be done either through operator=() or
assign()."

Ce bug affectait la version 5 de VisualC++ et a été corrigé dans la
version 6 (MS dixit). Il n'en reste pas moins vrai qu'on ne peut pas
accorder une confiance aveugle aux implémentations "plaquées or",
commme dirait Stroustrup.
Et que dire de la classe CString, qui vérifie bien la taille de ses
entrées (limitée à INT_MAX!), mais ne surveille pas la taille que peut
prendre la chaine en interne à la suite d'une ou pusieurs
concaténations. Faites-moi signe le jour où vous affecterez une chaîne
de 2147483647 caractères à une CString ou autre chose! En revanche, il
est très facile de déclarer une CString avant une boucle qui va la
concaténer 10 million de fois avec la même chaine de 25 caractères, et
avant même d'avoir atteint le fatidique INT_MAX, CString plante
lamentablement... Alors, à qui faire confiance?

Pour résumer, tout cela est bien difficile et donne à penser. :')

Bien à vous.

--
phneveu

Pour une réponse directe, retirez RETIRER


Avatar
kanze
phneveu wrote in message
news:...

[...]
Dans tout les cas, l'utilisation de tableaux de char à la main est
très sujette à erreur (personnellement, le temps de débugage de mes
programmes a été grandement diminué quand je suis passé à
std::string), et conduit à écrire beaucoup de code pour dire peu de
choses.


Tiens, chez moi, c'est l'inverse. Le temps de mise au point a
largement diminué quand j'ai passé du C en C++ (et donc de
char*/char[] à GB_String), le passage de GB_String à std::string en
revanche l'a légèrement augmenté (probablement parce que je n'avais
pas une bonne implémentation -- il existe aussi des implémentations
où une erreur d'indice provoque l'échec d'une assertion, comme dans
GB_String.)


Loïc Joly a raison de dire que des tableaux de char "à la main" sont
source d'erreurs probables, pour ne pas dire certaines.


Certainement. Tout ce que j'ai dit, c'est que personne n'a attendu la
norme pour remplacer la solution C avec quelque chose de plus solide. Et
que la solution dans la norme n'est pas betonnée non plus.

Et qu'en tout cas ils "conduisent à écrire beaucoup de code". On
pourrait donc en déduire que les alternatives présentées,
"industrielles", sont un gage de sécurité. Mais on peut légitimement
se poser la question. Si elles sont certainement plus sûres que la
plupart des classes implémentant des strings que l'on trouve sur les
sites consacrés au développement, elles n'en présentent pas moins,
elles aussi, leurs péchés mignons. Pour preuve ce fix de Microsoft
concernant la fameuse std::string dont on fait grand cas ici :

"When you assign a shorter string to an existing string that
originally contained a longer string, the assignment corrupts the
heap. [...] This problem is due to a bug in the Standard C++ Library
basic_string class implementation. When assigning a shorter string
to > an existing string that originally contained a longer string,
the heap > is corrupted. The assignment can be done either through
operator=() or > assign()."

Ce bug affectait la version 5 de VisualC++ et a été corrigé dans la
version 6 (MS dixit).


Tiens, je ne le connaissais pas celui-ci. Je sais que même en VC++ 6.0,
il y avait des problèmes si la chaîne contenait des ''.

Il n'en reste pas moins vrai qu'on ne peut pas accorder une confiance
aveugle aux implémentations "plaquées or", commme dirait Stroustrup.


Aucun logiciel aujourd'hui est sans erreurs. Pire : la plupart des
logiciels, commercials ou gratuits, ont un niveau de qualité lamentable,
qui donne l'impression que le groupe qui les a développé écrivait à peu
près n'importe quoi, puis bricolait avec le déboggueur jusqu'à ce que ça
marchotait dans les cas les plus évidents. Dans l'ensemble, VC++, et
surtout la bibliothèque de VC++, est parmi les mieux, mais c'est loin
d'être parfait. (N'empêche qu'il y a quand même du progrès. Je me
rappelle un époque où je trouvais plus d'erreurs dans le compilateur que
dans mon code.)

Il n'y a malheureusement pas de solution miracle.

Et que dire de la classe CString, qui vérifie bien la taille de ses
entrées (limitée à INT_MAX!), mais ne surveille pas la taille que peut
prendre la chaine en interne à la suite d'une ou pusieurs
concaténations. Faites-moi signe le jour où vous affecterez une chaîne
de 2147483647 caractères à une CString ou autre chose!


Ça c'est une erreur anodine -- tu ne peux pas (ou ne pouvais pas)
allouer autant de mémoire sous Windows.

En revanche, il est très facile de déclarer une CString avant une
boucle qui va la concaténer 10 million de fois avec la même chaine de
25 caractères, et avant même d'avoir atteint le fatidique INT_MAX,
CString plante lamentablement... Alors, à qui faire confiance?

Pour résumer, tout cela est bien difficile et donne à penser. :')


Le problème est complex. Sans vouloir découpabliser les fournisseurs de
compilateurs, dont la qualité est parfois lamentable, il faut bien dire
qu'ils n'ont pas été aidés par une spécification de langage qui pendant
longtemps n'a cessé de changer, ni par un marché où pour vendre, il faut
afficher des nouveautés, mais où la qualité ne joue aucun rôle.

--
James Kanze GABI Software mailto:
Conseils en informatique orientée objet/ http://www.gabi-soft.fr
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France, +33 (0)1 30 23 45 16