OVH Cloud OVH Cloud

Question entretien C++ - doutes.

95 réponses
Avatar
David Fleury
Bonjour,

voici une des questions que je pose pour lors de mes entretiens pour des
développeurs avec expérience orienté C++

"Ecrire une fonction qui renverse une chaine (ex: ABC -> CBA, AE -> EA, ...)

Après avoir posé la question deux ou trois fois déjà, je suis surpris
des réponses (souvent fausses). Je laisse volontairement le choix au
candidat d'utiliser (char* ou std::string) et/ou une fonction modifiant
la chaine entrée ou retournant une nouvelle chaine, et l'utilisation de
la STL est autorisé.

A votre avis, est-ce que la question n'est pas trop mauvaise ?

David.

PS : Dans le même genre, il y a la fonction EstPalindrome qui semble
bloquer.

10 réponses

6 7 8 9 10
Avatar
Jean-Marc Bourguet
Wykaaa writes:

Jean-Marc Bourguet a écrit :
Wykaaa writes:

Jean-Marc Bourguet a écrit :
Wykaaa writes:





Les tableaux C sont déjà une source de confusion assez grande comme
cela; faire croire que le nom d'un tableau a un type pointeur ne fait
qu'augmenter la confusion -- en particulier en C++.


Désolé, mais "un type pointeur", ça ne veut rien dire puisque les pointeurs
sont... typés.



Bizarre, l'expression se trouve dans les normes C90, C++98, C99 et le
dernier brouillon disponible de C++0X avec le sens que je lui donne ici.



La norme ne devrait pas dire "type pointeur", elle devrait dire "de nature
pointeur".



Le mot est en italique dans la partie que j'ai regardé: la norme donne une
définition du terme. Au même endroit qu'elle défini les autres types
dérivés.

De toute façon, il y a belle lurette que je ne fais pas confiance à la
norme pour le vocabulaire car il semble qu'il n'a pas été porté une
attention suffisante sur ce point tout au long de la norme...



C'est le moment de donner tes commentaires.

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2798.pdf

