Какова цель унарного оператора плюс (+) в C?

В C допустимо написать что-то вроде:

int foo = +4;

Однако, насколько я могу судить, унарный плюс (+) в +4 не работает. Это?


person zneak    schedule 09.07.2011    source источник
comment
Не совсем то же самое, но связано: stackoverflow.com/questions/727516/   -  person jglouie    schedule 09.07.2011
comment
msdn.microsoft.com/en-us/library/s50et82s.aspx Унарный оператор плюс, предшествующий выражению в круглых скобках, принудительно группирует вложенные операции. Он используется с выражениями, включающими более одного ассоциативного или коммутативного бинарного оператора. Операнд должен иметь арифметический тип. Результатом является значение операнда. Целочисленный операнд подвергается целочисленному преобразованию. Тип результата — это тип расширенного операнда.   -  person Tim S.    schedule 09.07.2011
comment
K&R говорит, что это было просто добавлено для симметрии в стандарте.   -  person Aaron Yodaiken    schedule 10.07.2011
comment
@ Джереми: есть. Например. там написано, что +short(1) имеет тип int, а не short.   -  person MSalters    schedule 11.07.2011
comment
@Jeremy, re: Тим С. Проверьте источник. Вы здесь про самолет? :)   -  person luser droog    schedule 12.08.2011
comment
@TimS.: Унарный оператор плюс, предшествующий выражению в круглых скобках, принудительно группирует заключенные операции -- О? Именно круглые скобки, а не +, заставляют группироваться.   -  person Keith Thompson    schedule 01.07.2013
comment
@MSalters: +short(1) - синтаксическая ошибка. Вы думали о +(short)1?   -  person Keith Thompson    schedule 01.07.2013
comment
Бьерн Страуструп дал объяснение на своем языке калькулятора в одной из своих книг, перефразировав, когда есть унарный минус, кто-то всегда пытается сделать унарный плюс, поэтому проще просто реализовать это, чем объяснять, почему это бесполезно.   -  person Brandin    schedule 26.06.2017


Ответы (8)


Согласно стандарту C90 в 6.3.3.3:

Результатом унарного оператора + является значение его операнда. Интегральное продвижение выполняется над операндом. и результат имеет продвигаемый тип.

и

Операнд унарного оператора + или - должен иметь арифметический тип..

person lccarrasco    schedule 09.07.2011
comment
Итак, +x - это noop, если только sizeof x < sizeof(int)? - person zneak; 09.07.2011
comment
Эти цитаты из стандарта показывают, что унарный + — это не просто пустая операция. Он выполняет интегральное продвижение операнда. И, что, возможно, более важно, он превращает lvalue в rvalue. - person Sander De Dycker; 09.07.2011
comment
Что ж, теоретически возможно, что sizeof(short) == sizeof(int), но short имеет заполнение, и теоретически в такой системе заполнение может потребовать обнуления или расширения знака. Теоретически. - person Dietrich Epp; 09.07.2011
comment
Обратите внимание, что когда он говорит арифметический тип, он относится как к целочисленным типам, так и к типам с плавающей запятой, пример, который показывает Nemo, работает, потому что указатели не входят в эту классификацию. Целочисленные типы и указатели образуют скалярные типы. - person lccarrasco; 09.07.2011
comment
Это никак не влияет на float или double, не так ли? - person S.S. Anne; 14.01.2020

Вы можете использовать его как своего рода утверждение, что выражение имеет арифметический тип:

#define CHECK_ARITHMETIC(x) (+(x))

Это вызовет ошибку времени компиляции, если x оценивается (скажем) как указатель.

Это единственное практическое применение, которое я могу придумать.

