2014-01-05 4 views
0

Недавно я наткнулся на кусок кода, как это (не реальный, но более короткий пример основывается на него):Оценка ярлыка вместо выполнения выражения if (condition);

#include <stdio.h> 

int main() 
{ 
    int n; 

    printf ("n? "); 
    scanf ("%d", &n); 

    n%2 && printf ("N is odd\n"); /* <-- this is it */ 

    return 0; 
} 

В случае, если кто-то не понял, этот код является эквивалент:

int main() 
{ 
    int n; 

    printf ("n? "); 
    scanf ("%d", &n); 

    if (n%2) 
    printf ("N is odd\n"); 

    return 0; 
} 

разборки этого кода, скомпилированный с GCC 4.4.5-8 для x86-64 бит дает это для той части, в которой n оценивается и printf() условно называется:

 andl $1, %eax 
     testb %al, %al 
     je  .L3 
     movl $.LC2, %eax 
     movq %rax, %rdi 
     movl $0, %eax 
     call printf 
     testl %eax, %eax 
.L3: 
     movl $0, %eax 
     leave 
     ret 

Звучит как код, созданный оператором if. «Стандартный» способ дает это:

 andl $1, %eax 
     testb %al, %al 
     je  .L2 
     movl $.LC2, %edi 
     call puts 
.L2: 
     movl $0, %eax 
     leave 
     ret 

Slighly короче, и немного быстрее, тоже, потому что компилятор может использовать puts() вместо printf(), так как он обнаружил, что printf() используется для печати одной строки, и его возвращаемое значение не используется. Первый пример должен оценить второе выражение после &&, потому что первый оценивается как истинный, поэтому printf() должен был использоваться для получения значения для оценки с.

Итак, мой вопрос: может ли этот ярлык для оценки ярлыка считаться хорошим кодированием? Он работает, но ... делает это лучше, чем помогает выиграть конкурс на один лайнер C? Судя по примеру, который я предоставил, я бы сказал «нет», но может ли он быть примером, который доказывает иначе?

ПРИМЕЧАНИЕ: при попытке скомпилировать исходный код компилятор даже сгенерировал ICE (MingW 2.95.2) с текстовой ошибкой, говорящей о «ошибке при выполнении do_jump» или что-то в этом роде.

+0

Это действительно основанный на мнениях вопрос и, следовательно, может быть закрыт. Мое субъективное мнение заключается в том, что версия с использованием '&&' менее понятна, чем просто использовать 'if' для защиты' printf'. Во всяком случае, поэтому многие компании имеют [соглашения о кодировании] (http://en.wikipedia.org/wiki/Coding_conventions), чтобы предотвратить подобные вещи. – DaoWen

+1

Ну, я действительно не ищу субъективных мнений.Мое утверждение состоит в том, что этот трюк, похоже, не полезен, но может существовать пример, который доказывает, что этот трюк работает лучше (используя любую метрику, такую ​​как скорость или размер кода), чем эквивалентный оператор if, и я спрашиваю, знает ли кто-нибудь такой пример (ы). –

+1

Все, что скрывает намерение вашего кода, нельзя считать хорошим кодированием, если у него нет ОГРОМНОЙ окупаемости. (И в этом случае он должен быть задокументирован до смерти.) И даже если бы это было быстрее, большинство машин были достаточно быстрыми, и в наши дни большинство программ достаточно велики, что попытка микро-оптимизации инструкций почти всегда неправильный выбор, если вы не можете * ПРОВЕРИТЬ *, что это критический путь в вашей программе. – keshlam

ответ

5

Этот метод не является полезным вообще, поскольку он противоречит общепринятому соглашению и ведет к нечитаемому коду. (Обратите внимание, что эта техника приемлема на других языках, таких как perl, где это обычно используемая идиома.)

Одно место, которое вы можете использовать этой техникой, находится в функциональном макросе.

#define foo(x, y) (((x) % 2) && (y)) 

Вы не можете написать макрос как

#define foo(x, y) if ((x) % 2) (y) 

Потому что это запутывают вещи, как

if (a) foo(x, y); else bar(); 

Обычный обходной путь от

#define foo(x, y) do { if ((x) % 2) (y); } while (0) 

не работает с запятая ,

for (i = 0, foo(x, y); i < 10; i++) ... 

Таким образом, любое такое использование этой идиомы должно быть хорошо прокомментировано.

+0

Конечно, если/else в макросах C также можно обрабатывать с помощью оператора?:. – keshlam