Что такое оператор «->» в C ++?

8927

После прочтения скрытых объектов и темные углы C ++ / STL на comp.lang.c++.moderated, я был полностью удивлен , что следующий фрагмент кода компилируется и работает как в Visual Studio 2008 и G ++ 4.4.

Вот код:

#include <stdio.h>
int main()
{
    int x = 10;
    while (x --> 0) // x goes to 0
    {
        printf("%d ", x);
    }
}

Вывод:

9 8 7 6 5 4 3 2 1 0

Я бы предположил, что это C, так как он работает и в GCC. Где это определено в стандарте и откуда оно взято?

GManNickG
источник
503
Или даже просто правильный интервал ... Я не думаю, что когда-либо видел пробел между переменной и тем ++или --
другим
1155
Этот оператор «идет» может быть отменен (0 <- x). А также есть оператор «бежит к» (0 <---- x). Боже, самая забавная вещь, которую я когда-либо слышал о синтаксисе c ++ =) +1 за вопрос.
SadSido
233
Как ни странно, хотя интерпретация очень неправильная, она описывает, что код делает правильно. :)
Нолдорин
811
Представьте себе новые возможности синтаксиса: #define upto ++<, #define downto -->. Если вы чувствуете зло, вы можете сделать #define for while(и #define do ) {#define done ;}) и написать for x downto 0 do printf("%d\n", x) doneо, человечество ...
Крис Лутц
98
Открывает возможность совершенно нового выразительного способа кодирования, который стоит пожертвовать несколькими предупреждениями компилятора: bool CheckNegative (int x) {return x <0? верно :-( неверно); }
ттт

Ответы:

8606

-->не оператор. Это на самом деле два отдельных оператора, --и >.

Код условного кода уменьшается x, возвращая xисходное (не уменьшенное) значение, а затем сравнивает исходное значение с 0использованием >оператора.

Чтобы лучше понять, заявление можно записать следующим образом:

while( (x--) > 0 )
Potatoswatter
источник
262
С другой стороны, в этом контексте он действительно выглядит как некий оператор диапазона.
Чарльз Сальвия
109
Сказать, что x пост-уменьшен, а затем сравнивается с 0 - то же самое, что сказать, что x уменьшается после сравнения с 0
Чарльз Сальвия,
8
Я не думаю, что это то же самое. Я думаю, что слово «затем» подразумевает, что есть порядок (после пост-убывания значение х становится на единицу меньше). Я думаю, что можно сказать «Вы отправляете убывание x, а затем сравниваете его старое значение и 0 ...», чтобы сделать его более понятным. Но это все равно придирки. Мы все знаем, что имеется в виду.
Йоханнес Шауб -
38
На Java это также компилируется :)
Steven Devijver
44
Название @Jay - плохой стиль программирования :-) Об этом свидетельствует тот факт, что вопрос был задан в первую очередь. Гораздо больше смысла в текстовом связывании операторов с тем, над чем они работают, а не с чем-то не связанным, так while (x-- > 0)что было бы более подходящим. Это также делает более очевидным, что происходит (по крайней мере, в редакторе с фиксированным шрифтом), означая, что скобки в этом ответе не будут необходимы.
paxdiablo
3132

Или для чего-то совершенно другого ... xслайды 0.

while (x --\
            \
             \
              \
               > 0)
     printf("%d ", x);

Не так математично, но ... каждая картина рисует тысячу слов ...

несинхронизированы
источник
216
@mafutrct - насколько я помню, \ in C просто добавляет следующую строку, как будто не было перевода строки. Здесь в основном ничего не делают.
Хоган
2
@mafu символ '\' сообщает компилятору, что текущая строка продолжается в следующей строке, поэтому компилятор должен объединить обе строки и скомпилировать их как одну. 'while (x -> 0)' будет работать до тех пор, пока x не станет равным -1. Но то, как он делает отступы, делает вид, что х скользит к нулю. «Пока х сползает к 0 ...»
Фелипе
16
IIRC, K & R C допускали пробел между '-s в операторе декремента, в этом случае вы могли бы иметь обратную косую черту в середине, что выглядело бы еще круче. :)
Жюль
10
@ArnavBorborah - это старое значение выражения why waste words when a picture does a better job, используемое в этом контексте как шутка. (есть на самом деле 2 ключевых слова whileи printf)
несинхронизировано
88
Ах да, неясный оператор слайдов. Как я мог забыть!
демонкорю
2378