Je voulais dire que le nom de tableau est manipulé comme un pointeur
(d'ailleurs du type des éléments).



Pas dans tous les contextes:

void f(int (&t)[10]);
template <typename T>
void g(T&);

int tab[10];

Dans aucune des expressions sizeof tab, &tab, typeid(tab), f(tab), g(tab)
tab n'est converti en un pointeur vers son premier élément.


Certes mais cependant tout le monde parle de l'équivalence pointeur/tableau
à propos de C. Donc c'est un peu jouer sur les mots de ne pas reconnaître
qu'il y a souvent confusion pour les utilisateurs.



Pour supprimer cette confusion, ma première action est de ne pas employer
cette expression et de montrer à quel point elle est impropre. Il y a déjà
assez de sources de confusion comme cela que pour en ajouter une qui n'est
pas intrinsèque au langage.

S'il n'y avait pas cette "équivalence" pointeur/tableau, il n'y aurait pas
besoin des [] pour indiquer à delete que le pointeur pointe sur un tableau
alloué dynamiquement afin d'appeler les destructeurs pour chaque instance.



J'ai du mal à comprendre ce que tu veux dire. Dans

int* t = new int[10];
delete[] t;

la conversion implicite d'un tableau en un pointeur n'intervient pas.


Certes mais il faut mettre les crochets car C, donc C++, n'est pas capable
de distinguer, de lui-même, entre un pointeur sur variable scalaire et un
pointeur sur tableau.



Ne referais-tu pas la même erreur de nomenclature que celle qui a lancé
cette discussion?

int* x;

int (*x)[10];



Il faut "l'aider", ce qui est anormal. Si le langage
était "bien fichu" il n'y aurait pas besoin de cette syntaxe pour le moins
bizarre. Il n'y a qu'à voir le nombre de questions sur ce sujet dans les
différents fora Internet.
On
la virerait et on permettrait d'appliquer [] sur les tableaux qu'il y a peu
de choses qui changerait à part les appels de fonctions.



Oui mais les appels de fonction, c'est quand même important non ?



On aurait fait pour les tableaux ce qui a été fait pour les structures --
permettre le passage par valeur, être obligé d'utiliser & pour passer un
pointeur -- il y aurait beaucoup de confusion en moins sans changer
fondamentalement le rapport entre tableaux et pointeurs (dont la
substantifique moelle est pour moi l'arithmétique sur les pointeurs).

Par ailleurs, l'objectif de simplifier la conversion du code B existant a
été source d'autres mauvais choix comme la priorité des opérateurs, en
particulier pour les opérateurs sur bits (&) par rapport à = ou ==.



Ah ce qu'on garde par soucis de faciliter la transition même si ce n'est
pas idéal et qu'il faudra vivre avec -- on le sait bien souvent au moment
même ou on prend la décision, mais on a l'impression, souvent fondée, que
c'est nécessaire pour vivre longtemps... et ce n'est pas propre au C
(envers B et BCPL) et au C++ (envers le C) ni même aux langages ou à
l'informatique.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org
Avatar
James Kanze
On Nov 3, 1:45 pm, Wykaaa wrote:
Jean-Marc Bourguet a écrit :
> Wykaaa writes:



>> Jean-Marc Bourguet a écrit :
>>> Wykaaa writes:



>>>> Jean-Marc Bourguet a écrit :
>>>>> Wykaaa writes:
>>>>>  Les tableaux C sont déjà une source de confusion assez grand e comme
>>>>> cela; faire croire que le nom d'un tableau a un type pointeur ne fa it
>>>>> qu'augmenter la confusion -- en particulier en C++.
>>>> Désolé, mais "un type pointeur", ça ne veut rien dire
>>>> puisque les pointeurs sont... typés.
>>> Bizarre, l'expression se trouve dans les normes C90,
>>> C++98, C99 et le dernier brouillon disponible de C++0X
>>> avec le sens que je lui donne ici.
>> La norme ne devrait pas dire "type pointeur", elle devrait
>> dire "de nature pointeur".



> Le mot est en italique dans la partie que j'ai regardé: la
> norme donne une définition du terme.  Au même endroit
> qu'elle défini les autres types dérivés.



J'ai cherché... et je n'ai pas trouvé d'endroit où "pointer
type" était en italique dns la référence que tu m'as fournie
plus loin.



C'est vrai que dans la définition (§3.9.2), elle parle de
« pointers », et non « pointer types », mais vu le nom de l a
section (« Composite types »), je crois que c'est clair que ce
dont elle parle ici, ce sont des types pointeur (et non des
variables pointeurs, etc.).

>> De toute façon, il y a belle lurette que je ne fais pas
>> confiance à la norme pour le vocabulaire car il semble
>> qu'il n'a pas été porté une attention suffisante sur ce
>> point tout au long de la norme...



> C'est le moment de donner tes commentaires.



>http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2798.pdf



Pffft : 1350 pages, je n'ai pas le courage...
Il n'y a pas de vrai glossaire C++ dans la norme. C'est très
regrettable. Quantité de termes apparemment techniques qui
figurent dans le texte ne sont définis nulle part.



En tant qu'annex, je suis bien d'accord. Tu te portes volentaire
pour le réaliser ?

[...]
>>>> S'il n'y avait pas cette "équivalence" pointeur/tableau,
>>>> il n'y aurait pas besoin des [] pour indiquer à delete
>>>> que le pointeur pointe sur un tableau alloué
>>>> dynamiquement afin d'appeler les destructeurs pour chaque
>>>> instance.
>>> J'ai du mal à comprendre ce que tu veux dire.  Dans



>>> int* t = new int[10];
>>> delete[] t;



>>> la conversion implicite d'un tableau en un pointeur
>>> n'intervient pas.
>> Certes mais il faut mettre les crochets car C, donc C++,
>> n'est pas capable de distinguer, de lui-même, entre un
>> pointeur sur variable scalaire et un pointeur sur tableau.



C'est parce qu'ici, il n'y a pas de pointeur sur tableau.

> Ne referais-tu pas la même erreur de nomenclature que celle
> qui a lancé cette discussion?



> int* x;



> int (*x)[10];



int* i = new int;
int* p = new int[10];
delete i;
delete [] p;
Pourquoi les [] seraient-ils nécessaires?
C'est cela que je voulais dire.



Parce que la norme l'exige. Il n'y a pas d'autre raison. La
norme distingue deux types d'opérateur new qui renvoient un T*,
selon le type alloué. Et donc, deux types d'opérateur delete.

C'est bête, mais dans la pratique, je ne me suis jamais servi
d'un new [] ni d'un delete []. Qu'un débuttant ne sache même pas
qu'ils existent ne me gènerait pas outremesure.

>> Il faut "l'aider", ce qui est anormal. Si le langage était
>> "bien fichu" il n'y aurait pas besoin de cette syntaxe pour
>> le moins bizarre. Il n'y a qu'à voir le nombre de questions
>> sur ce sujet dans les différents fora Internet.



>>>On la virerait et on permettrait d'appliquer [] sur les
>>>tableaux qu'il y a peu de choses qui changerait à part les
>>>appels de fonctions.
>> Oui mais les appels de fonction, c'est quand même important
>> non ?



> On aurait fait pour les tableaux ce qui a été fait pour les
> structures -- permettre le passage par valeur, être obligé
> d'utiliser & pour passer un pointeur -- il y aurait beaucoup
> de confusion en moins sans changer fondamentalement le
> rapport entre tableaux et pointeurs (dont la substantifique
> moelle est pour moi l'arithmétique sur les pointeurs).



Ben voilà. Ca serait déjà beaucoup mieux.



Sauf qu'au départ, il y avait le C. Qui lui évolue de B.

On en a discuté dans le comité de normalisation. Tout le monde
était d'accord que les tableaux de type C, c'est un désastre.
Seulement, comment l'améliorer sans briser la comptabilité C. À
la fin, la décision était de les laisser tels quels, en
fournissant des altérnatifs nouveaux supérieur (genre
std::vector).

Je relève quand même dans la norme que :
4.2 Array-to-pointer conversion [conv.array]
1 An lvalue or rvalue of type “array of N T” or “array of
unknown bound of T” can be converted to an rvalue of type
“pointer to T”. The result is a pointer to the first element
of the array. Le chapitre 4 traite des conversions standards
et commence par "Standard conversions are implicit conversions
defined for built-in types". Qu'on le veuille ou non, la
conversion d'un nom de tableau en pointeur sur son premier
élément est bien im-pli-cite et, donc, standard.



Certes. Qui a dit le contraire ?

>> Par ailleurs, l'objectif de simplifier la conversion du
>> code B existant a été source d'autres mauvais choix comme
>> la priorité des opérateurs, en particulier pour les
>> opérateurs sur bits (&) par rapport  à = ou ==.



> Ah ce qu'on garde par soucis de faciliter la transition même
> si ce n'est pas idéal et qu'il faudra vivre avec -- on le
> sait bien souvent au moment même ou on prend la décision,
> mais on a l'impression, souvent fondée, que c'est nécessaire
> pour vivre longtemps... et ce n'est pas propre au C (envers
> B et BCPL) et au C++ (envers le C) ni même aux langages ou à
> l'informatique.



En C, lors de la normalisation, on s'est bien débarrassé du
trait de langage très ennuyeux qui consistait à pouvoir faire
un branchement au milieu d'un bloc d'instruction. En effet,
dans ce cas, le C dit de K&R, avait la sémantique curieuse
qu'il fallait allouer les variables locales déclarées
éventuellement dans le bloc mais s'il y avait des
initialisations, le compilateur n'était pas obligé de les
faire lors du branchement...



J'ai dû mal comprendre. J'ai l'impression que tu dis qu'on s'est
débarrassé de quelque chose comme :

void
f()
{
goto toto ;
{
int i ;
toto:
i = 43 ;
}
}

Or, c'est un programme on ne peut plus légal. (Pas beau du tout,
mais légal, quand même.)

Il y a des décisions apparemment anodines qui coûtent très
cher par la suite quand on normalise.
Les choix de priorités des opérateurs (en particulier les
opérateurs bit à bit) en est un.



Je suis bien d'accord avec toi, mais ça date. (Voir
http://www.quut.com/c/dmr-on-or.html#main pour la question des
priorités, par exemple.) La compatibilité avec C, c'est
probablement la raison principale pour la réussite de C++. C'est
aussi, malheureusement, la source de pas mal de problèmes et
d'ambiguïtés ; je crois qu'on peut dire que le C n'a pas
vraiment été conçu de façon à servir de base de tant de
languages.

J'ai eu l'occasion d'auditer de très nombreux programmes C++.
C'est une des erreurs les plus fréquentes que j'ai rencontrée.



Qu'est-ce qui est l'une des erreurs les plus fréquentes ?
D'utiliser les tableaux de type C quand il existe de meilleurs
alternatifs ?

--
James Kanze (GABI Software) email:
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
James Kanze
On Nov 3, 4:47 pm, Wykaaa wrote:
Michael DOUBEZ a écrit :
[snip]



>> int* i = new int;
>> int* p = new int[10];
>> delete i;
>> delete [] p;
>> Pourquoi les [] seraient-ils nécessaires?
>> C'est cela que je voulais dire.



> Parce que la norme le dit en 5.3.4/5

Ca, ce n'est pas un argument. La vraie question est : pourquoi
est-ce nécessaire ?



Pour des raisons historiques. Personne n'a trouvé qu'il valait
la peine à améliorer, sans doute parce que personne n'a encore
trouver une utilisation pour new int[].

--
James Kanze (GABI Software) email:
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
Wykaaa
James Kanze a écrit :
On Nov 3, 1:45 pm, Wykaaa wrote:
Jean-Marc Bourguet a écrit :
Wykaaa writes:





Jean-Marc Bourguet a écrit :
Wykaaa writes:









Jean-Marc Bourguet a écrit :
Wykaaa writes:
Les tableaux C sont déjà une source de confusion assez grande comme
cela; faire croire que le nom d'un tableau a un type pointeur ne fait
qu'augmenter la confusion -- en particulier en C++.


Désolé, mais "un type pointeur", ça ne veut rien dire
puisque les pointeurs sont... typés.


