Me v'là avoir perdu 5 bonnes minutes à cause d'une bête erreur. Quelqu'un pourrait m'expliquer pourquoi ni gcc ni VisualExpress ne râle avec une ligne comme ça:
Oui, oui. Et après cela, p vaut bien NULL!Code:
1
2 void *p; p = false;
Version imprimable
Me v'là avoir perdu 5 bonnes minutes à cause d'une bête erreur. Quelqu'un pourrait m'expliquer pourquoi ni gcc ni VisualExpress ne râle avec une ligne comme ça:
Oui, oui. Et après cela, p vaut bien NULL!Code:
1
2 void *p; p = false;
Visual Studio 2005 ne râle pas non plus (avec warning level 4)
Peut être parce que false vaut 0 et que c'est autorisé de mettre 0 dans un pointeur.
Par contre, le code suivant provoque un warning "impossible de convertir de '' en 'void *'"
Code:
1
2
3
4
5
6
7
8 enum { val_0, val_1 }; int main(void) { void *p; p = val_0; return 0; }
Salut,
Simplement, parce que, la seule chose dont on soit sur, c'est que false vaut 0, alors que true vaut... tout le reste (même si la norme refuse que l'on puisse envisager d'incrémenter true :P).
A coté de cela, il se fait que NULL correspond à une adresse invalide, et, elle aussi, vaut 0 (0x00000000)...
En effet, la plupart des implémentation de NULL sont de l'ordre de
La preuve en est que tu peux tester ton pointeur directement sous la forme deCode:#define NULL ((void*) 0)
if(ptr) pour t'assurer qu'il a été initialisé ou de if(!ptr) pour t'assurer qu'il n'existe pas
Dés lors, que tu utilise false, 0 ou NULL pour t'assurer qu'un pointeur est invalide reviendra toujours au même ;)
Il est à noter que la nouvelle norme va introduire le terme null_ptr, qui sera plus explicite quant à la représentation, mais qui risque quand meme de n'etre, à l'implémentation que quelque chose comme...
(à confirmer :P)Code:#define null_ptr NULL
Oui je suis tout à fait d'accord mais alors pourquoi mon exemple avec une enum et val_0 (qui vaut 0, j'en suis sûr, j'ai regardé au debugger) provoque t'il un warning à la compilation.
Cela veut t'il dire que le type boolean n'est pas un type mais un espece de nombre puisque'on peut faire "void *p = false" mais pas "void *p = val_0"
Le type booléen est malheureusement lui aussi un #define de l'ordre de
(ou quelque chose dans le genre)Code:
1
2 #define false 0 #define true !false
Alors que l'énumération correspond à un TAD, au même titre qu'une structure, une union ou une classe.
Il peut donc y avoir une vérification de type sur une énumération qu'il ne peut pas y avoir avec un booléen ;)
C'est la raison pour laquelle il est généralement recommandé d'utiliser les énumérations en lieu et place d'une série de #define "concurrents": Avec les #define, il ne peut y avoir aucune vérification quant à cohérence de la valeur utilisée, alors que, dans le cas des énumérations, les valeur sont regroupées... au sein du type dans lequel elles sont énumérées, ce qui permet au compilateur:
- de vérifier si la valeur est du type attendu
- de vérifier, dans le cas d'un test à choix multiple ("switch... case" ) de s'assurer que toutes les valeurs seront envisagées
Les enum et les int ne sont pas convertibles implicitement en void*, ni les bool d'ailleurs (en tout cas gcc sort des erreurs), il faut croire que false (le littéral) lui a le droit d'être convertit en void*, true ne le peut pas.
Je sais pas si le coup du false est défini dans la norme ou si c'est juste les compilos qui sont permissifs.
En fait, ce que la norme dit, c'est:
- les litéraux booléens sont les mots clé true et false
- ces litéraux ont le type bool
- Ce ne sont pas des lvalue
- ils participe au processus de promotion des types entiers
- les conversions en booléen sont implicites, et interviennent au même niveau que les conversion numériques (promotions d'entier ou de réels, conversion d'entier, de réels en entiers, de pointeurs et de pointeurs sur membres)
- une rvalue arithmétique, une énumération, un pointeur et un pointeur sur membre peuvent être convertie en rvalue de type bool. Une valeur nulle, un pointeur à valeur nulle, ou un pointeur sur membre à valeur nulle sera converti à false; tout autre valeur sera convertie à true
- une rvalue de type bool peut etre convertie en entier, false devenant 0 et true devenant 1
- surement encore d'autres choses intéressantes...
Ce qu'il ressort de tout cela, c'est que, au final tout peut être converti en booléen selon le principe 0 ==> false !0==>true et un booleen peut être converti en n'importe quoi selon le principe false ==>0 true ==>1
NULL étant le transtypage "barbare" de 0 en un pointeur de type void, mais un pointeur n'étant jamais qu'un entier (soit unsigned int soit unsigned long), et les entiers étant implicitement convertibles en booléen, mais un booléen intervenant dans l'ordre des promotions d'entiers (bool->char->short->int->long), l'initialisation d'un pointeur à false correspond exactement à l'initialisation à NULL...
Comme indiqué, du fait qu'une énumération représente un type abstrait de donnée, le C++ est capable de vérifier si le type correspond à ce qui est attendu, ce qui justifie que, de son coté, l'utilisation d'une valeur énumérée valant 0 ne pourra être envisagée que si elle est transtypée en pointeur du type correspondant ;)
Euh, non, quand même pas… sinon quel serait l'intérêt de ce nouveau mot-clé ?!
http://www.open-std.org/jtc1/sc22/wg...2007/n2431.pdf
Il s'agit de nullptr, et non null_ptr ;)
Je me suis embêté à faire un billet dessus :mrgreen:
http://blog.developpez.com/alp?title..._le_pointeur_n
Bonne lecture ;)
D'après ton article, nullptr est une valeur d'un nouveau type std::nullptr_t.
Ça me rappelle beaucoup la manip artisanale que j'avais faite...
Que false soit converti en 0 et le pointeur se retrouve à NULL, c'est bien le comportement attendu. Ce qui me gène c'est de ne pas avoir eu au - un warning m'indiquant la conversion implicite.
Par exemple, quand je fais :, j'ai bien un warning. Et même pire, si je faisCode:int i = 0.0;
, j'ai carrément une erreur. Ce que je comprends.Code:void *pf= 0.0f;
Du coup, j'ai perdu un peu de temps à voir pourquoi mon booléen dans ma structure ne changeait pas de valeur avant de me rendre compte que j'avais écrit trop vite...
Tu peux toujours utiliser le "nullptr trick".
Tu définis les conversions implicites qui te conviennent et c'est réglé.
false est une constante entière de valeur nulle. Toute constante entière de valeur nulle peut être utilisée comme pointeur nul.
Aucune implémentation conforme du C++ peut avoir une telle définition de NULL (elle est autorisée pour le C).
En passant, le fait que NULL soit une constante entière de valeur nulle (en C++, en C on peut aussi avoir une telle valeur castée en void*) n'indique rien du tout sur sa représentation.
J'ai du mal à voir en quoi c'est la preuve de quoi que ce soit.Citation:
La preuve en est que tu peux tester ton pointeur directement sous la forme de
if(ptr) pour t'assurer qu'il a été initialisé ou de if(!ptr) pour t'assurer qu'il n'existe pas
bool est un type de base en C++ et true et false des mots clés. Pas des define ni des typedefs.
Non -- a plus d'un titre. Premièrement en C++ NULL ne peut pas être (void*)0. Deuxièmement, le (void*) n'a pas pour but d'être un transtypage barbare (que je comprends comme un reinterpret_cast<>). Le cast en question ici serait un static_cast en C++. L'objectif de ce cast est de passer un void* dans des contextes comme:Citation:
NULL étant le transtypage "barbare" de 0 en un pointeur de type void,
où une définition de NULL comme 0 passerait simplement un entier. La différence étant justement visible sur les architectures où (void*) 0 et 0 ne partage pas la même représentation (la différence la plus commune,mais pas la seule, étant une taille différente)Code:
1
2
3
4 void f(int, ...); ... f(1, NULL); ...
Depuis quand?Citation:
mais un pointeur n'étant jamais qu'un entier (soit unsigned int soit unsigned long),
Doit-on comprendre que ces valeurs ne sont pas fortement typés (l'expression n'est pas correcte, mais j'en trouve pas de meilleure): toutes les constantes entières de valeur nulle suivantes: 0, false et NULL sont équivalentes et peuvent donc être utilisées dans les types suivants sans qu'il y ait un avertissement de transtypage: bool, pointeur, scalaire?
Effectivement, aucun warning (Visual Express) pour le code suivant:
gcc me sort un warning pour chaque conversion de NULL vers un scalaire (int, bool, char, short, long, float, double).Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 int i1=false; int i2=NULL; int i3=0; bool b1=0; bool b2=NULL; bool b3=false; A*p1=0; A*p2=false; A*p3=NULL; char c1=NULL; char c2=0; char c3=false; short s1=NULL; short s2=0; short s3=false; long l1=NULL; long l2=0; long l3=false; float f1=NULL; float f2=0; float f3=false; double d1=NULL; double d2=0; double d3=false;
C'est simplement une valeur qui est surchargee (de meme que null en Ada ou nil en Pascal).
Le compilateur peut avertir pour ce qu'il veut -- voir gcc pour NULL, on pourrait imaginer la meme chose pour false. Techniquement, la norme ne demande pas de diagnostique pour cela.
Note que (36L+3*2-42) est aussi une constante entiere de valeur nulle. (0L a ete utilise comme definition de NULL sur des archi ou long avait la meme taille qu'un pointeur alors qui int etait plus petit pour le meme genre de raisons qui ont pousse a utiliser (void*)0).