person Nemo    schedule 09.07.2011
comment
Вы также можете привести значение перечисления к его целочисленному значению таким образом. - person GManNickG; 10.07.2011
comment
Просто задокументируйте это, потому что это совершенно неочевидно. - person BlueRaja - Danny Pflughoeft; 10.07.2011
comment
Почему нельзя сделать то же самое, например, с (-(-(x)))? - person Aaron Yodaiken; 10.07.2011
comment
@luxun, я уверен, что ты сможешь. - person zneak; 10.07.2011
comment
@GMan, разве преобразование enum в int не является неявным в C? - person zneak; 10.07.2011
comment
@zneak: Да, но с этим вы можете выполнить преобразование без указания типа цели. В общем, нет никакого способа узнать основной тип. - person GManNickG; 10.07.2011
comment
@luxun @zneak: Интересная идея... Дай-ка посмотреть... Хорошо, как насчет этого. Если int является 32-битным, а x оказывается int равным -2^31, то -x переполняет целое число со знаком, что технически является неопределенным поведением. :-) - person Nemo; 10.07.2011
comment
@Nemo, смысл в том, чтобы вызвать ошибку времени компиляции, если CHECK_ARITHMETIC не используется ни в каком присваивании, меня это устраивает :) - person zneak; 10.07.2011
comment
@zneak: Дело в том, чтобы вызвать ошибку времени компиляции, если выражение имеет тип указателя, но быть неоперативным, если оно имеет арифметический тип. Ваша версия работает нормально, за исключением арифметических выражений, которые имеют значение INT_MIN. (Ну, во всяком случае, в теории. На практике это, вероятно, работает нормально на любой реалистичной машине.) Тем не менее, строгое прочтение стандарта говорит, что они разные. - person Nemo; 10.07.2011
comment
Я имел в виду, что вы можете просто сделать CHECK_ARITHMETIC(x);, не используя его в задании, чтобы получить ошибку компиляции, и использовать x сразу после этого. - person zneak; 10.07.2011
comment
@zneak: Даже если вы не используете его в задании, я считаю, что просто оценить -x, когда x равно INT_MIN, технически вызвать неопределенное поведение. - person Nemo; 10.07.2011
comment
То же самое происходит с #define CHECK_ARITHMETIC(x) (-(x)). Унарный + был добавлен в стандарт ANSI только для симметрии с унарным -. - person Priyank Bhatnagar; 10.07.2011
comment
@logic_max: смысл предложения состоит в том, чтобы использовать (+(x)), чтобы утверждать, что выражение имеет арифметический тип во время компиляции. Вместо этого вы не можете использовать (-(x)) или (-(-(x))), не рискуя неопределённым поведением. Не то чтобы это было особенно полезно... И я не утверждаю, что это было обоснованием... Но насколько мне известно, это единственный способ сделать это. - person Nemo; 11.07.2011
comment
@Nemo: Да, я понимаю, +1 за это. Но я не думаю, что (-(x)) вызовет какое-либо неопределенное поведение. Оба, унарные + и - не применимы к указателям, поэтому оба будут генерировать ошибки времени компиляции. Значение во время выполнения не имеет к этому никакого отношения. - person Priyank Bhatnagar; 11.07.2011
comment
Вы также можете использовать - для утверждения арифметического типа, если вы не вычисляете выражение. Например, (0&&-(x)). Если вы хотите убедиться, что x все еще оценивается, используйте ((x),(0&&-(x))). - person R.. GitHub STOP HELPING ICE; 28.09.2011

Я знаю одно очень удобное применение унарного оператора плюс: в макросах. Предположим, вы хотите сделать что-то вроде

#if FOO > 0

Если FOO не определено, язык C требует, чтобы в этом случае его заменили на 0. Но если FOO было определено с пустым определением, приведенная выше директива приведет к ошибке. Вместо этого вы можете использовать:

#if FOO+0 > 0

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

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

Изменить: обратите внимание, что вы даже можете использовать это, чтобы различить случаи, когда FOO определяется как ноль, а определяется как пустое, например:

#if 2*FOO+1 == 1
/* FOO is 0 */
#else
/* FOO is blank */
#endif
person R.. GitHub STOP HELPING ICE    schedule 09.07.2011
comment
Кстати, мне было бы интересно услышать, есть ли у кого-нибудь другой способ сделать тесты, которые я описал. Мне ничего не приходило в голову... - person R.. GitHub STOP HELPING ICE; 10.07.2011
comment
Вы уверены, что эти директивы препроцессора действительно относятся к унарному оператору +, как это видно в C? Во втором примере с пустым FOO выражение будет недействительным C. - person zneak; 10.07.2011
comment
zneak прав: это крутой трюк, но технически это пример того, как унарный оператор + обрабатывается препроцессором C, а не компилятором C. - person benzado; 10.07.2011
comment
#ifndef FOO\n#define FOO 0\n#endif\n#if FOO > 0\n работает. - person Aaron Yodaiken; 10.07.2011
comment
@luxun: Нет, это не так. С #define FOO #ifndef FOO ложно. Неопределенные имена уже заменены на 0 в #if, поэтому ваш фрагмент кода бесполезен. Пустые определения сильно отличаются от неопределенных и распространены из-за таких вещей, как -DFOO. - person R.. GitHub STOP HELPING ICE; 10.07.2011
comment
Действительно так. В любом случае, пара операторов - - (требуется пробел!) эквивалентна +, насколько я могу судить, что делает + довольно избыточным... Нет, это неправильно, это не эквивалентно, когда операнд INT_MIN... :- ) - person R.. GitHub STOP HELPING ICE; 10.07.2011
comment
Не будет ли (2*FOO+1) работать как юридическая проверка во время выполнения того, как был определен FOO? - person hotpaw2; 10.07.2011
comment
Если вы поместите его внутрь #ifdef FOO, да. - person R.. GitHub STOP HELPING ICE; 10.07.2011
comment
Извините, что наткнулся на такой старый ответ, но разве это не пример двоичного плюса, а не унарного плюса? - person Aurora Vollvik; 02.11.2018
comment
@AuroraVollvik: это унарный плюс, если макрос определен, но с пустым определением. - person R.. GitHub STOP HELPING ICE; 02.11.2018
comment
Этот пример вовсе не является унарным плюсом, это двоичный код, упомянутый @AuroraVollvik. + может быть унарным или бинарным оператором, определяемым контекстом использования, если у вас есть два операнда, это не унарное использование. Унарные имеют только один операнд. - person Undefined Behavior; 07.02.2019
comment
@UndefinedBehavior: является ли он унарным или двоичным, зависит от того, как определено FOO. - person R.. GitHub STOP HELPING ICE; 07.02.2019
comment
Я не думаю, что это оператор, когда он находится в макросе препроцессора. - person S.S. Anne; 14.01.2020