Это очень сложный оператор, поэтому даже ISO / IEC JTC1 (Объединенный технический комитет 1) поместил свое описание в две разные части стандарта C ++.

Шутки в сторону, они представляют собой два разных оператора: --и >описаны , соответственно , в §5.2.6 / 2 и §5.9 на C ++ 03 стандарта.

Кирилл В. Лядвинский
источник
1278

Это эквивалентно

while (x-- > 0)

x--(после декремента) эквивалентно x = x-1так, код преобразуется в:

while(x > 0) {
    x = x-1;
    // logic
}
x--;   // The post decrement done when x <= 0
Shubham
источник
15
Это не совсем верно. Значение x внутри тела цикла отличается во втором случае. Оператор присваивания в вашем примере должен быть выше логики, чтобы он был эквивалентным. Postfix - вычитает 1, но сравнение будет происходить со значением до вычитания.
Uliwitness
4
@uliwitness Они действительно эквивалентны. Было бы неправильно, если бы использовался префикс: 0 >-- xв этом случае xуменьшается до логики. В постфиксе логика выполняется перед уменьшением, и, таким образом, оба образца эквивалентны. Не стесняйтесь записывать их в Consoleи тестировать их.
Candleshark
12
Они все еще не эквивалентны. После первого цикла x равен -1 (или переполняется в случае, если он не подписан), после второго он равен 0. (Если исходить из того, что x начинается неотрицательно, ни один цикл не изменяет x или не прерывается или ...)
Цезарь
1
while(x=x-1,x+1 > 0)эквивалентно.
SS Anne
2
@Shebham, вот контрольный пример: если x начинается с 0, в исходном цикле он будет выходить как -1, а с вашей версией он останется равным нулю. Так что они не эквивалентны.
Эллиот
1210

x может пойти к нулю еще быстрее в обратном направлении:

int x = 10;

while( 0 <---- x )
{
   printf("%d ", x);
}

8 6 4 2

Вы можете контролировать скорость стрелкой!

int x = 100;

while( 0 <-------------------- x )
{
   printf("%d ", x);
}

90 80 70 60 50 40 30 20 10

;)

док
источник
6
какая операционная система, этот тип вывода генерируется, я использую Ubuntu 12.04 в том, что у меня было сообщение об ошибке
Bhuvanesh
74
Хотя это должно быть очевидно, для всех новичков в C ++ читающих это: не делайте этого. Просто используйте расширенное назначение, если вам нужно увеличить / уменьшить более чем на единицу.
Blimeo
263
Ноль с "лазерами". while (0> - - - - - - - - - ---------- x) ... тот же вывод.
Самуэль Дэниелсон
4
@phord ты уверен, что он не компилируется? -> coliru.stacked-crooked.com/a/5aa89a65e3a86c98
документ
18
@doc. Компилируется в c ++, но не в c.
Phord
549

Это

#include <stdio.h>
int main(void){
     int x = 10;

     while( x-- > 0 ){ // x goes to 0

       printf("%d ", x);
     }

     return 0;
}

Просто пространство заставляет вещи выглядеть смешно, --уменьшать и >сравнивать.

RageZ
источник
432

Использование -->имеет историческую значимость. Уменьшение было (и остается в некоторых случаях) быстрее, чем увеличение в архитектуре x86. Использование -->предполагает, что xсобирается 0, и обращается к тем, у кого есть математическое образование.

