Подтвердить что ты не робот

C: Сравнение с NULL

Религиозные аргументы в стороне:

  • Option1:

    if (pointer[i] == NULL) ...
    
  • Option2:

    if (!pointer[i]) ...  
    

В C есть опция 1, функционально эквивалентная опции2?

Бывает ли более быстрое решение быстрее из-за отсутствия сравнения?

4b9b3361

Ответ 1

Мне нравится второй, другим людям нравится первый.

Собственно, я предпочитаю третий вид для первого:

if (NULL == ptr) {
   ...
}

Потому что тогда я:

  • не сможет пропустить и просто набрать одно "="
  • не пропустит "== NULL" и не допустит ошибку, если условие длинное (несколько строк)

Функционально они эквивалентны.

Даже если указатель NULL не является "0" (все нулевые биты), if (!ptr) сравнивается с указателем NULL.

Неправильно. Он все еще здесь, потому что есть много комментариев, относящихся к нему: Однако не сравнивайте указатель с буквальным нолем. Он будет работать почти везде, но это undefined поведение IIRC.

Ответ 2

Я предпочитаю явный стиль (первая версия). Это делает очевидным, что есть указатель, а не целое или что-то еще, но это просто вопрос стиля.

С точки зрения производительности, это не должно иметь никакого значения.

Ответ 3

Эквивалент. Он говорит так в стандарте языка. И у людей самые сущие религиозные предпочтения!

Ответ 4

Часто бывает полезно предположить, что у компиляторов есть как минимум минимум интеллекта. Ваш компилятор не написан сумасшедшими утятами. Он написан людьми, с годами опыта программирования и годами, изучающими теорию компилятора. Это не означает, что ваш компилятор идеален и всегда лучше знает, но это означает, что он отлично способен обрабатывать тривиальные автоматические оптимизации.

Если эти две формы эквивалентны, то почему компилятор просто не переводит один в другой, чтобы гарантировать, что обе они одинаково эффективны?

Если if (pointer[i] == NULL) был медленнее, чем if (!pointer[i]), не компилятор просто изменил бы его на вторую, более эффективную форму?

Так что нет, если они эквивалентны, они одинаково эффективны.

Что касается первой части вопроса, то да, они эквивалентны. В языковом стандарте это явно указано где-то - указатель оценивает значение true, если он не является NULL, а false, если он равен NULL, поэтому они точно идентичны.

Ответ 5

Практически нет никакой разницы в производительности. Однако я предпочитаю неявный стиль второго.

Ответ 6

NULL должен быть объявлен в одном из стандартных файлов заголовков как таковых:

#define NULL ((void*)0)

Итак, в любом случае, вы сравниваете против нуля, и компилятор должен оптимизировать одинаково. Каждый процессор имеет некоторую "оптимизацию" или код операции для сравнения с нулем.

Ответ 7

Ранняя оптимизация плохая. Микро-оптимизация также плохая, если вы не пытаетесь сжать каждый последний бит Гц от вашего процессора, нет смысла делать это. Как уже показали люди, компилятор в любом случае оптимизирует большую часть вашего кода.

Лучше всего сделать ваш код максимально сжатым и понятным. Если это более читаемо

if (!ptr)

чем это

if (NULL==ptr)

затем используйте его. Пока все, кто будет читать ваш код, согласятся.

Лично я использую полностью определенное значение (NULL == ptr), поэтому ясно, что я проверяю. Может быть длиннее, но я могу легко прочитать его. Я бы подумал, что! Ptr будет легко пропустить! если вы читаете быстро.

Ответ 8

Это действительно зависит от компилятора. Я был бы удивлен, если бы большинство современных компиляторов C не генерировали практически идентичный код для конкретного сценария, который вы описываете.

Получите ваш компилятор для создания списка сборок для каждого из этих сценариев, и вы можете ответить на свой собственный вопрос (для вашего конкретного компилятора:)).

И даже если они отличаются, разница в производительности, вероятно, будет неактуальной в практических приложениях.

Ответ 9

Включите оптимизацию компилятора, и они в основном одинаковы

проверили это на 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

не выводил результат:)

Ответ 10

Я сделал свалку сборки и нашел разницу между двумя версиями:

@@ -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

Ответ 11

#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 игнорировать его...

Всегда указывайте версию сборки компилятора:)