Довольно много. Он в основном присутствует для полноты и для того, чтобы подобные конструкции выглядели немного чище:

int arr[] = {
    +4,
    -1,
    +1,
    -4,
};
person Community    schedule 09.07.2011
comment
Ни в коем случае это не чище, чем альтернатива без унарных +s. - person ruohola; 13.06.2019

Я обнаружил две вещи, которые делает унарный оператор +:

  • integer promotion
  • turning lvalue into rvalue

Пример целочисленного продвижения:

#include <stdio.h>

int main(void) {

    char ch;
    short sh;
    int i;
    long l;

    printf("%d %d %d %d\n",sizeof(ch),sizeof(sh),sizeof(i),sizeof(l));
    printf("%d %d %d %d\n",sizeof(+ch),sizeof(+sh),sizeof(+i),sizeof(+l));
    return 0;
}

Типичный вывод (на 64-битной платформе):

1 2 4 8
4 4 4 8

Пример преобразования lvalue в rvalue:

int i=0,j;

j=(+i)++; // error lvalue required
person A.s. Bhullar    schedule 17.07.2014
comment
Ну наконец то. Потенциально полезное приложение, которое сообщит некоторому универсальному коду размер аргумента после целочисленного преобразования, который может не быть sizeof(int). Я понятия не имею, когда я буду его использовать. Но приятно все равно. NB: отредактированный ответ, чтобы показать, что не все идет к 4. - person Persixty; 07.09.2017

Не совсем бездействующий

Унарный оператор + делает только одну вещь: применяет целочисленные повышения. Поскольку это произошло бы в любом случае, если бы операнд использовался в выражении, можно представить, что унарный + находится в C просто для симметрии с унарным -.

Трудно увидеть это в действии, потому что рекламные акции применяются повсеместно.

Я придумал это:

printf("%zd\n", sizeof( (char) 'x'));
printf("%zd\n", sizeof(+(char) 'x'));

который (на моем Mac) печатает

1
4
person DigitalRoss    schedule 11.07.2011
comment
Это полезно для печати char в C++ с std::cout. - person S.S. Anne; 14.01.2020

Какова цель унарного оператора «+» в C?

Унарный плюс был добавлен к C для симметрии с унарным минусом из Обоснование международного стандарта — Языки программирования — C:

Унарный плюс был принят Комитетом C89 из нескольких реализаций для симметрии с унарным минусом.

и это не операция, она выполняет целочисленное продвижение своего операнда. Цитата из моего ответа на Выполняет ли оператор Unary + преобразование типов?:

В черновом разделе стандарта C99 6.5.3.3 Унарные арифметические операторы говорится:

Результатом унарного оператора + является значение его (расширенного) операнда. На операнде выполняется целочисленное повышение, и результат имеет повышенный тип.

Стоит отметить, что Справочное руководство по C++ с примечаниями (ARM) предоставляет следующие комментарий к унарному плюсу:

Унарный плюс — историческая случайность и вообще бесполезный.

person Shafik Yaghmour    schedule 11.06.2015
comment
В C++ также злоупотребляют, когда static_cast более четко показывает намерение, например Разрешение неоднозначной перегрузки указателя функции и std::function для лямбда используя + - person Shafik Yaghmour; 30.11.2017

Под «не-оперативкой» вы имеете в виду инструкцию по сборке?
Если да, то точно нет.

+4 равно 4 — компилятор не будет добавлять никаких дополнительных инструкций.

person DefenestrationDay    schedule 10.07.2011
comment
Под «noop» я имел в виду «ничего не делать» — я ожидаю, что компиляторы не будут добавлять инструкцию ради добавления инструкции. - person zneak; 10.07.2011
comment
Но +(char)4 не то же самое, что (char)4. - person DigitalRoss; 11.07.2011