Мэтт Джойнер
источник
479
Не совсем верно. Уменьшение и увеличение занимают одинаковое количество времени, преимущество в том, что сравнение с нулем происходит очень быстро по сравнению со сравнением с переменной. Это верно для многих архитектур, а не только для x86. Что-нибудь с инструкцией JZ (прыгать, если ноль). В поисках вы можете найти множество циклов for, которые записаны в обратном направлении, чтобы сохранить циклы при сравнении. Это особенно быстро на x86, поскольку при уменьшении переменной устанавливается соответствующий флаг нуля, поэтому вы можете переходить без необходимости явно сравнивать переменную.
Burito
25
Ну, уменьшение до нуля означает, что вам нужно сравнивать только с 0 на каждую итерацию цикла, в то время как итерация к n означает сравнение с n на каждой итерации. Первый способ проще (и на некоторых архитектурах он автоматически тестируется после каждой операции с регистром данных).
Джои Адамс
9
@burrito Хотя я не согласен, циклы, обусловленные ненулевыми значениями, обычно предсказываются почти идеально.
Дункан
14
Увеличение и уменьшение одинаково быстро, вероятно, на всех платформах (определенно на x86). Разница заключается в тестировании условия окончания цикла. Чтобы увидеть, достиг ли счетчик нуля, он практически свободен - когда вы уменьшаете значение, в процессоре устанавливается нулевой флаг, и для определения конечного условия вам просто нужно проверить этот флаг, тогда как при увеличении требуется операция сравнения перед конечным условием. может быть обнаружен.
Lego
7
Конечно, все это спорно в эти дни, так как современные компиляторы могут векторизации и обратные петли автоматически.
Лямбда Фея
367
while( x-- > 0 )

Вот как это проанализировано.

Grumdrig
источник
363

Совершенно отвратительный, но я буду использовать это:

#define as ;while

int main(int argc, char* argv[])
{
    int n = atoi(argv[1]);
    do printf("n is %d\n", n) as ( n --> 0);
    return 0;
}
Arrieta
источник
17
@SAFX - это были бы совершенно иероглифы с египетскими скобками
mouviciel
1
Это не компилируется. C не Паскаль, где внутренняя часть do ... whileявляется списком операторов. В С это блок, так и должно быть do { ... } while.
Маркиз Лорн
25
@EJP это компилируется. Синтаксис есть do statement while ( expression ) ;. Сказав это, я надеюсь, что это поняли, я имел в виду пример как шутку.
Эскуало
321

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

В этом случае выражение:

x-->0

Разборы по самым большим токенам:

token 1: x
token 2: --
token 3: >
token 4: 0
conclude: x-- > 0

То же правило применяется к этому выражению:

a-----b

После разбора:

token 1: a
token 2: --
token 3: --
token 4: -
token 5: b
conclude: (a--)-- - b

Я надеюсь, что это помогает понять сложное выражение ^^

NguyenDat
источник
98
Ваше второе объяснение не правильно. Компилятор увидит a-----bи подумает (a--)-- - b, что не компилируется, потому a--что не возвращает lvalue.
Тим Лиф
22
Дополнительно xи --есть два отдельных токена.
Роланд Иллиг
23
@DoctorT: он проходит лексер. Только семантический проход способен испустить эту ошибку. поэтому его объяснение верно.
v.oddou
9
Пока вы думаете, что -->это оператор (что подразумевается под вопросом, который был задан), этот ответ вообще не полезен - вы будете думать, что маркер 2 - -->не просто --. Если вы знаете, что -->это не оператор, у вас, вероятно, нет проблем с пониманием кода в вопросе, поэтому, если у вас нет совершенно другого вопроса, я не совсем уверен, как это может быть полезно.
Бернхард Баркер
4
Пример @DoctorT может быть правильным, если предположить, что aперегружен оператор пост-декремента, который возвращает lvalue. coliru.stacked-crooked.com/a/e1effc351ae79e9f
док
275

Это точно так же, как

while (x--)
{
   printf("%d ", x);
}

для неотрицательных чисел

хороший человек
источник
153
Не должно ли это быть for(--x++;--x;++x--)?
Матин Улхак
9
@DoctorT это то, что unsignedдля
Коул Джонсон
12
@MateenUlhaq, это неправильно в соответствии со стандартом, выражение --x++имеет неопределенное поведение в соответствии с §1.9.15
WorldSEnder
Если бы он использовал unsigned, он бы использовал%u
Cacahuete Frito
242

В любом случае, у нас теперь есть оператор «идет». "-->"его легко запомнить как направление, и «пока х уходит в ноль» означает прямое значение.

Кроме того, это немного более эффективно, чем "for (x = 10; x > 0; x --)"на некоторых платформах.

