OVH Cloud OVH Cloud

empêcher la compilation de 'x'+"yz" ?

50 réponses
Avatar
Philippe Guglielmetti
J'ai perdu des heures à chasser un bug stupide du style
std::string bad='x'+"yz"; // donne tout sauf "xyz"
("yz" était en fait le résultat d'une conversion genre class::operator
char*() ...)

Existe-t-il une combine pour empêcher C++ de compiler des bêtises à la C ?

J'ai essayé de définir un operator+(const char lhs, const char* rhs) qui
râlerait, mais VC 7.1 dit qu'une des deux opérandes doit être une classe...
--
Philippe Guglielmetti - www.dynabits.com

10 réponses

1 2 3 4 5
Avatar
Fabien LE LEZ
On Thu, 19 Aug 2004 18:45:36 +0200, drkm :

Si je ne me trompe pas, le résultat est un char const *, pointant à
l'adresse de "yz" augmentée de la valeur entière 'x'.


Et rien n'empêche un compilo de servir un café au lait, puisqu'il
s'agit d'un comportement indéfini.


--
;-)

Avatar
drkm
Fabien LE LEZ writes:

On Thu, 19 Aug 2004 18:45:36 +0200, drkm :

Si je ne me trompe pas, le résultat est un char const *, pointant à
l'adresse de "yz" augmentée de la valeur entière 'x'.


Et rien n'empêche un compilo de servir un café au lait, puisqu'il
s'agit d'un comportement indéfini.


Le problème est qu'il peut également te le servir avec du sel en
guise de sucre, ce qui le fait moins bien passer ;-)

Mais si je comprend bien, le comportement indéfini vient du fait que
l'on sort des bornes du tableau (et du « one past the end »). Mais
ceci aurait le résultat escompté :

char c = 5 ;
std::string s = c + "12345xyz" ;

Non ?

--drkm, en recherche d'un stage : http://www.fgeorges.org/ipl/stage.html


Avatar
Fabien LE LEZ
On Thu, 19 Aug 2004 19:31:20 +0200, drkm :

Le problème est qu'il peut également te le servir avec du sel en
guise de sucre, ce qui le fait moins bien passer ;-)


De toutes façons, je déteste le café, qu'il soit sucré ou salé. Même
avec des frites.

Mais si je comprend bien, le comportement indéfini vient du fait que
l'on sort des bornes du tableau (et du « one past the end »).


Oui.

Mais
ceci aurait le résultat escompté :

char c = 5 ;
std::string s = c + "12345xyz" ;


"Escompté" ne me paraît pas convenir ici -- le résultat "logique"
serait "512345xyz". Mais le fait est que ce code met bien "xyz" dans
s.


--
;-)

Avatar
drkm
Fabien LE LEZ writes:

On Thu, 19 Aug 2004 19:31:20 +0200, drkm :

Mais ceci aurait le résultat escompté :

char c = 5 ;
std::string s = c + "12345xyz" ;


"Escompté" ne me paraît pas convenir ici -- le résultat "logique"
serait "512345xyz". Mais le fait est que ce code met bien "xyz" dans
s.


Lorsque je disais « escompté », je me référais à l'exemple du PO et
au résultat qu'il pensait obtenir.

Je ne sais pas s'il y a un résultat plus logique que l'autre. Si
l'on suppose que l'auteur des deux lignes de code ci-dessus connait
son affaire, je pense que le résultat /escompté/ est bien "xyz".

Mais il est vrai qu'a priori, si je vois ces mêmes lignes de code,
je ne dirais pas que son auteur connait son affaire.

--drkm, en recherche d'un stage : http://www.fgeorges.org/ipl/stage.html


Avatar
Fabien LE LEZ
On Thu, 19 Aug 2004 21:47:26 +0200, drkm :

Je ne sais pas s'il y a un résultat plus logique que l'autre. Si
l'on suppose que l'auteur des deux lignes de code ci-dessus connait
son affaire,


Peut-être. Je pense au pauvre relecteur qui essaie de décrypter le
code de l'auteur en question.

je pense que le résultat /escompté/ est bien "xyz".


