C: comparaison avec NULL
Arguments religieux mis à part:
-
Option1:
if (pointer[i] == NULL) ...
-
Option2:
if (!pointer[i]) ...
En C, l'option1 est-elle fonctionnellement équivalente à l'option2?
La résolution ultérieure est-elle plus rapide en raison de l'absence de comparaison ?
11 réponses
J'aime le second, les autres aiment le premier.
En fait, je préfère un troisième type au premier:
if (NULL == ptr) {
...
}
Parce que alors je:
- ne sera pas en mesure de manquer et tapez simplement un " = "
- ne manquera pas le "= = NULL " et le confondra avec le contraire si la condition est longue (plusieurs lignes)
Fonctionnellement, ils sont équivalents.
Même si un pointeur NULL
n'est pas " 0 " (tous les bits zéro), if (!ptr)
se compare au pointeur NULL
.
Le ce qui suit est incorrect. Il est toujours là parce qu'il y a beaucoup de commentaires qui s'y réfèrent: Cependant, ne comparez pas un pointeur avec un zéro littéral. Cela fonctionnera presque partout mais est un comportement indéfini IIRC.
Je préfère le style explicite (première version). Il est évident qu'il y a un pointeur impliqué et non un entier ou autre chose, mais c'est juste une question de style.
Du point de vue de la performance, cela ne devrait pas faire de différence.
Équivalent. Il le dit dans la norme de langue. Et les gens ont les plus damndest préférences religieuses!
Il est souvent utile de supposer que les auteurs de compilateurs ont au moins un minimum d'intelligence. Votre compilateur est Pas écrit par des canetons commotionnés. Il est écrit par des êtres humains, avec des années d'expérience en programmation et des années passées à étudier la théorie du compilateur. Cela ne signifie pas que votre compilateur est parfait, et sait toujours mieux, mais celasignifie qu'il est parfaitement capable de gérer des optimisations automatiques triviales.
Si les deux formes sont équivalentes, alors pourquoi le compilateur ne traduirait-il pas simplement l'un dans l'autre pour s'assurer que les deux sont également efficaces?
Si {[0] } était plus lent que if (!pointer[i])
, le compilateur ne le changerait-il pas simplement en une deuxième forme plus efficace?
Donc non, en supposant qu'ils sont équivalents, ils sont tout aussi efficaces.
Quant à la première partie de la question, oui, ils sont équivalents. La norme de langage l'indique explicitement quelque part-un pointeur évalue à true s'il est non NULL, et false s'il l'est NULL, donc les deux sont exactement identiques.
Presque certainement pas de différence de performance. Je préfère le style implicite de la seconde, cependant.
NULL
doit être déclaré dans l'un des fichiers d'en-tête standard en tant que tel:
#define NULL ((void*)0)
Donc, de toute façon, vous comparez contre zéro, et le compilateur devrait optimiser les deux de la même manière. Chaque processeur a une "optimisation" ou un opcode pour comparer avec zéro.
L'optimisation précoce est mauvaise. La micro-optimisation est également mauvaise, sauf si vous essayez de presser chaque dernier bit De Hz de votre CPU, il ne sert à rien de le faire. Comme les gens l'ont déjà montré, le compilateur optimisera la plupart de votre code de toute façon.
Il est préférable de rendre votre code aussi concis et lisible que possible. Si c'est plus lisible
if (!ptr)
Que ce
if (NULL==ptr)
Ensuite, utilisez-le. Tant que tous ceux qui liront votre code sont d'accord.
Personnellement, j'utilise la valeur entièrement définie (NULL= = ptr) donc il est clair ce que je vérifie. Peut être plus long à taper, mais je peux facilement lire. Je pense à la !ptr serait facile à manquer ! si la lecture rapidement.
Cela dépend vraiment du compilateur. Je serais surpris si la plupart des compilateurs C modernes ne généraient pas de code pratiquement identique pour le scénario spécifique que vous décrivez.
Obtenir votre compilateur pour générer une assemblée d'inscription pour chacun de ces scénarios et vous pouvez répondre à votre propre question (pour votre compilateur :)).
Et même si elles sont différentes, la différence de performance sera probablement hors de propos dans les applications pratiques.
Activez l'optimisation du compilateur et ils sont fondamentalement les mêmes
Testé sur gcc 4.3.3
int main (int argc, char** argv) {
char c = getchar();
int x = (c == 'x');
if(x == NULL)
putchar('y');
return 0;
}
Vs
int main (int argc, char** argv) {
char c = getchar();
int x = (c == 'x');
if(!x)
putchar('y');
return 0;
}
gcc -O -o test1 test1.c
gcc -O -o test2 test2.c
diff test1 test2
Produit aucune sortie :)
J'ai fait un vidage d'assemblage, et j'ai trouvé la différence entre les deux versions:
@@ -11,8 +11,7 @@
pushl %ecx
subl $20, %esp
movzbl -9(%ebp), %eax
- movsbl %al,%eax
- testl %eax, %eax
+ testb %al, %al
Il semble que ce dernier génère réellement une instruction et la première en génère deux, mais c'est assez non scientifique.
C'est gcc, pas d'optimisations:
Essai1.c:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char *pointer[5];
if(pointer[0] == NULL) {
exit(1);
}
exit(0);
}
Test2.c: Changer pointer[0] == NULL
en !pointer[0]
Gcc-s test1.c, gcc - s test2.C, test diff-U1.s essai2.s
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char pointer[5];
/* This is insense you are comparing a pointer to a value */
if(pointer[0] == NULL) {
exit(1);
}
...
}
=> ...
movzbl 9(%ebp), %eax # your code compares a 1 byte value to a signed 4 bytes one
movsbl %al,%eax # Will result in sign extension...
testl %eax, %eax
...
Attention, gcc aurait dû sortir un avertissement, sinon la compilation de cas avec -Wall
drapeau sur
Cependant, vous devez toujours compiler en code gcc optimisé.
BTW, précédez votre variable avec le mot-clé volatile afin d'éviter que gcc ne l'ignore...
Mentionnez toujours la version de construction de votre compilateur:)