Религиозные аргументы в стороне:
-
Option1:
if (pointer[i] == NULL) ...
-
Option2:
if (!pointer[i]) ...
В C есть опция 1, функционально эквивалентная опции2?
Бывает ли более быстрое решение быстрее из-за отсутствия сравнения?
Религиозные аргументы в стороне:
Option1:
if (pointer[i] == NULL) ...
Option2:
if (!pointer[i]) ...
В C есть опция 1, функционально эквивалентная опции2?
Бывает ли более быстрое решение быстрее из-за отсутствия сравнения?
Мне нравится второй, другим людям нравится первый.
Собственно, я предпочитаю третий вид для первого:
if (NULL == ptr) {
...
}
Потому что тогда я:
Функционально они эквивалентны.
Даже если указатель NULL
не является "0" (все нулевые биты), if (!ptr)
сравнивается с указателем NULL
.
Неправильно. Он все еще здесь, потому что есть много комментариев, относящихся к нему: Однако не сравнивайте указатель с буквальным нолем. Он будет работать почти везде, но это undefined поведение IIRC.
Я предпочитаю явный стиль (первая версия). Это делает очевидным, что есть указатель, а не целое или что-то еще, но это просто вопрос стиля.
С точки зрения производительности, это не должно иметь никакого значения.
Эквивалент. Он говорит так в стандарте языка. И у людей самые сущие религиозные предпочтения!
Часто бывает полезно предположить, что у компиляторов есть как минимум минимум интеллекта. Ваш компилятор не написан сумасшедшими утятами. Он написан людьми, с годами опыта программирования и годами, изучающими теорию компилятора. Это не означает, что ваш компилятор идеален и всегда лучше знает, но это означает, что он отлично способен обрабатывать тривиальные автоматические оптимизации.
Если эти две формы эквивалентны, то почему компилятор просто не переводит один в другой, чтобы гарантировать, что обе они одинаково эффективны?
Если if (pointer[i] == NULL)
был медленнее, чем if (!pointer[i])
, не компилятор просто изменил бы его на вторую, более эффективную форму?
Так что нет, если они эквивалентны, они одинаково эффективны.
Что касается первой части вопроса, то да, они эквивалентны. В языковом стандарте это явно указано где-то - указатель оценивает значение true, если он не является NULL, а false, если он равен NULL, поэтому они точно идентичны.
Практически нет никакой разницы в производительности. Однако я предпочитаю неявный стиль второго.
NULL
должен быть объявлен в одном из стандартных файлов заголовков как таковых:
#define NULL ((void*)0)
Итак, в любом случае, вы сравниваете против нуля, и компилятор должен оптимизировать одинаково. Каждый процессор имеет некоторую "оптимизацию" или код операции для сравнения с нулем.
Ранняя оптимизация плохая. Микро-оптимизация также плохая, если вы не пытаетесь сжать каждый последний бит Гц от вашего процессора, нет смысла делать это. Как уже показали люди, компилятор в любом случае оптимизирует большую часть вашего кода.
Лучше всего сделать ваш код максимально сжатым и понятным. Если это более читаемо
if (!ptr)
чем это
if (NULL==ptr)
затем используйте его. Пока все, кто будет читать ваш код, согласятся.
Лично я использую полностью определенное значение (NULL == ptr), поэтому ясно, что я проверяю. Может быть длиннее, но я могу легко прочитать его. Я бы подумал, что! Ptr будет легко пропустить! если вы читаете быстро.
Это действительно зависит от компилятора. Я был бы удивлен, если бы большинство современных компиляторов C не генерировали практически идентичный код для конкретного сценария, который вы описываете.
Получите ваш компилятор для создания списка сборок для каждого из этих сценариев, и вы можете ответить на свой собственный вопрос (для вашего конкретного компилятора:)).
И даже если они отличаются, разница в производительности, вероятно, будет неактуальной в практических приложениях.
Включите оптимизацию компилятора, и они в основном одинаковы
проверили это на gcc 4.3.3
int main (int argc, char** argv) {
char c = getchar();
int x = (c == 'x');
if(x == NULL)
putchar('y');
return 0;
}
против
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
не выводил результат:)
Я сделал свалку сборки и нашел разницу между двумя версиями:
@@ -11,8 +11,7 @@
pushl %ecx
subl $20, %esp
movzbl -9(%ebp), %eax
- movsbl %al,%eax
- testl %eax, %eax
+ testb %al, %al
Похоже, что последнее фактически генерирует одну инструкцию, а первое генерирует два, но это довольно ненаучно.
Это gcc, без оптимизации:
test1.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: Измените pointer[0] == NULL
на !pointer[0]
gcc -s test1.c, gcc -s test2.c, diff -u test1.s test2.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
...
Остерегайтесь, gcc должен был выбить предупреждение, если не в случае компиляции с флагом -Wall
Хотя, вы всегда должны компилировать оптимизированный код gcc.
BTW, перед переменной введите ключевое слово volatile, чтобы избежать gcc игнорировать его...
Всегда указывайте версию сборки компилятора:)