Pour moi, operator + (..., char const*) devrait faire de la
concaténation -- surtout si l'autre argument est un char.

Même écrire

char const chaine[]= "Hello";
char x= ...;
...= chaine[x];

me semble à la limite de l'obfuscation -- généralement, un indice
devrait plutôt être un int.


--
;-)

Avatar
Philippe Guglielmetti
merci à tous pour vos réponses.
J'étais bien conscient que 'x' + "yz" fait de l'arithmétique de pointeurs.

A mon avis, il est anormal que char soit considéré comme un (short) int sans
le moindre warning.
Tout comme bool, char et wchar_t ne devraient pas être des intégraux, du
moins pas sans conversion explicite.

J'ai toujours milité pour que C++ soit considéré comme un langage moderne,
très différent de C, mais là y'a encore du boulot...
--
Philippe Guglielmetti - www.dynabits.com
Avatar
kanze
Falk Tannhäuser wrote in message
news:<cg2lso$tee$...

Fabien LE LEZ wrote:

Existe-t-il une combine pour empêcher C++ de compiler des bêtises à
la C ?


A priori, non. Mais il n'est pas impossible que certains compilos
génèrent un warning (modulo l'option qui va bien).


Avec 'gcc -Wall', j'ai bien un warning "array subscript has type `char'"
quand je fais
std::string bad = &'x'["yz"];
(qui est fonctionellement équivalent à
std::string bad = 'x' + "yz";
mais il me ne semble pas avoir de moyen pour faire générer un
warning dans ce cas)


On pourrait générer un avertissement chaque fois que le programmeur
essaie d'utiliser un char comme un petit entier:-). Le problème alors
sera, comment trouver les avertissements « intéressants » parmi
l'inondation.

Dans ce cas particulier, l'arithmétique ne comporte que des constantes
(un pointeur constant et un entier constant). Or, tous les compilateurs
que je connais font du « constant folding », c-à-d qu'ils évaluent
l'expression lors de la compilation, et non à l'exécution. C'est un peu
plus complex quand il y a un pointeur, évidemment, parce que constante
ou non, l'adresse exacte finale n'est pas connue du compilateur. Mais le
compilateur doit pouvoir détecter que le pointeur qui résulte de 'x' +
"yz" ne peut pas être valide, en aucun cas, et réfuser à compiler le
code. Je ne connais pas la structure interne de g++, mais j'imagine
qu'il serait même assez simple d'ajouter ce genre de vérification.

Ceci dit, est-ce que ce genre d'erreur est assez fréquente pour qu'ils
investissent l'effort nécessaire pour le détecter ?

Ce sont les joies de l'arithmétique des pointeurs combiné aux règles
loufoques de conversion tableau => pointeur, qui font que le code en
question *doit* être accepté par le compilateur, bien qu'il produise
un comportement indéfini.


Dans le cas général, tu as raison. Dans ce cas précis, en revanche, le
code a un comportement indéfini -- on génère un pointeur invalid, et le
compilateur a le droit même de ne pas le compiler.

--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34



Avatar
kanze
"Philippe Guglielmetti" wrote in message
news:<41259cbb$0$2796$...

A mon avis, il est anormal que char soit considéré comme un (short)
int sans le moindre warning. Tout comme bool, char et wchar_t ne
devraient pas être des intégraux, du moins pas sans conversion
explicite.


Et qu'est-ce qu'ils doivent être, alors ? Il faut bien, comme minimum,
que je puisse utiliser des char comme index dans un tableau. Y compris
un tableau de bits, ce qui impose au moins les opérateurs %, /, >> et
<<.

(Je ne trouve pas que la situation actuelle est optimal, mais je n'ai
pas vu de propositions qui l'aurait fait mieux.)

--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Avatar
Pierre Maurette
a écrit:

Falk Tannhäuser wrote in message
news:<cg2lso$tee$...

Fabien LE LEZ wrote:

Existe-t-il une combine pour empêcher C++ de compiler des bêtises à
la C ?


A priori, non. Mais il n'est pas impossible que certains compilos
génèrent un warning (modulo l'option qui va bien).


Avec 'gcc -Wall', j'ai bien un warning "array subscript has type `char'"
quand je fais
std::string bad = &'x'["yz"];
(qui est fonctionellement équivalent à
std::string bad = 'x' + "yz";
mais il me ne semble pas avoir de moyen pour faire générer un
warning dans ce cas)


On pourrait générer un avertissement chaque fois que le programmeur
essaie d'utiliser un char comme un petit entier:-). Le problème alors
sera, comment trouver les avertissements « intéressants » parmi
l'inondation.
1: const char offset_nom = sizeof("Pierre");// / sizeof(char);

2: std::string pluriel_debile = "Automobile" + 's';
3: std::string nom_prenom = "Pierre Maurette";
4: std::string mon_nom0 = offset_nom + "Pierre Maurette";
5: std::string mon_nom1 = offset_nom + nom_prenom.c_str();

Tout ça sans ouharning (gcc 3.3.1, -Wall, et VC7.1).

Si l'on considère que tous mes compilos me préviennent quand je fais:
if(ptr = fonction_qui_renvoie_un_pointeur()){...}
et logiquement se taisent avec:
if((ptr = fonction_qui_renvoie_un_pointeur()) != NULL){...}
le ligne 2 sent également la faute de frappe ou l'erreur de débutant à
plein nez. Elle mériterait un warning. En fait, je préfèrerais une
classe string qui refuse la ligne 2 et qui accepte:
NouvelleString pluriel_debile = "Automobile" + "s";

La ligne 3 me convient parfaitement. Alors que je me demandais si elle
était légale ;-)

La ligne 4 est à mon sens débile. Il faudrait au moins avertir (même
dans ce cas, où le compilo peut savoir que rien de fâcheux ne va se
passer). Si l'on veut faire du C, les classes string ont prévu ce
qu'il faut (ligne 5).

Et ce qui me déçoit, c'est que l'AnsiString de la VCL Borland a le
même comportement que std::string sur ce point. En revanche, Delphi et
la même VCL autorisent logiquement:

var
Test: AnsiString;
begin
Test := 'Pierre '+ 'Maurette';

La différence est facilement explicable, mais c'est chiant.


Dans ce cas particulier, l'arithmétique ne comporte que des constantes
(un pointeur constant et un entier constant). Or, tous les compilateurs
que je connais font du « constant folding », c-à-d qu'ils évaluent
l'expression lors de la compilation, et non à l'exécution. C'est un peu
plus complex quand il y a un pointeur, évidemment, parce que constante
ou non, l'adresse exacte finale n'est pas connue du compilateur.
Oui. Je pense que le compilateur connaît toutes les adresses des

données initialisées (ou même non initialisées) au pire à une
constante près, déterminée par le lieur puis le chargeur. On peut donc
affirmer qu'il a tous les éléments nécessaires et le programmeur peut
considérer ces adresses comme des constantes connues à la compilation.
int* ptr = &MaVar;
génère un truc du genre de ce qu'on peut faire directement en
assembleur:
mov eax, offset MaVar
offset MaVar est une constante immédiate, en tous cas se retrouvera
sous cette forme dans l'executable en mémoire (pas nécessairement dans
le point exe sur le disque).
Et il me semble que sur les archis que je connais (en fait, une seule,
ou à peu près :-() les choses sont encore plus simples, le travail du
lieur et du chargeur se réduit à positionner la valeur de segments ou
leurs descripteurs. Ainsi, le compilateur connait les valeurs
numériques des offset, c'est à dire des adresses.
--
Pierre




Avatar
Philippe Guglielmetti
a écrit:
Il faut bien, comme minimum,
que je puisse utiliser des char comme index dans un tableau.


Pourquoi faire? c'est hyper dangereux!
Tu penses comme un C-iste pour qui il y a un "typedef unsigned short int
char;" quelque part...
AMHA (à mon humble avis...), tableau['x'] devrait être interdit en C++,
surtout depuis que 'é'
n'a pas la même valeur selon les OS, et encore plus depuis Unicode.
Le bidouilleur nostalgique peut toujours faire tableau[(unsigned short)'x']
--
Philippe Guglielmetti - www.dynabits.com

1 2 3 4 5