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

Почему C не имеет оператора логического присваивания?

Мне нужно было закодировать инструкцию формы

a = a || expr;

где expr должен быть оценен, и результат должен быть назначен a, если if a не установлен. это зависит от логических или коротких замыканий.

Более короткий способ написать выше, конечно, будет

a ||= expr;

но (к моему удивлению) C не имеет операторов логического присваивания.

Итак, мой вопрос двоякий. Во-первых, существует более короткий способ написать первое утверждение в стандарте C (тернарный оператор еще хуже - a = a ? a : expr требует, чтобы я произносил a трижды).

Во-вторых, почему в C нет логических назначений? Возможные причины, о которых я мог подумать, следующие:

  • это усложняет анализ грамматики?
  • Есть ли какая-то тонкость в обработке короткого замыкания для этих случаев?
  • он считался лишним (но разве это не аргумент против ВСЕХ назначений операторов?)

EDIT

Пожалуйста, откройте этот вопрос, потому что:

  • Вопрос, с которым он был связан (как предполагаемый дубликат), НЕ ОТВЕТИЛ. Ответ на этот вопрос (принятый) гласит, что ||= отсутствует, поскольку дублирует функциональность |=. Это неправильный ответ. |= не замыкается.

  • C и С++ не являются одними и теми же языками. Я хочу знать, почему у C его нет. Фактически, тот факт, что производные языки, такие как С++ и, в частности, Java (которые не страдают от проблем с устаревшим кодом, как это было предложено в ответе Эдмунда), делает вопрос еще более интересным.

РЕДАКТИРОВАТЬ 2

Теперь кажется, что мое первоначальное намерение было неправильным. В заявлении a = a || expr (где a является интегралом, а expr возвращает целочисленное значение, сначала оба a и expr будут неявно преобразованы в "booleans", а затем будет присвоено значение "boolean" до a. Это будет некорректно - интегральное значение будет потеряно. Спасибо, Йенс и Эдмунд.

Итак, для первой части вопроса правильные пути, а не альтернативы:), чтобы закодировать мое намерение, было бы следующим:

if (!a) a = expr;

или

a = a ? a : expr;

они должны быть оптимизированы одинаково (я думаю), хотя лично я предпочел бы первый (потому что он имеет один тип a).

Однако вторая часть вопроса остается. Аргументы, высказанные Йенсом и Эдмундом о двусмысленности в a ||= expr, одинаково хорошо применимы к a = a || expr. случай назначения можно просто рассматривать как нормальный:

  • конвертировать a в boolean
  • если это правда, значение всего выражения становится равным логическому значению a
  • иначе оцениваем expr, преобразуем результат в boolean, присваиваем a и возвращаем его

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

4b9b3361

Ответ 1

a ||= expr является проблематичным из-за короткого замыкания его эквивалента a = a || expr.

Чтобы функция a ||= expr вроде a = a || expr рассматривала утверждение OP:

"В заявлении a = a || expr... сначала оба a и expr будут неявно преобразованы в" booleans ","

Это не совсем правильно. expr не будет преобразован, если a оценивается до true. Это будет иметь значение, если expr будет чем-то вроде scanf() или rand().

Код, например a ||= scanf("%d", &i) != 1;, будет пытаться сканировать данные с ложным значением в a. Хотя можно было бы расширить язык таким образом, дополнительные операторы короткого замыкания для текущего набора || и && скорее всего вызовут больше проблем с кодированием, чем упрощения.

OTOH: быстрый, если запутанный способ записи кода, где функции возвращают ненулевые коды при ошибке.

// Only perform functions until an error occurs.
bool error = foo1();
error &&= foo2();
error &&= foo3();

Ответ 2

Я предполагаю, что простой ответ заключается в том, что || является логическим оператором: а в C "логическое" - 0 или 1. Операнды неявно преобразуются в логические значения (я не проверял, что то, что спецификация фактически говорит, но это как C ведет себя), а результат - логический.

Изменение семантики для поддержки этого шаблона вполне возможно - пока кто-то не полагается на || делать то, что он всегда делал.

Ответ 3

Поскольку возвращаемый тип операторов || и && не совпадает с типом их левого аргумента.

Возвращаемый тип || и && всегда int 1 а левый аргумент может быть любым целым, с плавающей точкой или типом указателя. Операнды также не должны быть одного типа. Поэтому определение x ||= y как x = x || y и x &&= y как x = x && y, как было бы согласуется с другими расширенными назначениями, не сможет сохранить результат в аргументе для большинства типов.

Вы можете найти другие определения, например. x ||= y как if(!x) x = y и x &&= y как if(!y) x = y, но это было бы не совсем очевидно, и это не так полезно, поэтому оно не было включено.

1 В С++ это bool.

Ответ 4

Я не могу найти никакой конкретной причины, почему операторы не существуют (на C99).

Поэтому единственной причиной, по которой я могу найти, является то, что в C89 не было логического типа, и эти логические операторы были предназначены исключительно для использования в if.

Пример:

int i = 5;

/* This should not make any difference,
   since or'ing with false, shouldn't change
   the value... dib di dib diddy...*/
i ||= 0; /* Actually: i = i || 0, which gives 'true' */

i теперь "1", что для большинства людей довольно контрастно интуитивно.

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

По-моему, реализация a ||= b; как if(!a) a = b; была бы довольно простой и была бы реализована, например, Lua.

Итак, вы сомневаетесь, почему C был спроектирован так, как он был разработан. Если бы этот вопрос касался С++, вы могли бы, например, спросить Бьярна Страустрапа и спросить его, что с ним случилось. Поскольку это не так, мне кажется, что это тупик, потому что стандарт был написан довольно давно, и вы больше не можете спрашивать людей, почему h ***.

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

Надеюсь, я немного помогу.