Bizarre, l'expression se trouve dans les normes C90,
C++98, C99 et le dernier brouillon disponible de C++0X
avec le sens que je lui donne ici.


La norme ne devrait pas dire "type pointeur", elle devrait
dire "de nature pointeur".







Le mot est en italique dans la partie que j'ai regardé: la
norme donne une définition du terme. Au même endroit
qu'elle défini les autres types dérivés.





J'ai cherché... et je n'ai pas trouvé d'endroit où "pointer
type" était en italique dns la référence que tu m'as fournie
plus loin.



C'est vrai que dans la définition (§3.9.2), elle parle de
« pointers », et non « pointer types », mais vu le nom de la
section (« Composite types »), je crois que c'est clair que ce
dont elle parle ici, ce sont des types pointeur (et non des
variables pointeurs, etc.).



Il faut vraiment lire entre les lignes !
Décidément, je n'aime pas du tout le terme "pointer type" qui sème la
confusion.

De toute façon, il y a belle lurette que je ne fais pas
confiance à la norme pour le vocabulaire car il semble
qu'il n'a pas été porté une attention suffisante sur ce
point tout au long de la norme...







C'est le moment de donner tes commentaires.





http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2798.pdf





Pffft : 1350 pages, je n'ai pas le courage...
Il n'y a pas de vrai glossaire C++ dans la norme. C'est très
regrettable. Quantité de termes apparemment techniques qui
figurent dans le texte ne sont définis nulle part.



En tant qu'annex, je suis bien d'accord. Tu te portes volentaire
pour le réaliser ?



Je sais, c'est facile de critiquer mais je n'ai pas de temps pour ça,
hélas...

[...]
S'il n'y avait pas cette "équivalence" pointeur/tableau,
il n'y aurait pas besoin des [] pour indiquer à delete
que le pointeur pointe sur un tableau alloué
dynamiquement afin d'appeler les destructeurs pour chaque
instance.


J'ai du mal à comprendre ce que tu veux dire. Dans









int* t = new int[10];
delete[] t;









la conversion implicite d'un tableau en un pointeur
n'intervient pas.


Certes mais il faut mettre les crochets car C, donc C++,
n'est pas capable de distinguer, de lui-même, entre un
pointeur sur variable scalaire et un pointeur sur tableau.







C'est parce qu'ici, il n'y a pas de pointeur sur tableau.

Ne referais-tu pas la même erreur de nomenclature que celle
qui a lancé cette discussion?





int* x;





int (*x)[10];





int* i = new int;
int* p = new int[10];
delete i;
delete [] p;
Pourquoi les [] seraient-ils nécessaires?
C'est cela que je voulais dire.



Parce que la norme l'exige. Il n'y a pas d'autre raison. La
norme distingue deux types d'opérateur new qui renvoient un T*,
selon le type alloué. Et donc, deux types d'opérateur delete.



Ce que je ne comprends pas c'est pourquoi la norme l'exige. Quelle est
la raison à l'origine de cette décision ?

C'est bête, mais dans la pratique, je ne me suis jamais servi
d'un new [] ni d'un delete []. Qu'un débuttant ne sache même pas
qu'ils existent ne me gènerait pas outremesure.



C'est bête car je préconise que tout delete soit accompagné des crochets
pour faciliter la maintenance.
En effet :
classe Client {...};
Client* cl = new Client;
plus loin : delete cl;
Un jour on change la déclaration de cl : Client* cl = new Client [10];
delete cl ne détruit pas les instances de Client créées dans le tableau
alloué dynamiquement.
Normalement, delete [] xxx est accepté, même si xxx ne pointe pas sur un
tableau.

Il faut "l'aider", ce qui est anormal. Si le langage était
"bien fichu" il n'y aurait pas besoin de cette syntaxe pour
le moins bizarre. Il n'y a qu'à voir le nombre de questions
sur ce sujet dans les différents fora Internet.







On la virerait et on permettrait d'appliquer [] sur les
tableaux qu'il y a peu de choses qui changerait à part les
appels de fonctions.


Oui mais les appels de fonction, c'est quand même important
non ?







On aurait fait pour les tableaux ce qui a été fait pour les
structures -- permettre le passage par valeur, être obligé
d'utiliser & pour passer un pointeur -- il y aurait beaucoup
de confusion en moins sans changer fondamentalement le
rapport entre tableaux et pointeurs (dont la substantifique
moelle est pour moi l'arithmétique sur les pointeurs).





Ben voilà. Ca serait déjà beaucoup mieux.



Sauf qu'au départ, il y avait le C. Qui lui évolue de B.



Pour moi, ce n'était pas une raison suffisante. Le processus de
normalisation n'est souvent qu'une série de compromis entre "marchands
de tapis".

On en a discuté dans le comité de normalisation. Tout le monde
était d'accord que les tableaux de type C, c'est un désastre.



C'est le moins qu'on puisse dire !

Seulement, comment l'améliorer sans briser la comptabilité C. À
la fin, la décision était de les laisser tels quels, en
fournissant des altérnatifs nouveaux supérieur (genre
std::vector).


D'accord, cette décision n'a pas brisé la compatibilité, mais pour être
"propre", il faudrait donc transformer toutes les utilisations des
tableaux C en vector lors du passage de C à C++ ou ne jamais utiliser
les tableaux C en C++. Dans ce cas, ça ne servait à rien de conserver la
compatibilité...

Je relève quand même dans la norme que :
4.2 Array-to-pointer conversion [conv.array]
1 An lvalue or rvalue of type “array of N T” or “array of
unknown bound of T” can be converted to an rvalue of type
“pointer to T”. The result is a pointer to the first element
of the array. Le chapitre 4 traite des conversions standards
et commence par "Standard conversions are implicit conversions
defined for built-in types". Qu'on le veuille ou non, la
conversion d'un nom de tableau en pointeur sur son premier
élément est bien im-pli-cite et, donc, standard.



Certes. Qui a dit le contraire ?


La formulation de Jean-Marc Bourguet peut le laisser croire, quelque
part, dans ce fil, je cite : "faire croire que le nom d'un
tableau a un type pointeur ne fait qu'augmenter la confusion -- en
particulier en C++."

Par ailleurs, l'objectif de simplifier la conversion du
code B existant a été source d'autres mauvais choix comme
la priorité des opérateurs, en particulier pour les
opérateurs sur bits (&) par rapport à = ou ==.







Ah ce qu'on garde par soucis de faciliter la transition même
si ce n'est pas idéal et qu'il faudra vivre avec -- on le
sait bien souvent au moment même ou on prend la décision,
mais on a l'impression, souvent fondée, que c'est nécessaire
pour vivre longtemps... et ce n'est pas propre au C (envers
B et BCPL) et au C++ (envers le C) ni même aux langages ou à
l'informatique.





En C, lors de la normalisation, on s'est bien débarrassé du
trait de langage très ennuyeux qui consistait à pouvoir faire
un branchement au milieu d'un bloc d'instruction. En effet,
dans ce cas, le C dit de K&R, avait la sémantique curieuse
qu'il fallait allouer les variables locales déclarées
éventuellement dans le bloc mais s'il y avait des
initialisations, le compilateur n'était pas obligé de les
faire lors du branchement...



