OVH Cloud OVH Cloud

[debutant] NULL

36 réponses
Avatar
pasde.bcausse.spam
NULL n'etant pas defini en C++ ? (bizare)

dans un header Global.h j'ai

const int NULL = 0;

la ou j'utilise NULL j'ai un warning :

invalid conversion from `int' to `RXMove*' qui me parait normal

comment faire?

PS: je viens de JAVA et cela me choque :(


--
Bruno Causse
http://perso.wanadoo.fr/othello

10 réponses

1 2 3 4
Avatar
James Kanze
Anthony Fleury wrote:

Anthony Fleury wrote:
Le C++ définit ce qu'est un "null pointer constant", et il
le définit comme étant une valeur entière constante
comparée égale à 0. Donc une bonne définition de NULL en
C++ est celle donnée en dessous. (4.10 dans la norme)




Sauf que l'utilisateur n'a pas le droit de le faire, dès
qu'il a inclu un parmi un certain nombre d'en-têtes.



Ca ne me paraissait pas trivial dans la norme. En fait, la
définition apparait dans des en-tête qui sont à un moment
appelés "C++ Headers for C Library Facilities" [ les cstddef &
co ] J'ai toujours considéré ces headers comme étant fait
simplement pour la compatibilité avec le C. Mais apparement
c'est bien plus. Quel est leur statut ??


Les en-têtes du genre <cstddef> font partie de la norme, à même
titre de <ostream> ou <vector>. Si pour certains, comme
<cstdio>, on préfèrerait les fonctionalités pûrement C++,
définies dans <istream>, <ostream>, etc., pour d'autres, comme
<ctime>, il n'existe pas d'alternatif. En tout cas, ils font
tous partie integrante de C++.

D'ailleurs, j'ai commencé le C++ en lisant TC++PL, et il y est
explicitement dit :


"En langage C, il arrivait fréquemment qu'une macro NULL soit définie
pour représenter le pointeur 0. En C++, un contrôle de type plus sérieux
rend moins problématique l'utilisation du 0 normal, plutôt que de toute
macro NULL. Le nombre de problèmes est ainsi considérablement réduit. Si
vous pensez devoir définir NULL, utilisez
const int NULL = 0;
Le qualificatif const (§5.4) évite toute redéfinition accidentelle de
NULL et permet l'utilisation dans tous les cas où une constante est
requise. "


On en a déjà parlé ici, il me semble. En tout cas, le conseil
d'utiliser « const int NULL = 0 » ne peut être qu'une erreur,
étant donné que c'est illégal.

Historiquement, la situation est confuse. Ce que je crois, c'est
qu'indépendamment des problèmes techniques (et peut-être même en
C), Stroustrup préfère 0. Je ne sais pas pourquoi. En revanche,
je sais aussi qu'à l'époque où j'ai commencé le C++, beaucoup de
monde insistait sur 0 parce qu'alors, la plupart des
compilateurs C++ utilisaient des en-têtes C directement, et que
dans certains compilateurs C, NULL était défini ((void*)0), et
que cette définition ne fonctionnait pas comme constante de
pointeur null avec le compilateur C++. Je crois qu'on peut dire
que cette raison est à peu près périmée. Mais il a influencé les
habitudes de pas mal de programmeurs.

Aujourd'hui, je crois qu'on peut distingué en fait trois camps
(ce qui n'est pas mal pour une question simple de oui ou de
non), ce qui préfère 0 tout court, y compris en C (parce qu'en
fin de compte, c'est ce qu'on a réelement, au moins avec
certains compilateurs), ce qui préfère NULL en C, mais qui
préfère 0 en C++ (à cause des questions de la résolution du
surcharge et des templates), et ceux comme moi qui utilisent
NULL partout (pour des raisons de lisibilité).

En fait, je crois que la différence entre les deux derniers
camps dépend au moins du contexte où on travaille. Si on pousse
le langage à ces limites, avec beaucoup de templates compliqués
et des surcharges, on préfère 0, tandis que si on travaille sur
des projets assez grands, avec des collégues qui ne sont pas
toujours des spécialistes des détails du C++, les critères de
lisibilité prime, d'autant plus que de toute façon, pousser le
langage à ces limites est interdit.

N'est ce dont pas une grosse erreur dans TC++PL ?


Il y a deux partie de l'énoncé. Le conseil de définir soi-même
NULL est une grosse erreur, certainement. Le conseil de préférer
0, en revanche, relève de l'opinion personnelle ; on peut
regretter que Stroustrup n'a pas présenté d'autres avis aussi,
mais on ne peut pas lui nier le droit à son opinion.

