Тернарный оператор как команда?

В исходном коде для nanodns нетипичное использование тернарного оператора в попытке уменьшить размер кода:

/* If the incoming packet has an AR record (such as in an EDNS request),
 * mark the reply as "NOT IMPLEMENTED"; using a?b:c form to save one byte*/
q[11]?q[3]|=4:1;

Непонятно, что делает эта строка. На первый взгляд кажется, что он присваивает значение одному из двух элементов массива, но это не так. Скорее, похоже, что либо это элемент массива, либо ничего не делается (выполняется «команда» 1).

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

if(q[11])q[3]|=4;

Буквальный эквивалент будет таким:

if (q[11])
  q[3]|=4;
else
  1;

Тернарный оператор обычно используется как часть выражения, поэтому его использование в качестве отдельной команды кажется странным. В сочетании с кажущимся неуместным 1 эта строка почти квалифицируется как запутанный код.

Я провел быстрый тест и смог скомпилировать и запустить программу C(++) с константами данных в качестве «команды», такой как void main() {0; 'a'; "foobar"; false;}. Это похоже своего рода команда nop, но я не могу найти никакой информации о таком использовании — Google не является очень удобно к этому типу поиск запрос).

Может ли кто-нибудь объяснить, что это такое и как это работает?


person Synetech    schedule 17.09.2015    source источник
comment
q[11], а не q[1]. Ваш анализ правильный; без большего контекста эквивалентен if (q[11]) { q[3] |= 4; }, только короче.   -  person Amadan    schedule 17.09.2015
comment
Это эквивалентно if ( q[11] ) q[3] |= 4;   -  person M.M    schedule 17.09.2015
comment
«эта строка почти соответствует запутанному коду» — действительно   -  person 5gon12eder    schedule 17.09.2015
comment
Кто-нибудь может точно объяснить, что это такое и как это работает? - ваш вопрос правильно описывает оба аспекта.   -  person Tony Delroy    schedule 17.09.2015
comment
Это плохой код. Используйте 1_   -  person EOF    schedule 17.09.2015


Ответы (2)


Обратите внимание, что код, на который вы ссылаетесь, ужасен и написан очень плохим программистом. В частности, заявление

«В крошечных программах на C принято определять повторно используемые выражения, чтобы сделать код меньше»

это полная б***ь***. Именно с этого заявления все пошло не так.

Размер исходного кода не имеет никакого отношения ни к размеру исполняемого файла компилятора, ни к потреблению памяти этим исполняемым файлом, ни к производительности программы. Единственное, на что это влияет, — это размер файлов исходного кода на компьютере программиста, выраженный в байтах.

Если вы не программируете на каком-нибудь компьютере 8086 середины 80-х годов с очень ограниченным пространством на жестком диске, вам никогда не нужно «уменьшать размер кода». Вместо этого пишите читаемый код.

При этом, поскольку q является массивом символов, код, который вы связали, эквивалентен

if(q[11])
{
  (int)(q[3] |= 4);
}
else
{
  1;
}

Где 1 — оператор без побочных эффектов, он будет оптимизирован. Он был помещен туда только потому, что оператор ?: требует 3-го оператора.

Единственная разница между операторами if и оператором ?: невелика: ?: неявно уравновешивает тип между 2-м и 3-м операндами.

Для повышения удобочитаемости и создания самодокументируемого кода код должен быть переписан во что-то вроде

if (q[AR_INDEX] != 0)
{
  q[REPLY_INDEX] |= NOT_IMPLEMENTED;
}

Кстати, здесь есть ошибка: q[2]|=128;. q имеет тип char, который имеет определяемую реализацией подпись, поэтому эта строка потенциально опасна. Основная проблема заключается в том, что вы никогда не должны использовать тип char для побитовых операций или любых форм арифметики, что является классической ошибкой новичка. Его необходимо заменить на uint8_t или unsigned char.

person Lundin    schedule 17.09.2015
comment
Особенно иронично то, что если автор кода так беспокоится о размере исходного кода, что решил использовать запутанную форму оператора для сохранения одного байта, они затем добавляют using a?b:c form to save one byte в комментарий - эти 33 байта, вероятно, уничтожают все сохраненное. по непонятному коду! - person TripeHound; 17.09.2015
comment
@TripeHound, это расширенная, поясненная версия кода. уменьшенная версия не содержит комментариев (кроме необязательный в начале объяснения лицензии). - person Synetech; 18.09.2015
comment
@Lundin, этот конкретный код предназначался только для минимального DNS-сервера. Их целью было "уменьшить" более полная версия, как код-гольф. Но хорошо поймать ошибку, и спасибо за объяснения. - person Synetech; 18.09.2015
comment
@Synetech До сих пор не объясняет, почему они сокращают сам исходный код. На самом деле кажется, что человек, написавший код, не понимает разницы между размером исходного кода и размером исполняемого файла. - person Lundin; 18.09.2015

В C и C++ любое выражение можно преобразовать в оператор, поставив в конце ;.

Другой пример: выражение x = 5 можно преобразовать в оператор: x = 5; . Надеюсь, вы согласитесь, что это хорошая идея.

Попытка «запретить» некоторым подмножествам выражений ; следовать за ними без необходимости усложнила бы язык. Этот код не очень полезен, но он легален.

person M.M    schedule 17.09.2015
comment
Не просто усложнить — куча функций стала бы почти непригодной для использования, например printf — вы можете себе представить, чтобы каждый раз писать int outputted = printf("...");? :) - person Amadan; 17.09.2015
comment
In C and C++ any expression can be made into a statement by putting ; at the end. И, естественно, все типы POD («встроенные») — это выражения… в этом есть смысл. Конечно, они не обязательно что-то означают, верно? Я думаю, вам нужно знать такие детали для запутывания кода-гольфа. Я бы подумал, что объекты должны быть специально написаны для поддержки использования в качестве выражения, но, по-видимому, это не так; это встроено. Я только что проверил с myclass;. Это почти напоминает мне языки более высокого уровня, такие как Python и Ruby, с таким кодом, как 1.to_s. - person Synetech; 18.09.2015