J'ai dû mal comprendre. J'ai l'impression que tu dis qu'on s'est
débarrassé de quelque chose comme :

void
f()
{
goto toto ;
{
int i ;
toto:
i = 43 ;
}
}

Or, c'est un programme on ne peut plus légal. (Pas beau du tout,
mais légal, quand même.)



Non, j'ai parlé d'initialisation et non d'affectation. Le cas que je
décrivais est :

void
> f()
> {
> goto toto ;
> {
> int i = 43 ;
> toto:
> ...
une utilisation de i
...
> }
> }

L'ancien C disait que, dans ce cas,quand on fait le branchement à toto,
i doit avoir été alloué dans le bloc mais l'initialisation n'est pas
forcément effectuée...


Il y a des décisions apparemment anodines qui coûtent très
cher par la suite quand on normalise.
Les choix de priorités des opérateurs (en particulier les
opérateurs bit à bit) en est un.



Je suis bien d'accord avec toi, mais ça date. (Voir
http://www.quut.com/c/dmr-on-or.html#main pour la question des
priorités, par exemple.) La compatibilité avec C, c'est
probablement la raison principale pour la réussite de C++. C'est
aussi, malheureusement, la source de pas mal de problèmes et
d'ambiguïtés ; je crois qu'on peut dire que le C n'a pas
vraiment été conçu de façon à servir de base de tant de
languages.



C était à l'origine une "commodité" pour éviter que K&R code Unix en
assembleur. Le langage C n'était pas du tout destiné au public (et ça se
voit !). Il aurait dû resté interne aux Bell Labs...

J'ai eu l'occasion d'auditer de très nombreux programmes C++.
C'est une des erreurs les plus fréquentes que j'ai rencontrée.



Qu'est-ce qui est l'une des erreurs les plus fréquentes ?
D'utiliser les tableaux de type C quand il existe de meilleurs
alternatifs ?



Non la confusion sur la priorités des opérateurs, dans des expressions
comme :
x & ox00FF + 5 où l'intention du programmeur était de faire
(x & ox00FF) + 5 mais ceci est interprété comme : x & (ox00FF + 5) car +
est plus prioritaire que &, ce qui est une grosse erreur de conception
du langage.

De toute façon, K&R étaient très bons en système mais assez nuls en
langage (comme souvent chez les "hommes système").
Je sais que je vais faire bondir pas mal de personnes en disant cela...
Avatar
Jean-Marc Bourguet
Wykaaa writes:

Jean-Marc Bourguet a écrit :
> Le mot est en italique dans la partie que j'ai regardé: la norme donne une
> définition du terme. Au même endroit qu'elle défini les autres types
> dérivés.

J'ai cherché... et je n'ai pas trouvé d'endroit où "pointer type" était en
italique dns la référence que tu m'as fournie plus loin.



Flute j'avais la norme C sous les yeux. Dans le paragraphe correspondant
de celle du C++ le type n'y est plus.

>> De toute façon, il y a belle lurette que je ne fais pas confiance à la
>> norme pour le vocabulaire car il semble qu'il n'a pas été porté une
>> attention suffisante sur ce point tout au long de la norme...
> C'est le moment de donner tes commentaires.
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2798.pdf

Pffft : 1350 pages, je n'ai pas le courage...



Il n'y a pas de vrai glossaire C++ dans la norme. C'est très
regrettable. Quantité de termes apparemment techniques qui figurent dans le
texte ne sont définis nulle part.



Ils sont definis dans le texte.

> Ne referais-tu pas la même erreur de nomenclature que celle qui a lancé
> cette discussion?
> int* x;
> int (*x)[10];
>
int* i = new int;
int* p = new int[10];
delete i;
delete [] p;
Pourquoi les [] seraient-ils nécessaires?
C'est cela que je voulais dire.



C'est bien ce que je disais, meme genre de probleme de nomenclature.

Ben voilà. Ca serait déjà beaucoup mieux.



Personne n'a jamais dit que les rapports entre pointeurs et tableaux
etaient parfaits. Mon point est que l'utilisation d'une terminologie
incorrecte et source elle meme de confusion (parler d'equivalence) ne regle
rien.

Qu'on le veuille ou non, la conversion d'un nom de tableau en pointeur sur
son premier élément est bien im-pli-cite et, donc, standard.



Et un double est converti en un int implicitement aussi. Il n'y a pas
d'equivalence entre les deux.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org
Avatar
Michael DOUBEZ
Wykaaa a écrit :
James Kanze a écrit :
On Nov 3, 1:45 pm, Wykaaa wrote:




[snip]
int* i = new int;
int* p = new int[10];
delete i;
delete [] p;
Pourquoi les [] seraient-ils nécessaires?
C'est cela que je voulais dire.



Parce que la norme l'exige. Il n'y a pas d'autre raison. La
norme distingue deux types d'opérateur new qui renvoient un T*,
selon le type alloué. Et donc, deux types d'opérateur delete.



Ce que je ne comprends pas c'est pourquoi la norme l'exige. Quelle est
la raison à l'origine de cette décision ?



Je suppose que ça viens du même problème que la conversion implicite: si
j'écris une fonction pour effacer un tableau de taille inconnue, je dois
passer le tableau en paramètre et donc il passe par pointeur. Il faut
donc bien un moyen de signifier à delete qu'il s'agit d'un tableau:

void dispose_of(int* table)
{
delete[] table;
}


C'est bête, mais dans la pratique, je ne me suis jamais servi
d'un new [] ni d'un delete []. Qu'un débuttant ne sache même pas
qu'ils existent ne me gènerait pas outremesure.



C'est bête car je préconise que tout delete soit accompagné des crochets
pour faciliter la maintenance.
En effet :
classe Client {...};
Client* cl = new Client;
plus loin : delete cl;



Auquel je préfère:
scoped_ptr<Client> cli(new Client);

Un jour on change la déclaration de cl : Client* cl = new Client [10];
delete cl ne détruit pas les instances de Client créées dans le tableau
alloué dynamiquement.



Auquel je substitue:
scoped_array<Client> cli(new Client [10]);

Nous sommes d'accord que toute dépendance est source de problème. C'est
bien pour ça que C++0x donne tout un ensemble d'outils pour rendre la
vie plus facile (auto,decltype ...)

Normalement, delete [] xxx est accepté, même si xxx ne pointe pas sur un
tableau.



Accepté par le compilateur peut être. Ce genre de chose est impossible à
vérifier à la compilation.

[snip]
Ben voilà. Ca serait déjà beaucoup mieux.



Sauf qu'au départ, il y avait le C. Qui lui évolue de B.



Pour moi, ce n'était pas une raison suffisante. Le processus de
normalisation n'est souvent qu'une série de compromis entre "marchands
de tapis".

On en a discuté dans le comité de normalisation. Tout le monde
était d'accord que les tableaux de type C, c'est un désastre.



C'est le moins qu'on puisse dire !

Seulement, comment l'améliorer sans briser la comptabilité C. À
la fin, la décision était de les laisser tels quels, en
fournissant des altérnatifs nouveaux supérieur (genre
std::vector).


D'accord, cette décision n'a pas brisé la compatibilité, mais pour être
"propre", il faudrait donc transformer toutes les utilisations des
tableaux C en vector lors du passage de C à C++ ou ne jamais utiliser
les tableaux C en C++. Dans ce cas, ça ne servait à rien de conserver la
compatibilité...



Ce qui pose la question des VLAs pour C++0x. Je comprends que ça puisse
rendre la vie de compilateurs plus difficile mais c'est AMA là où il y a
le plus de travail quand on porte du code C99 vers C++ (ou quand le code
à été écrit sur un compilateur souple).

[snip]
Je relève quand même dans la norme que :
4.2 Array-to-pointer conversion [conv.array]
1 An lvalue or rvalue of type “array of N T” or “array of
unknown bound of T” can be converted to an rvalue of type
“pointer to T”. The result is a pointer to the first element
of the array. Le chapitre 4 traite des conversions standards
et commence par "Standard conversions are implicit conversions
defined for built-in types". Qu'on le veuille ou non, la
conversion d'un nom de tableau en pointeur sur son premier
élément est bien im-pli-cite et, donc, standard.



Certes. Qui a dit le contraire ?


La formulation de Jean-Marc Bourguet peut le laisser croire, quelque
part, dans ce fil, je cite : "faire croire que le nom d'un
tableau a un type pointeur ne fait qu'augmenter la confusion -- en
particulier en C++."



Oui. Un tableau a un type 'tableau' et un pointeur a un type 'pointeur'.
Qu'il y ait une conversion implicite et qu'on puisse utiliser
l'opérateur [] avec un pointeur ne crée pas une identité des types.

[snip]
Il y a des décisions apparemment anodines qui coûtent très
cher par la suite quand on normalise.
Les choix de priorités des opérateurs (en particulier les
opérateurs bit à bit) en est un.



Je suis bien d'accord avec toi, mais ça date. (Voir
http://www.quut.com/c/dmr-on-or.html#main pour la question des
priorités, par exemple.) La compatibilité avec C, c'est
probablement la raison principale pour la réussite de C++. C'est
aussi, malheureusement, la source de pas mal de problèmes et
d'ambiguïtés ; je crois qu'on peut dire que le C n'a pas
vraiment été conçu de façon à servir de base de tant de
languages.