[ En fait, d'après la norme et le paragraphe en question,
on peut mettre const int NULL = 0; ou const int NULL = 0L;
si j'ai tout bien compris ].




Selon la norme, NULL doit être 1) un macro, 2) défini dans
des en-têtes standard, et 3) évalué à une constante de
pointeur nul (qui malgré son nom, doit avoir un type entier,
et non un type pointeur).



Comme dit plus haut, comme cette définition n'apparaissait que
dans les en-tête <cX> avec X un nom d'en-tête C, je pensais
pas que le fait que ce soit une macro était obligatoire en
C++. Pour moi la norme du C++ ne définissait pas NULL.


Et qu'en fais-tu de §18.1 ?

Note que NULL n'est pas seul dans ce cas -- il y a aussi
ptrdiff_t, offsetof et size_t (dans la même section). Or, si on
pourrait dire qu'un programme C++ doit peut-être éviter
offsetof, et que les avis sur NULL varient, ptrdiff_t et size_t
servent à peu près partout dans la norme -- dans toute la STL,
par exemple. Alors, dans la pratique, si tu inclus un en-tête de
la STL, tu as de fortes chances d'avoir aussi la définition
officielle de NULL.

est interdite du fait que la conversion de void* -> T*
doit être explicitement demandée, tout utilisateur de
malloc() & co en C++ l'aura remarqué.




Je ne comprends pas trop ta phrase, ni son rapport avec NULL.
Sauf en ce qui concerne les formes permises, la définition de
NULL et des constantes de pointeur nul est identique en C et
en C++.



Ce que je voulais dire, c'est que certains compilateurs C
définissent NULL comme (void*)0. (ca fait même l'objet d'une
note dans la norme C++). Et cette forme est interdite en C++,
qui a des règles de conversions plus fortes sur ce point que
le C.


Les règles de conversions sont effectivement différentes dans
les deux langages. Mais pas en ce qui concerne les conversions
d'une « null pointer constant », et donc pas en ce qui concerne
NULL. (Le C donne une liberté de plus en ce qui est une
constante de pointeur nul, mais ça ne joue en rien ici -- il y a
dans les deux langages une conversion très spéciale en ce qui
concerne des constantes de pointeur nul.)

En effet, le code :


#include <stdlib.h>


int main(void) {
int *a = malloc(sizeof *a);
free(a);
return 0;
}


est valable en C mais pas en C++. Le C++ requiert un cast
explicite pour caster void* -> T* que ne requiert pas le C.


Certes, mais quel rapport avec des constantes de pointeur nul.
Que ce soit le C ou le C++ :

int* a = 0 ;
ou
int* a = NULL ;

est légal (garanti par la norme), tandis que :

int i = 0 ;
int* a = i ;

ne l'est pas. Le fait d'avoir une expression constante (de type
entier) introduit une conversion supplémentaire. Il y a de la
magie du compilateur.

Je crois qu'il y a un consensus pour dire que cette situation
est lamentable, ou au moins, moins d'idéale. Pas, en revanche,
sur ce qu'il faut faire en consequence. (Je crois qu'il y a une
proposition devant le gremium C++ pour une véritable solution --
Gaby pourrait peut-être en donner plus de détails. Je serais
pour, ne serait-ce que pour arrêter toutes ces discussions
interminables:-).)

Deux choses :
- d'une part en C++ je pense qu'il est préférable de
mettre 0 partout. (c'est une question de gout, un long
débat sur les groupes anglophones, et ca fait aussi une
ligne dans TC++PL)




Ça dépend de ton style de programmation et du compilateur
dont tu te sers. Fais-toi la règle que si le code ne se
compile pas avec g++ sans avertissements, il est incorrect,
et NULL n'a que des avantages par rapport à zéro.



Pour ma part j'ai pris l'habitude en C++ d'utiliser 0, mais
c'est vrai que NULL peut être plus lisible. Ca dépend en effet
du style de programmation. [ pour ma part comme dit plus haut
ca a surtout été conditionné par la lecture de TC++PL, et ca
fait une différence par rapport au C où j'utilise tout le
temps NULL. ]


Tout à fait. Il y a deux aspects importants : le style de
programmation, et les habitudes. Quand j'ai commencé le C++,
j'avais déjà 10 ans d'expérience en C, et puisque Kernighan et
Richie se servaient de NULL... En plus, à l'époque, il n'y avait
pas de templates, et on se servait assez peu des surcharges
ambigus. Et je travaillais dans une équipe où tout le monde
n'avait pas le même niveau, et où la lisibilité était
primordiale. Du coup, les règles de programmation exigeait
l'utilisation de NULL.

