Что здесь делает компилятор: int a = b * (c * d * + e)? [дубликат]
этот вопрос уже есть ответ здесь:
- Что делает оператор унарный плюс? 14 ответов
у меня был странный баг в моей программе, и после нескольких часов отладки, я нашел очень глупую фразу:
int a = b * (c * d * + e)
если вы не видите его: между d
и e
я писал * +
, где +
и было задумано.
почему это компилируется и что это на самом деле означает?
8 ответов:
на
+
интерпретируется как унарный оператор плюс. Он просто возвращает произведен значение своего операнда.
унарный
+
возвращает повышенное значение.
Унарный-
возвращает отрицание:int a = 5; int b = 6; unsigned int c = 3; std::cout << (a * +b); // = 30 std::cout << (a * -b); // = -30 std::cout << (1 * -c); // = 4294967293 (2^32 - 3)
это компилируется, потому что
+
интерпретируется как унарный плюс, который будет выполнять целочисленные продвижения по целочисленным или перечислительным типам, и результат будет иметь тип продвигаемого операнда.предполагая, что
e
является целочисленным или неназванным типом перечисления, который в конечном итоге будет иметь интегральные рекламные акции, применяемые в любом случае, так как*
применение обычные арифметические преобразования к его операндам, которые заканчиваются на Интеграл акции для интегральных типов.из проекта стандарта C++
5.3.1
[expr.унарный.op]:операнд унарного оператора + должен иметь арифметическое, перечисление Без области видимости или тип указателя и результатом является значение аргумента. Целочисленное продвижение выполняется на целочисленных или перечислительных операндах. Тип результата - это тип повышенного операнда.
интегральные акции в разделе
4.5
[conv.пром] а если переменныеe
это типаbool, char16_t, char32_t, or wchar_t
и имеют ранг преобразования меньше, чем int тогда это будет охвачено пунктом1
:значение prvalue целочисленного типа, отличного от bool, char16_t, char32_t или wchar_t, целочисленное преобразование которого ранг (4.13) меньше, чем ранг int может быть преобразован в prvalue типа int, если int может представлять все значения источника тип; в противном случае исходное значение prvalue может быть преобразовано в значение prvalue типа без знака int.
для полного набора случаев мы можем посмотреть на cppreference.
унарный плюс также может быть полезен в некоторых случаях для разрешения двусмысленности, интересный случай будет от разрешение неоднозначной перегрузки на указателе функции и std:: функция для лямбда с помощью +.
обратите внимание, для этих ответов, ссылаясь на унарный
-
и отрицательные значения, это вводит в заблуждение, как показывает этот пример:#include <iostream> int main() { unsigned x1 = 1 ; std::cout << -x1 << std::endl ; }
что приводит к:
4294967295
посмотреть его в прямом эфире использование gcc на wandbox.
интересно отметить, что унарный плюс был добавлен к C99 для симметрии с унарным минусом, от обоснование международного стандарта-языки программирования-C:
Unary plus был принят Комитетом C89 из нескольких реализаций, для симметрия с унарным минусом.
и я не могу придумать хороший случай, когда кастинг будет недостаточно для достижения того же желаемого продвижения/преобразования. Лямбда-пример я цитирую выше, используя унарный плюс, чтобы заставить лямбда-выражение быть преобразованным в указатель функции:
foo( +[](){} ); // not ambiguous (calls the function pointer overload)
может быть достигнуто с помощью явного приведения:
foo( static_cast<void (*)()>( [](){} ) );
и можно утверждать, что этот код лучше, так как намерение явный.
стоит отметить, что Аннотированное Справочное Руководство По C++ (ARM) имеет следующий комментарий:
унарный плюс-это историческая случайность и, как правило, бесполезно.
как они объяснили, ( + ) и ( - ) были просто использованы в качестве унарного оператора:
унарные операторы действуйте только на один операнд в выражении
int value = 6; int negativeInt = -5; int positiveInt = +5; cout << (value * negativeInt); // 6 * -5 = -30 cout << (value * positiveInt); // 6 * +5 = 30 cout << (value * - negativeInt); // 6 * -(-5) = 30 cout << (value * + negativeInt); // 6 * +(-5) = -30 cout << (value * - positiveInt); // 6 * -(+5) = -30 cout << (value * + positiveInt); // 6 * +(+5) = 30
Так в коде:
int b = 2; int c = 3; int d = 4; int e = 5; int a = b * (c * d * + e) //result: 2 * (3 * 4 * (+5) ) = 120
почему он компилируется? Он компилируется, потому что
+
анализируется как унарный плюс оператор, а не оператор сложения. Компилятор пытается проанализировать как можно больше, не создавая синтаксических ошибок. Так вот:d * + e
анализируется как:
d
(операнд)*
(оператор умножения)+
(оператор "унарный плюс" )
e
(операнд)а это:
d*++e;
анализируется как:
d
(операнд)*
(оператор умножения)++
(предварительно оператор инкремента )
e
(операнд)более того, этого:
d*+++e;
анализируется как:
d
(операнд)*
(оператор умножения)++
(предварительно оператор инкремента )
+
(оператор "унарный плюс" )
e
(операнд)обратите внимание, что он не создает синтаксическую ошибку, а ошибку компилятора "LValue requrired".
чтобы еще больше исказить правильные ответы, уже приведенные здесь, Если вы скомпилируете с флагом-s, компилятор C выведет файл сборки, в котором фактические сгенерированные инструкции могут быть проверены. С помощью следующего кода C:
int b=1, c=2, d=3, e=4; int a = b * (c * d * + e);
сгенерированная сборка (с использованием gcc, компиляция для amd64) начинается с:
movl , -20(%ebp) movl , -16(%ebp) movl , -12(%ebp) movl , -8(%ebp)
таким образом, мы можем идентифицировать отдельные позиции памяти -20 (%ebp) как переменную b, вплоть до -8 (%ebp) как переменную e. -4 (%epp) является переменной a. теперь, расчет отображается как:
movl -16(%ebp), %eax imull -12(%ebp), %eax imull -8(%ebp), %eax imull -20(%ebp), %eax movl %eax, -4(%ebp)
Итак, как было прокомментировано другими людьми, отвечающими, компилятор просто рассматривает "+e " как унарную положительную операцию. Первая инструкция movl помещает содержимое переменной e в регистр накопителя EAX, который затем быстро умножается на содержимое переменной d или -12(%ebp) и т. д.
Это просто основы математики. Например:
5 * -4 = -20 5 * +4 = 5 * 4 = 20 -5 * -4 = 20
Минус * Минус = Плюс
Положительный * Отрицательный = Отрицательный
Позитивный * Положительное = Положительное
Это самое простое объяснение есть.
минус ( -) и плюс (+) просто скажите, является ли число положительным или отрицательным.