C était à l'origine une "commodité" pour éviter que K&R code Unix en
assembleur. Le langage C n'était pas du tout destiné au public (et ça se
voit !). Il aurait dû resté interne aux Bell Labs...



Le C et les autres langages /brackets/ sont aujourd'hui ceux
principalement utilisées dans l'industrie. Pas forcément les plus
adaptés et les plus visuellement plaisant mais l'establishment est là;
ça leur donne quand même une certaine valeur.

J'ai eu l'occasion d'auditer de très nombreux programmes C++.
C'est une des erreurs les plus fréquentes que j'ai rencontrée.



Qu'est-ce qui est l'une des erreurs les plus fréquentes ?
D'utiliser les tableaux de type C quand il existe de meilleurs
alternatifs ?



Non la confusion sur la priorités des opérateurs, dans des expressions
comme :
x & ox00FF + 5 où l'intention du programmeur était de faire
(x & ox00FF) + 5 mais ceci est interprété comme : x & (ox00FF + 5) car +
est plus prioritaire que &, ce qui est une grosse erreur de conception
du langage.



Mais ça permet d'écrire:
if( a < b+1 & a > c ) ...

Et ça aurait peut être causé plus de confusion si ça n'avait pas été
interchangeable avec:
if( a < b+1 && a > c )...

De toute façon, K&R étaient très bons en système mais assez nuls en
langage (comme souvent chez les "hommes système").



Des langages comme fortran, cobol, algol ou lisp n'étaient pas non plus
adaptés à la programmation système.

Concernant les "hommes système", je ne sais pas, je ne suis pas
spécialiste, mais je pense qu'en ce qui concerne K&R, ils ont créé un
langage adapté à leur besoin.

AMA la théorie du langage se heurte parfois au mur de la réalité et
reproche juste au mur d'être un peu trop solide :).

--
Michael
Avatar
Wykaaa
Michael DOUBEZ a écrit :
Wykaaa a écrit :
James Kanze a écrit :
On Nov 3, 1:45 pm, Wykaaa wrote:




[snip]
int* i = new int;
int* p = new int[10];
delete i;
delete [] p;
Pourquoi les [] seraient-ils nécessaires?
C'est cela que je voulais dire.



Parce que la norme l'exige. Il n'y a pas d'autre raison. La
norme distingue deux types d'opérateur new qui renvoient un T*,
selon le type alloué. Et donc, deux types d'opérateur delete.



Ce que je ne comprends pas c'est pourquoi la norme l'exige. Quelle est
la raison à l'origine de cette décision ?



Je suppose que ça viens du même problème que la conversion implicite: si
j'écris une fonction pour effacer un tableau de taille inconnue, je dois
passer le tableau en paramètre et donc il passe par pointeur. Il faut
donc bien un moyen de signifier à delete qu'il s'agit d'un tableau:

void dispose_of(int* table)
{
delete[] table;
}


Ce n'est pas un bon exemple car ici c'est un tableau de int qui ne sont
pas des instances de classe. Les [] dans delete sont là pour que le
destructeur des instances de la classe soit appelé sur chacun des
éléments du tableau.


C'est bête, mais dans la pratique, je ne me suis jamais servi
d'un new [] ni d'un delete []. Qu'un débuttant ne sache même pas
qu'ils existent ne me gènerait pas outremesure.



C'est bête car je préconise que tout delete soit accompagné des
crochets pour faciliter la maintenance.
En effet :
classe Client {...};
Client* cl = new Client;
plus loin : delete cl;



Auquel je préfère:
scoped_ptr<Client> cli(new Client);

Un jour on change la déclaration de cl : Client* cl = new Client [10];
delete cl ne détruit pas les instances de Client créées dans le
tableau alloué dynamiquement.



Auquel je substitue:
scoped_array<Client> cli(new Client [10]);



Donc, du coup on voit que la discipline de programmation en C++ esttrès
éloignée de celle du C; Ce n'était donc pas la peine, lors de la
normalisation, de conserver une "compatibilité à tout prix" avec le C,
puisque, pour écrire "propre" en C++, on n'utilise pratiquement plus
aucun trait de C, au moins pour les tableaux et pointeurs...

On voit donc que la préservation de la compatibilité avec C a eu des
effets plutôt néfastes puisqu'il faut développer, en C++, tout un
ensemble d'outils pour pallier les déficiences du langage C qu'on a
voulu conserver coûte que coûte en C++, pour finalement dire qu'il ne
faut pas les utiliser.
Tout ceci est totalement absurde et rend le langage C++, de loin le
langage le plus abscon de tous les langages de programmation (c'est
quand même le seul langage qui a réussi à inventé des mots clés avec des
underscores !)

Nous sommes d'accord que toute dépendance est source de problème. C'est
bien pour ça que C++0x donne tout un ensemble d'outils pour rendre la
vie plus facile (auto,decltype ...)