- Cette version devrait compiler sans problème et sans
warning.




Ça dépend de ce qu'il a inclu avant. Si après l'expansion du
macro, le compilateur voit :



const int 0 = 0 ;



il risque de ne pas être content. Je ne crois pas que ça
explique le comportement qu'il a vu, mais il reste que sa
déclaration donne un comportement indéfini dès qu'il inclut
certains en-têtes.



En effet ca n'explique pas son problème car cette ligne
provoquerait une erreur de syntaxe, car en gros il trouve un
entier là où un identifiant est attendu. C'est donc
incorrecte. Mais en effet après inclusion de certains en-tête,
cette définition de NULL poserait des problèmes. Je ne savais
par contre pas que ca provoquait un comportement indéfinit de
redéfinir NULL.


Un comportement indéfini, c'est souvent ce que donne la norme
quand elle ne veut pas spécifier. C'est, je crois, le cas ici --
on veut que la rédéfinition de NULL soit une erreur de la part
du programmeur, à cause des cas comme celui-ci, mais on ne veut
pas non plus exiger un diagnostique, dans le cas où tu n'inclus
pas un des en-têtes fatadique. C'est donc formellement un
comportement indéfini, mais dans la pratique, ou bien, tu as une
erreur de syntaxe lors de la compilation, ou bien, ça marche.

Personnellement, et ça n'engage que moi, je trouve qu'on
pourrait bien introduire une catégorie supplémentaire des
erreurs, entre un comportement indéfini et un diagnostique
obligatoire, qui délimite les dégats possibles à la phase de
compilation -- si le compilateur ne râle pas, ça doit marcher.
Parce que ce genre de cas n'est pas rare dans la norme. (Mais ce
n'est pas si évident que ça -- si ensuite d'une inclusion
« cachée » d'un en-tête, tu vois plus ou moins d'un ensemble de
fonctions surchargées, la résolution du surcharge peut bien être
différente, sans pour autant provoquer une erreur de
compilation.)

Sans voir le contexte complet de ton problème, c'est
difficile à dire. La solution préférée, c'est d'inclure
<stddef.h> en tête, et de se servir du NULL défini là (ou de
s'en passer complètement, et écrire 0 -- selon le
compilateur, ton style de programmation, et les conventions
en vigueur où tu travailles).



Et en entreprise ca doit aussi dépendre du style de la boîte
non ? dans la coordination d'un projet ca doit faire partie
des styles de programmation qui sont définis je suppose ?


Tout à fait. Encore que je dois dire que chaque fois que j'ai vu
oùla question s'est posée, la décision s'est faite pour NULL,
plutôt que 0.

--
James Kanze mailto:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 pl. Pierre Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34



Avatar
James Kanze
Richard Delorme wrote:
"En langage C, il arrivait fréquemment qu'une macro NULL soit
définie pour représenter le pointeur 0. En C++, un contrôle de
type plus sérieux rend moins problématique l'utilisation du 0
normal, plutôt que de toute macro NULL. Le nombre de problèmes
est ainsi considérablement réduit. Si vous pensez devoir
définir NULL, utilisez
const int NULL = 0;
Le qualificatif const (§5.4) évite toute redéfinition
accidentelle de NULL et permet l'utilisation dans tous les cas
où une constante est requise. "



N'est ce dont pas une grosse erreur dans TC++PL ?



Non, tout est dans le "Si vous pensez devoir définir", qui
suppose que l'on veut se passer des entêtes et définir NULL
soi-même.


Pas vraiment. Dans la mésure qu'une telle définition est
interdite, il faut le dire. C'est un peu comme si on disait :
« Si vous pensez devoir modifier la même variable deux fois dans
la même expression... ».

--
James Kanze mailto:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 pl. Pierre Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34


Avatar
James Kanze
Hamiral wrote:

PS: je viens de JAVA et cela me choque :(



Sauf qu'en Java, le mot-clé null n'a rien à voir avec les
pointeurs, et pour cause : il n'y a pas de pointeur en Java,
mais des /références/ sur des objets. Et le mot-clé null sert
en Java à dire qu'une variable ne fait référence à aucun
objet.


Et quelle est la différence entre une référence en Java et un
pointeur en C++. Java a des pointeurs, aussi bien que le C++.
Les différences sont :

-- Les pointeurs de Java ne sont pas des objets en soi -- en ne
peut pas en prendre l'adresse, par exemple. C'est d'un
certain côté un défaut de Java.

-- Le Java ne supporte pas l'arithmétique sur des pointeurs.
Pour des programmes au niveau applicatif, c'est un avantage
de Java, mais du coup... est-ce qu'on pourrait écrire un
glaneur de cellules en Java ?

--
James Kanze mailto:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 pl. Pierre Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34


Avatar
Fabien LE LEZ
On Sun, 08 May 2005 17:53:59 +0200, James Kanze :

-- Les pointeurs de Java ne sont pas des objets en soi -- en ne
peut pas en prendre l'adresse, par exemple.

-- Le Java ne supporte pas l'arithmétique sur des pointeurs.


C'est ce qu'on appelle "référence" en C++, non ?

--
Le grand site de la philosophie animale : <http://perso.edulang.com/philo/>

Avatar
Alexandre
C'est ce qu'on appelle "référence" en C++, non ?


pas vraiment puisqu'en java il faut appeler new pour créer une variable...
Donc c'est plus un pointeur qu'une référence, au sens C++, puisque la
reférence C++ ne peut pas ne pas être "liée" dès sa création...


--
Le grand site de la philosophie animale :
<http://perso.edulang.com/philo/>


Avatar
Fabien LE LEZ
On Sun, 8 May 2005 19:07:14 +0200, "Alexandre"
:

C'est ce qu'on appelle "référence" en C++, non ?


pas vraiment puisqu'en java il faut appeler new pour créer une variable...


Bon, on va dire que la notion de pointeur en Java est à mi-chemin
entre les pointeurs et les références en C++ ;-)

--
Le grand site de la philosophie animale : <http://perso.edulang.com/philo/>


Avatar
Matthieu Moy
Fabien LE LEZ writes:

Bon, on va dire que la notion de pointeur en Java est à mi-chemin
entre les pointeurs et les références en C++ ;-)


Y'a des grands débats pour savoir si il y a des « pointeurs » en Java.
Vu qu'il y a un java.lang.NullPointerException, on serait tenté de
dire oui, mais on dit aussi qu'on ne manipule que des références.

Si j'ai bien compris, la version officielle est que la variable est
une référence, et sa valeur est un pointeur.

Blanc bonnet, toussa, ... ;-)

--
Matthieu

Avatar
Lambda
est-ce qu'on pourrait écrire un glaneur de cellules en Java ?



Allez, une petite réponse passe-partout pour le plaisir...

"Java est Turing-complete"

--

Lambda

Avatar
Matthieu Moy
Lambda writes:

"Java est Turing-complete"


Hmmm, un "Java est équivalent au lambda-calcul" aurait été plus adapté
dans ton cas ;-) !

--
Matthieu

Avatar
Anthony Fleury
Tout d'abord merci pour ce post extrêmement intéressant.

Anthony Fleury wrote:

Anthony Fleury wrote:


Ca ne me paraissait pas trivial dans la norme. En fait, la
définition apparait dans des en-tête qui sont à un moment
appelés "C++ Headers for C Library Facilities" [ les cstddef &
co ] J'ai toujours considéré ces headers comme étant fait
simplement pour la compatibilité avec le C. Mais apparement
c'est bien plus. Quel est leur statut ??


Les en-têtes du genre <cstddef> font partie de la norme, à même
titre de <ostream> ou <vector>. Si pour certains, comme
<cstdio>, on préfèrerait les fonctionalités pûrement C++,
définies dans <istream>, <ostream>, etc., pour d'autres, comme
<ctime>, il n'existe pas d'alternatif. En tout cas, ils font
tous partie integrante de C++.


Pour <ctime> et autre, en effet. Mais je sais pas pourquoi je me suis
toujours mis en tête que stdio.h, stddef.h, et certains autres n'avaient
pour rôle en C++ que la compatibilité avec son ptit frère le C. Erreur
de ma part qui est désormais corrigée.

On en a déjà parlé ici, il me semble. En tout cas, le conseil
d'utiliser « const int NULL = 0 » ne peut être qu'une erreur,
étant donné que c'est illégal.