тест
источник
17
Идет не может быть правдой всегда, особенно когда значение х является отрицательным.
Ганеш Гопаласубраманян
15
Другая версия не делает то же самое - for (size_t x=10; x-->0; )тело цикла выполняется с 9,8, .., 0, тогда как другая версия имеет 10,9, .., 1. В противном случае выйти из цикла с нуля с помощью переменной без знака довольно сложно.
Пит Киркхам
4
Я думаю, что это немного вводит в заблуждение ... У нас нет буквального оператора "идет к", так как нам нужен еще один, ++>чтобы сделать дополнительную работу.
Цлмы
19
@Josh: на самом деле, переполнение дает неопределенное поведение для int, так что он может так же легко съесть вашу собаку, как xи обнулить, если она начинается отрицательно.
SamB
3
Это очень важная для меня идиома по причине, указанной в comnmet @PeteKirkham, так как мне часто приходится делать убывающие циклы над беззнаковыми величинами вплоть до 0. (Для сравнения, идиома пропуска тестов на ноль, таких как запись while (n--)вместо «без знака» n, ничего не покупает, и для меня сильно затрудняет читаемость.) Он также обладает приятным свойством указывать на один больше, чем начальный индекс, который обычно Вы хотите (например, для цикла над массивом вы указываете его размер). Мне также нравится -->без места, так как это позволяет легко распознать идиому.
Марк ван Леувен
221

Этот код сначала сравнивает x и 0, а затем уменьшает x. (Также сказано в первом ответе: вы после x уменьшаете, а затем сравниваете x и 0 с >оператором.) Смотрите вывод этого кода:

9 8 7 6 5 4 3 2 1 0

Теперь мы сначала сравним, а затем уменьшим, увидев 0 в выводе.

Если мы хотим сначала уменьшить, а затем сравнить, используйте этот код:

#include <stdio.h>
int main(void)
{
    int x = 10;

    while( --x> 0 ) // x goes to 0
    {
        printf("%d ", x);
    }
    return 0;
}

Этот вывод:

9 8 7 6 5 4 3 2 1
SjB
источник
177

Мой компилятор распечатает 9876543210 при запуске этого кода.

#include <iostream>
int main()
{
    int x = 10;

    while( x --> 0 ) // x goes to 0
    {
        std::cout << x;
    }
}

Как и ожидалось. На while( x-- > 0 )самом деле означает while( x > 0). x--Пост декрементирует x.

while( x > 0 ) 
{
    x--;
    std::cout << x;
}

это другой способ написать то же самое.

Приятно, что оригинал выглядит как «пока х уходит в 0».

cool_me5000
источник
4
Результат не определен, только если вы увеличиваете / уменьшаете одну и ту же переменную более одного раза в одном выражении. Это не относится к этой ситуации.
Тим Лиф
13
while( x-- > 0 ) actually means while( x > 0)- Я не уверен, что вы пытались сказать там, но то, как вы это сформулировали, подразумевает, что не --имеет никакого значения, что, очевидно, очень неправильно.
Бернхард Баркер
Для того, чтобы управлять точки дома из @Dukeling, этот ответ не то же самое , что и оригинальный пост. В оригинальном посте xбудет -1после того, как выйдет из цикла, а в этом ответе xбудет 0.
Марк Лаката
148

Между --и >. Отсутствует пробел . xявляется пост уменьшенным, то есть уменьшенным после проверки условия x>0 ?.

Mr. X
источник
42
Пробел не пропущен - C (++) игнорирует пробел.
27
@ H2CO3 Это не совсем так. Есть места, где пробел должен использоваться для разделения токенов, например, в #define foo()сравнении с #define foo ().
Дженс
30
@Jens Как насчет: «Пробел не пропущен - C (++) игнорирует ненужные пробелы.»?
Кевин П. Райс
139

--является оператором декремента и >является оператором больше, чем .

Два оператора применяются как один, как -->.

muntoo
источник
11
Они применяются как 2 отдельных оператора. Они только написаны обманчиво выглядеть как «один один».
underscore_d
129

Это комбинация двух операторов. Первый --- для уменьшения значения и >для проверки, больше ли значение, чем правый операнд.

#include<stdio.h>

int main()
{
    int x = 10;

    while (x-- > 0)
        printf("%d ",x);

    return 0;
}

Выход будет:

9 8 7 6 5 4 3 2 1 0            
Раджив Дас
источник
122

На самом деле, xпост-декремент и с этим условием проверяется. Это не так -->, это(x--) > 0

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