Normalement, delete [] xxx est accepté, même si xxx ne pointe pas sur
un tableau.



Accepté par le compilateur peut être. Ce genre de chose est impossible à
vérifier à la compilation.


Ben si, si on prend la peine d'ajouter des informations sur les pointeurs...


[snip]
Ben voilà. Ca serait déjà beaucoup mieux.



Sauf qu'au départ, il y avait le C. Qui lui évolue de B.



Pour moi, ce n'était pas une raison suffisante. Le processus de
normalisation n'est souvent qu'une série de compromis entre "marchands
de tapis".

On en a discuté dans le comité de normalisation. Tout le monde
était d'accord que les tableaux de type C, c'est un désastre.



C'est le moins qu'on puisse dire !

Seulement, comment l'améliorer sans briser la comptabilité C. À
la fin, la décision était de les laisser tels quels, en
fournissant des altérnatifs nouveaux supérieur (genre
std::vector).


D'accord, cette décision n'a pas brisé la compatibilité, mais pour
être "propre", il faudrait donc transformer toutes les utilisations
des tableaux C en vector lors du passage de C à C++ ou ne jamais
utiliser les tableaux C en C++. Dans ce cas, ça ne servait à rien de
conserver la compatibilité...



Ce qui pose la question des VLAs pour C++0x. Je comprends que ça puisse
rendre la vie de compilateurs plus difficile mais c'est AMA là où il y a
le plus de travail quand on porte du code C99 vers C++ (ou quand le code
à été écrit sur un compilateur souple).

[snip]
Je relève quand même dans la norme que :
4.2 Array-to-pointer conversion [conv.array]
1 An lvalue or rvalue of type “array of N T” or “array of
unknown bound of T” can be converted to an rvalue of type
“pointer to T”. The result is a pointer to the first element
of the array. Le chapitre 4 traite des conversions standards
et commence par "Standard conversions are implicit conversions
defined for built-in types". Qu'on le veuille ou non, la
conversion d'un nom de tableau en pointeur sur son premier
élément est bien im-pli-cite et, donc, standard.



Certes. Qui a dit le contraire ?


La formulation de Jean-Marc Bourguet peut le laisser croire, quelque
part, dans ce fil, je cite : "faire croire que le nom d'un
tableau a un type pointeur ne fait qu'augmenter la confusion -- en
particulier en C++."



Oui. Un tableau a un type 'tableau' et un pointeur a un type 'pointeur'.
Qu'il y ait une conversion implicite et qu'on puisse utiliser
l'opérateur [] avec un pointeur ne crée pas une identité des types.



Non pas identité, ce qui n'a jamais été dit, mais équivalence (de par la
conversion implicite justement). Du point de vue de la sémantique
identité et équivalence ne sont pas des termes... équivalents !

[snip]
Il y a des décisions apparemment anodines qui coûtent très
cher par la suite quand on normalise.
Les choix de priorités des opérateurs (en particulier les
opérateurs bit à bit) en est un.



Je suis bien d'accord avec toi, mais ça date. (Voir
http://www.quut.com/c/dmr-on-or.html#main pour la question des
priorités, par exemple.) La compatibilité avec C, c'est
probablement la raison principale pour la réussite de C++. C'est
aussi, malheureusement, la source de pas mal de problèmes et
d'ambiguïtés ; je crois qu'on peut dire que le C n'a pas
vraiment été conçu de façon à servir de base de tant de
languages.



C était à l'origine une "commodité" pour éviter que K&R code Unix en
assembleur. Le langage C n'était pas du tout destiné au public (et ça
se voit !). Il aurait dû resté interne aux Bell Labs...



Le C et les autres langages /brackets/ sont aujourd'hui ceux
principalement utilisées dans l'industrie. Pas forcément les plus
adaptés et les plus visuellement plaisant mais l'establishment est là;
ça leur donne quand même une certaine valeur.



Le langage C ne doit sa popularité qu'au succès d'Unix car le couple
C-Unix est l'équivalent (du moins dans le monde système) de l'ancien
couple PL/1-Multics.

J'ai eu l'occasion d'auditer de très nombreux programmes C++.
C'est une des erreurs les plus fréquentes que j'ai rencontrée.



Qu'est-ce qui est l'une des erreurs les plus fréquentes ?
D'utiliser les tableaux de type C quand il existe de meilleurs
alternatifs ?



Non la confusion sur la priorités des opérateurs, dans des expressions
comme :
x & ox00FF + 5 où l'intention du programmeur était de faire
(x & ox00FF) + 5 mais ceci est interprété comme : x & (ox00FF + 5) car
+ est plus prioritaire que &, ce qui est une grosse erreur de
conception du langage.



Mais ça permet d'écrire:
if( a < b+1 & a > c ) ...

Et ça aurait peut être causé plus de confusion si ça n'avait pas été
interchangeable avec:
if( a < b+1 && a > c )...



Avoue que l'interchangeabilité de ces deux expressions n'est
certainement pas essentielle en développement.

De toute façon, K&R étaient très bons en système mais assez nuls en
langage (comme souvent chez les "hommes système").



Des langages comme fortran, cobol, algol ou lisp n'étaient pas non plus
adaptés à la programmation système.



Ils n'ont jamais prétendu l'être contrairement au langage C et C++.

Concernant les "hommes système", je ne sais pas, je ne suis pas
spécialiste, mais je pense qu'en ce qui concerne K&R, ils ont créé un
langage adapté à leur besoin.


Je n'ai rien dit d'autre mais l'erreur est d'avoir rendu C public...

AMA la théorie du langage se heurte parfois au mur de la réalité et
reproche juste au mur d'être un peu trop solide :)



La théorie des langages n'a rien à voir avec l'utilisation que l'on fait
des langages.


Ici, on a parlé surtout des confusions occasionnées par les tableaux,
les pointeurs et le mauvais choix de la priorité des opérateurs en
langage C...
Avatar
Michael DOUBEZ
Wykaaa a écrit :
Michael DOUBEZ a écrit :
Wykaaa a écrit :
James Kanze a écrit :
On Nov 3, 1:45 pm, Wykaaa wrote:




[snip]
int* i = new int;
int* p = new int[10];
delete i;
delete [] p;
Pourquoi les [] seraient-ils nécessaires?
C'est cela que je voulais dire.



Parce que la norme l'exige. Il n'y a pas d'autre raison. La
norme distingue deux types d'opérateur new qui renvoient un T*,
selon le type alloué. Et donc, deux types d'opérateur delete.



Ce que je ne comprends pas c'est pourquoi la norme l'exige. Quelle
est la raison à l'origine de cette décision ?



Je suppose que ça viens du même problème que la conversion implicite:
si j'écris une fonction pour effacer un tableau de taille inconnue, je
dois passer le tableau en paramètre et donc il passe par pointeur. Il
faut donc bien un moyen de signifier à delete qu'il s'agit d'un tableau:

void dispose_of(int* table)
{
delete[] table;
}


Ce n'est pas un bon exemple car ici c'est un tableau de int qui ne sont
pas des instances de classe.



Mais le problème est le même.

Les [] dans delete sont là pour que le
destructeur des instances de la classe soit appelé sur chacun des
éléments du tableau.