Bon, faut que je l'oublie alors :-) Ca va pas être très dur de ne pas
l'utiliser vu que j'utilisais déjà toujours 0 en C++ (et NULL en C comme
l'une de vos catégories), mais faut maintenant me mettre dans la tête
que c'est illégal.

N'est ce dont pas une grosse erreur dans TC++PL ?


Il y a deux partie de l'énoncé. Le conseil de définir soi-même
NULL est une grosse erreur, certainement. Le conseil de préférer
0, en revanche, relève de l'opinion personnelle ; on peut
regretter que Stroustrup n'a pas présenté d'autres avis aussi,
mais on ne peut pas lui nier le droit à son opinion.


N'empêche que ce passage est très peu clair pour quelqu'un qui apprend
le C++ avec ce livre, et en tant que premier lien avec le langage, ca
laisse des traces !

Comme dit plus haut, comme cette définition n'apparaissait que
dans les en-tête <cX> avec X un nom d'en-tête C, je pensais
pas que le fait que ce soit une macro était obligatoire en
C++. Pour moi la norme du C++ ne définissait pas NULL.


Et qu'en fais-tu de §18.1 ?


§18.1 parle de NULL définie dans <cstddef>, donc tout découle de la même
erreur qu'au dessus. Le point 4 nous dit que NULL est une macro dont la
définition respecte le §4.10, qui dit ce que j'ai énoncé plus haut. Pour
moi (et toujours dans la même idée), cette macro était définie comme ca
pour que l'inclusion de <cstddef> soit possible en C++, donc avec une
macro NULL compatible.

Alors, dans la pratique, si tu inclus un en-tête de
la STL, tu as de fortes chances d'avoir aussi la définition
officielle de NULL.


Ah ca par contre je ne savais pas, merci. Vu que je n'ai jamais définit
ce const int NULL, je n'ai jamais eu ce problème. Mais c'est bon à
savoir que j'ai souvent une macro NULL "sans le savoir".

Ce que je voulais dire, c'est que certains compilateurs C
définissent NULL comme (void*)0. (ca fait même l'objet d'une
note dans la norme C++). Et cette forme est interdite en C++,
qui a des règles de conversions plus fortes sur ce point que
le C.


Les règles de conversions sont effectivement différentes dans
les deux langages. Mais pas en ce qui concerne les conversions
d'une « null pointer constant », et donc pas en ce qui concerne
NULL. (Le C donne une liberté de plus en ce qui est une
constante de pointeur nul, mais ça ne joue en rien ici -- il y a
dans les deux langages une conversion très spéciale en ce qui
concerne des constantes de pointeur nul.)


Nous sommes bien d'accord, je parlais de la conversion dans le cas
général et non d'un "null pointer constant". Mais c'était mal exprimé
dans mon post initial.

Je crois qu'il y a un consensus pour dire que cette situation
est lamentable, ou au moins, moins d'idéale. Pas, en revanche,
sur ce qu'il faut faire en consequence. (Je crois qu'il y a une
proposition devant le gremium C++ pour une véritable solution --
Gaby pourrait peut-être en donner plus de détails. Je serais
pour, ne serait-ce que pour arrêter toutes ces discussions
interminables:-).)


http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1601.pdf
Celle ci ? J'avais lu aussi suite à un lien donné par Gabriel Dos Reis
ici même. Ca a l'air interessant en effet. Pour ma part je ne vois pas
toute l'implication de la proposition, vu qu'hormis dans cette
discussion, je n'ai jamais eu de problème avec un pointeur null en C++.

Personnellement, et ça n'engage que moi, je trouve qu'on
pourrait bien introduire une catégorie supplémentaire des
erreurs, entre un comportement indéfini et un diagnostique
obligatoire, qui délimite les dégats possibles à la phase de
compilation -- si le compilateur ne râle pas, ça doit marcher.
Parce que ce genre de cas n'est pas rare dans la norme. (Mais ce
n'est pas si évident que ça -- si ensuite d'une inclusion
« cachée » d'un en-tête, tu vois plus ou moins d'un ensemble de
fonctions surchargées, la résolution du surcharge peut bien être
différente, sans pour autant provoquer une erreur de
compilation.)


Et dans ce cas, bonjour l'amusement pour debugger aussi... Surtout quand
c'est la première fois qu'on tombe sur le problème.

Tout à fait. Encore que je dois dire que chaque fois que j'ai vu
oùla question s'est posée, la décision s'est faite pour NULL,
plutôt que 0.


C'est une sorte de "mot clé" notamment pour tout ceux qui ont fait du C.
(pas taper sur l'expression "mot clé", mais j'ai pas trouvé mieux) C'est
donc en effet plus clair pour tout le monde, et tous les relecteurs du
code.

--
Anthony Fleury



1 2 3 4