-->    x-->0
++>    x++>0
-->=   x-->=0
++>=   x++>=0
AndroidLearner
источник
6
За исключением того, что ++> вряд ли можно использовать в то время (). Оператор "идет до ..." будет ++ <, который выглядит не так хорошо. Оператор -> это счастливое совпадение.
Флориан F
2
@BenLeggiero Это может «сработать» в смысле генерации кода, который что-то делает (в то же время приводя в бешенство читателей, которым не нравится искусственный код), но семантика отличается, так как использование предопределенного кода означает, что он будет выполнять на одну итерацию меньше. В качестве надуманного примера, он никогда не будет выполнять тело цикла, если xначнется с 1, но while ( (x--) > 0 )будет. {edit} Эрик Липперт освещал оба в своих заметках о выпуске C # 4: blogs.msdn.microsoft.com/ericlippert/2010/04/01/…
underscore_d
120

C и C ++ подчиняются правилу "максимальное жевание". Точно так же, как a --- b переводится (a--) - b, в вашем случае x-->0переводится как (x--)>0.

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

Peter Mortensen
источник
5
Именно это и предполагал ФП: «((a) ->)» был максимальным глотком. Оказывается, исходное предположение ОП было неверным: «->» не является максимально допустимым оператором.
Дэвид
4
Также известен как жадный разбор, если я правильно помню.
Рой Тинкер
1
@RoyTinker Жадное сканирование. Парсер не имеет к этому никакого отношения.
Маркиз Лорн
27

Почему все осложнения?

Простой ответ на оригинальный вопрос просто:

#include <stdio.h>
int main()
{
    int x = 10;
    while (x > 0) 
    {
        printf("%d ", x);
        x = x-1;
    }
}

Делает то же самое. Я не говорю, что вы должны делать это так, но он делает то же самое и ответил бы на вопрос в одном посте.

Это x--просто сокращение для вышеупомянутого, и >просто нормальное значение больше, чем operator. Нет большой загадки!

Сейчас слишком много людей делают простые вещи сложными;)

Garry_G
источник
17
Этот вопрос не о сложностях, а о ** скрытых особенностях и темных углах C ++ / STL **
пикс
20
Программа здесь дает другой вывод, чем оригинальный, потому что x здесь уменьшается после printf. Это хорошо демонстрирует, как «простые ответы» обычно неверны.
Öö Tiib
2
The OP's way: 9 8 7 6 5 4 3 2 1 0иThe Garry_G way: 10 9 8 7 6 5 4 3 2 1
Энтони
2
Это не делает то же самое. Переместите ваш x=x-1раньше, printfтогда вы можете сказать, что он делает то же самое.
CITBL
26

Традиционным образом мы определяем условие в whileскобках цикла ()и условие завершения внутри фигурных скобок {}, но -->определяем оба сразу.

Например:

int abc(void)
{
    int a = 5
    while((a--) > 0) // Decrement and comparison both at once
    {
        // Code
    }
}

Это уменьшает aи запускает цикл, пока aбольше, чем 0.

Условно это будет выглядеть так:

int abc(void)
{
    int a = 5;
    while(a > 0)
    {
        a--;
        // Code
    }
    a--;
}

В обоих направлениях мы делаем одно и то же и достигаем одинаковых целей.

Zohaib Ejaz
источник
5
Это неверно Код в вопросе: «test-write-execute» (сначала протестируйте, запишите новое значение, выполните цикл), ваш пример - «test-execute-write».
v010дя
@ v010dya Исправлен ответ, теперь он test-write-executeкак в вопросе, спасибо за указание!
Котаускас
@VladislavToncharov Ваша правка все еще была неправильной. Смотри мой.
SS Anne
8

(x --> 0) средства (x-- > 0)

  1. ты можешь использовать (x -->)
    output -: 9 8 7 6 5 4 3 2 1 0

  2. Вы можете использовать (-- x > 0) Это значит(--x > 0)
    output -: 9 8 7 6 5 4 3 2 1

  3. ты можешь использовать
(--\
    \
     x > 0)

output -: 9 8 7 6 5 4 3 2 1

  1. ты можешь использовать
(\
  \
   x --> 0)

output -: 9 8 7 6 5 4 3 2 1 0

  1. ты можешь использовать
(\
  \
   x --> 0
          \
           \
            )

output -: 9 8 7 6 5 4 3 2 1 0

  1. Вы также можете использовать
(
 x 
  --> 
      0
       )

output -: 9 8 7 6 5 4 3 2 1 0

Кроме того, вы можете попробовать много методов, чтобы успешно выполнить эту команду

Kalana
источник