En outre, rien n'empêche new[] d'ajouter des information dans le header
de la mémoire allouée qui ne sont pas ajoutées dans le cas de new. Donc
delete[] doit être appelé pour utiliser correctement ces informations
(typiquement le nombre d'éléments alloués).

C'est bête, mais dans la pratique, je ne me suis jamais servi
d'un new [] ni d'un delete []. Qu'un débuttant ne sache même pas
qu'ils existent ne me gènerait pas outremesure.



C'est bête car je préconise que tout delete soit accompagné des
crochets pour faciliter la maintenance.
En effet :
classe Client {...};
Client* cl = new Client;
plus loin : delete cl;



Auquel je préfère:
scoped_ptr<Client> cli(new Client);

Un jour on change la déclaration de cl : Client* cl = new Client [10];
delete cl ne détruit pas les instances de Client créées dans le
tableau alloué dynamiquement.



Auquel je substitue:
scoped_array<Client> cli(new Client [10]);



Donc, du coup on voit que la discipline de programmation en C++ esttrès
éloignée de celle du C; Ce n'était donc pas la peine, lors de la
normalisation, de conserver une "compatibilité à tout prix" avec le C,
puisque, pour écrire "propre" en C++, on n'utilise pratiquement plus
aucun trait de C, au moins pour les tableaux et pointeurs...



Mais ça permet d'utiliser les headers C dans un programme C++. Et les
interfaces en C sont la grandes force du langage C. Toutes les
applications en fournissent; ce qui n'est pas le cas de C++. Et même
quand c'est en C++, c'est souvent un wrapper autour des fonctions C.


On voit donc que la préservation de la compatibilité avec C a eu des
effets plutôt néfastes puisqu'il faut développer, en C++, tout un
ensemble d'outils pour pallier les déficiences du langage C qu'on a
voulu conserver coûte que coûte en C++, pour finalement dire qu'il ne
faut pas les utiliser.



C'est pas qu'il ne faut pas les utiliser. Elles sont juste source
d'erreur et donc il faut être plus prudent.
Le C++ a voulu être un meilleur C au départ; maintenant c'est un langage
à part mais si on lâchait cette compatibilité, ça ferait des problèmes
sans nom.

Tout ceci est totalement absurde et rend le langage C++, de loin le
langage le plus abscon de tous les langages de programmation (c'est
quand même le seul langage qui a réussi à inventé des mots clés avec des
underscores !)



C'est un langage avec beaucoup de subtilité mais si on utilise que la
partie haute du langage, il y a peu de problèmes. Si on regarde
"Accelerated C++", a aucun momen ils n'utilisent des tableaux ou des
pointeurs avant le chapitre 10 (sur 16).

C'est souvent les codeurs C qui ont du mal a faire la transition.

Nous sommes d'accord que toute dépendance est source de problème.
C'est bien pour ça que C++0x donne tout un ensemble d'outils pour
rendre la vie plus facile (auto,decltype ...)

Normalement, delete [] xxx est accepté, même si xxx ne pointe pas sur
un tableau.



Accepté par le compilateur peut être. Ce genre de chose est impossible
à vérifier à la compilation.


Ben si, si on prend la peine d'ajouter des informations sur les
pointeurs...



Les ajouter où ? Après la conversion tableau en pointeur, il n'y a en
général plus d'information à la compilation.
Au mieux on pourrait ajouter une signature à l'exécution mais ça fait
beaucoup pour quelque chose qui n'est pour ainsi dire jamais utilisé. Un
autre principe du C++ est de ne pas payer pour ce qui n'est pas utilisé.

Seul le codeur sais si un pointeur pointe sur un objet ou si c'est un
tableau converti; c'est donc au codeur de prendre la décision. Le C++
comme le C fait confiance au programmeur.

[snip]
Je relève quand même dans la norme que :
4.2 Array-to-pointer conversion [conv.array]
1 An lvalue or rvalue of type “array of N T” or “array of
unknown bound of T” can be converted to an rvalue of type
“pointer to T”. The result is a pointer to the first element
of the array. Le chapitre 4 traite des conversions standards
et commence par "Standard conversions are implicit conversions
defined for built-in types". Qu'on le veuille ou non, la
conversion d'un nom de tableau en pointeur sur son premier
élément est bien im-pli-cite et, donc, standard.



Certes. Qui a dit le contraire ?


La formulation de Jean-Marc Bourguet peut le laisser croire, quelque
part, dans ce fil, je cite : "faire croire que le nom d'un
tableau a un type pointeur ne fait qu'augmenter la confusion -- en
particulier en C++."



Oui. Un tableau a un type 'tableau' et un pointeur a un type
'pointeur'. Qu'il y ait une conversion implicite et qu'on puisse
utiliser l'opérateur [] avec un pointeur ne crée pas une identité des
types.



Non pas identité, ce qui n'a jamais été dit, mais équivalence (de par la
conversion implicite justement). Du point de vue de la sémantique
identité et équivalence ne sont pas des termes... équivalents !



D'accord, mais l'équivalence est dans les deux sens or un template
attendant un tableau ne peut pas matcher un pointeur (heureusement) ou
encore sizeof ne renvoie pas la même valeur.

template<int N>
int count(const T& arr[N])
{
return N;
}

int array[10];
int* ptr=array;

count(array);//ok
count(ptr);//error

Ce que tu dois vouloir dire c'est qu'ils partagent le même opérateur de
déférencement[]. Mais c'est tout, et même d'après la norme, il s'agit
d'une conversion implicite du tableau en pointeur (voir 8.3.4/6 de n2798:
Except where it has been declared for a class, the subscript operator []
is interpreted in such a way that E1[E2] is identical to *((E1)+(E2)).
Because of the conversion rules that apply to +, if E1 is an array and
E2 an integer, then E1[E2] refers to the E2-th member of E1.[...]

Je pense qu'au final on dit la même chose mais avec des mots différents
concernant la conversion tableau->pointeur mais un pointeur et un
tableau sont complètement différents: l'un est un agrégat homogène et
l'autre une valeur de référence.

[snip]
Il y a des décisions apparemment anodines qui coûtent très
cher par la suite quand on normalise.
Les choix de priorités des opérateurs (en particulier les
opérateurs bit à bit) en est un.



Je suis bien d'accord avec toi, mais ça date. (Voir
http://www.quut.com/c/dmr-on-or.html#main pour la question des
priorités, par exemple.) La compatibilité avec C, c'est
probablement la raison principale pour la réussite de C++. C'est
aussi, malheureusement, la source de pas mal de problèmes et
d'ambiguïtés ; je crois qu'on peut dire que le C n'a pas
vraiment été conçu de façon à servir de base de tant de
languages.



C était à l'origine une "commodité" pour éviter que K&R code Unix en
assembleur. Le langage C n'était pas du tout destiné au public (et ça
se voit !). Il aurait dû resté interne aux Bell Labs...



Le C et les autres langages /brackets/ sont aujourd'hui ceux
principalement utilisées dans l'industrie. Pas forcément les plus
adaptés et les plus visuellement plaisant mais l'establishment est là;
ça leur donne quand même une certaine valeur.



Le langage C ne doit sa popularité qu'au succès d'Unix car le couple
C-Unix est l'équivalent (du moins dans le monde système) de l'ancien
couple PL/1-Multics.



Pourtant Java et Objective-C l'ont trouvé a leur gout sans aucune
volonté d'être UNIX friendly.

AMHA, le langage C doit sa popularité à sa simplicité apparente (et au
fait que toutes les interfaces sont en C). Le problème est qu'il est
simple à apprendre et moins simple à utiliser correctement voir lourd
quand il s'agit de fonctionnalités haut-niveau.

J'ai eu l'occasion d'auditer de très nombreux programmes C++.
C'est une des erreurs les plus fréquentes que j'ai rencontrée.



Qu'est-ce qui est l'une des erreurs les plus fréquentes ?
D'utiliser les tableaux de type C quand il existe de meilleurs
alternatifs ?



Non la confusion sur la priorités des opérateurs, dans des
expressions comme :
x & ox00FF + 5 où l'intention du programmeur était de faire
(x & ox00FF) + 5 mais ceci est interprété comme : x & (ox00FF + 5)
car + est plus prioritaire que &, ce qui est une grosse erreur de
conception du langage.



Mais ça permet d'écrire:
if( a < b+1 & a > c ) ...

Et ça aurait peut être causé plus de confusion si ça n'avait pas été
interchangeable avec:
if( a < b+1 && a > c )...



Avoue que l'interchangeabilité de ces deux expressions n'est
certainement pas essentielle en développement.



Certe mais quand on pense a tous les problème de = utilisés au lieu de
== dans les conditions; je trouve que c'est pas mal. Et puis, le
problème réel est dans les expression du type x & 0x01 == 0.

Mais en général, j'utilise & pour faire un masque alors je trouve
naturel de parenthèser.

[snip]OK
Ici, on a parlé surtout des confusions occasionnées par les tableaux,
les pointeurs et le mauvais choix de la priorité des opérateurs en
langage C...



La conversion d'un tableau en pointeur semblera naturelle à une personne
habituée à faire de l'assembleur. Quand aux opérateurs, l'idéal aurait
peut être été d'utiliser des keywords pour les opération booléennes.

Et alors ?

--
Michael
Avatar
Jean-Marc Bourguet
Wykaaa writes:

Décidément, je n'aime pas du tout le terme "pointer type" qui sème la
confusion.



Je ne vois rien qui cause de la confusion. Les pointeurs sont une famille
de types -- on pourrait facilement imaginer un langage qui utilise la meme
syntaxe pour construire les types pointeurs que pour instancier des types
templates -- un type pointeur est un de ces types. Tout comme on a des
types entiers (int, long), des types tableaux, des types classes etc...

Ce que je ne comprends pas c'est pourquoi la norme l'exige. Quelle est la
raison à l'origine de cette décision ?



La non difference entre un pointeur vers un objet isole et un pointeur vers
un objet d'un tableau (ou meme le premier objet d'un tableau).

> C'est bête, mais dans la pratique, je ne me suis jamais servi
> d'un new [] ni d'un delete []. Qu'un débuttant ne sache même pas
> qu'ils existent ne me gènerait pas outremesure.

C'est bête car je préconise que tout delete soit accompagné des crochets
pour faciliter la maintenance.



Pour facilite la maintenance d'un cas extremement rare, tu compliques le
cas usuel? Les cas ou j'ai du faire cette transformation sont rarissimes,
s'ils existent. Ma premiere reaction c'etait que j'utiliserais un
std::vector<>, mais ma deuxieme est "pourquoi alors est-ce que cl est un
pointeur?".

En effet :
classe Client {...};
Client* cl = new Client;
plus loin : delete cl;
Un jour on change la déclaration de cl : Client* cl = new Client [10];
delete cl ne détruit pas les instances de Client créées dans le tableau
alloué dynamiquement.
Normalement, delete [] xxx est accepté, même si xxx ne pointe pas sur un
tableau.



delete[] n'est accepte que si il y a eu un new[].

Donc

Client* cl = new Client[1];

delete[] cl;

ou

Client* cl = new Client;

delete cl;

Mais pas

Client* cl = new Client;

delete[] cl;

> Sauf qu'au départ, il y avait le C. Qui lui évolue de B.

Pour moi, ce n'était pas une raison suffisante. Le processus de
normalisation n'est souvent qu'une série de compromis entre "marchands de
tapis".



Comme tout ce qui cherche une evolution plutot qu'une revolution. Et
l'experience montre qu'on survit plus longtemps avec une evolution
imparfaite qu'une revolution qui detruit tout l'existant.

> Seulement, comment l'améliorer sans briser la comptabilité C. À
> la fin, la décision était de les laisser tels quels, en
> fournissant des altérnatifs nouveaux supérieur (genre
> std::vector).
D'accord, cette décision n'a pas brisé la compatibilité, mais pour être
"propre", il faudrait donc transformer toutes les utilisations des tableaux
C en vector lors du passage de C à C++ ou ne jamais utiliser les tableaux C
en C++. Dans ce cas, ça ne servait à rien de conserver la compatibilité...



Le C++ est comme le C, un langage qui a evolue sur une periode *longue*. A
chaque fois au moment de la normalisation c'etait un langage d'une
quinzaine d'annees deja utilise pour des choses non triviales.

>> Je relève quand même dans la norme que :
>> 4.2 Array-to-pointer conversion [conv.array]
>> 1 An lvalue or rvalue of type “array of N T” or “array of
>> unknown bound of T” can be converted to an rvalue of type
>> “pointer to T”. The result is a pointer to the first element
>> of the array. Le chapitre 4 traite des conversions standards
>> et commence par "Standard conversions are implicit conversions
>> defined for built-in types". Qu'on le veuille ou non, la
>> conversion d'un nom de tableau en pointeur sur son premier
>> élément est bien im-pli-cite et, donc, standard.
> Certes. Qui a dit le contraire ?
La formulation de Jean-Marc Bourguet peut le laisser croire, quelque part,
dans ce fil, je cite : "faire croire que le nom d'un
tableau a un type pointeur ne fait qu'augmenter la confusion -- en
particulier en C++."



J'ai dit et repete, "le nom d'un tableau designe un objet de type tableau.
Tous les objets de types tableau (qu'il soit nomme ou pas) sont convertis
implicitement dans la plupart des contextes en un pointeur vers leur
premier element".

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org
Avatar
Jean-Marc Bourguet
Michael DOUBEZ writes:

Ce qui pose la question des VLAs pour C++0x. Je comprends que ça puisse
rendre la vie de compilateurs plus difficile mais c'est AMA là où il y a le
plus de travail quand on porte du code C99 vers C++ (ou quand le code à été
écrit sur un compilateur souple).



Je ne crois pas que ce soit un probleme de travail pour les compilateurs --
a priori un ordre de grandeur ou deux en moins que pour les concepts --
mais c'est surtout un probleme de difficulte de definition par rapport aux
benefices attendus et donc de l'absence de quelqu'un pour prendre la peine
d'examiner les consequences et de faire une proposition.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org
6 7 8 9 10