Що таке оператор "->" в C ++?


8924

Після прочитання прихованих об'єктів і темні кути 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. Звідки це визначено в стандарті і звідки воно взялося?


503
Або навіть просто правильний інтервал ... Я не думаю, що я ніколи не бачив проміжку між змінною та або, ++або --раніше ...
Меттью Шарлі

1154
Цей оператор "переходить до" можна повернути (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? правда :-( помилково); }
ttt

Відповіді:


8599

-->не є оператором. Насправді це два окремі оператори, --і >.

Зменшення коду умовного x, повертаючи xпочаткове (не зменшене) значення, а потім порівнює вихідне значення з 0використанням >оператора.

Для кращого розуміння заяву можна написати так:

while( (x--) > 0 )

262
Потім знову виглядає як якийсь оператор діапазону в цьому контексті.
Чарльз Сальвія

109
Скажімо, що x є посткрементованим, а потім порівнюється з 0, це те саме, що сказати x зменшується після порівняння з 0
Чарльз Сальвія

8
Я не думаю, що це те саме. Я думаю, що слово "тоді" означає, що є порядок (після постдекрементації значення x на одну меншу). Я думаю, що можна сказати: "Ви розміщуєте декрементування x, а потім порівнюєте його старе значення і 0 ...", щоб зробити його зрозумілішим. Але це так чи інакше. Ми всі знаємо, що мається на увазі.
Йоханнес Шауб - запалений

38
На Java він також компілює :)
Стівен Девівер

44
Назва для нього, @Jay, поганий стиль програмування :-) Про це свідчить той факт, що питання було задано в першу чергу. Це має набагато більше сенсу текстуально прив’язувати операторів до речі, над якою вони працюють, а не чогось незв'язаного, тому while (x-- > 0)було б більш доречно. Це також робить більш очевидним, що відбувається (принаймні, в редакторі фіксованих шрифтів), тобто дужки в цій відповіді були б не потрібні.
paxdiablo

3130

Або для чогось зовсім іншого ... xслайдів до 0.

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

Не настільки математичний, але ... кожна картина малює тисячу слів ...


216
@mafutrct - Як я його пам’ятаю \ в C просто додає наступний рядок так, ніби не було розриву рядка. Тут в основному нічого не роблять.
Хоган

2
@mafu символ '\' повідомляє компілятору, що поточний рядок продовжується в наступному рядку, і тому компілятор повинен об'єднати обидва рядки та компілювати як один. "while (x -> 0)" буде працювати, поки x не дорівнює -1. Але те, як він робить відступи, виглядає так, що х ковзає до нуля. 'Поки х ковзає до 0 ...'
Феліпе

16
IIRC, K&R C дозволив пробіл між '' в операторі декременту, і в цьому випадку у вас може бути зворотний нахил в середині, який виглядатиме ще крутіше. :)
Жуль

10
@ArnavBorborah - це давнє значення виразу why waste words when a picture does a better job, яке використовується як жарт у цьому контексті. (насправді є 2 ключові слова whileта printf)
несинхронізовано

87
Ага так, незрозумілий оператор слайдів. Як я міг забути!
демонькорю


1277

Це рівнозначно

while (x-- > 0)

x--(пост декремент) еквівалентно x = x-1так, код перетворюється на:

while(x > 0) {
    x = x-1;
    // logic
}
x--;   // The post decrement done when x <= 0

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)рівнозначно.
СС Енн

2
@Shebham, Ось зустрічний приклад: якщо x починається з 0, під початковим циклом він вийде як -1, а у вашій версії він залишиться нульовим. Тому вони не рівноцінні.
Елліотт

1208

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 ++, не слід робити цього. Просто використовуйте розширене завдання, якщо вам потрібно збільшити / зменшити більш ніж на один.
Блімео

263
Нуль з "лазерами". в той час як (0> - - - - - - - - - ---------- x) ... той же вихід.
Самуель Даніельсон

4
@phord Ви впевнені, що він не компілюється? -> coliru.stacked-crooked.com/a/5aa89a65e3a86c98
doc

18
@doc Він компілюється в c ++, але не в c.
фрад

548

Його

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

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

       printf("%d ", x);
     }

     return 0;
}

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


431

Використання -->має історичну актуальність. Зменшення було (і все ще є в деяких випадках) швидшим, ніж збільшення в архітектурі x86. Використання -->припускає, що xце відбувається 0, і звертається до тих, хто має математичний досвід.


479
Не зовсім правда. Зменшення та збільшення приросту займають однакову кількість часу, перевага цього полягає в тому, що порівняння до нуля дуже швидко порівняно з порівнянням порівняно зі змінною. Це справедливо для багатьох архітектур, а не лише для x86. Все, що має інструкцію JZ (стрибнути, якщо нуль). Оглянувши навколо, ви можете знайти багато циклів "для", записаних назад, щоб зберегти цикли на порівнянні. Це особливо швидко на x86, оскільки акт декрементування змінної встановив нульовий прапор належним чином, тож ви могли потім розгалужуватись без явного порівняння змінної.
буріто

25
Ну, декрементування до нуля означає лише порівняння з 0 за цикл ітерації, тоді як ітерація до n означає порівняння з n кожною ітерацією. Попередній, як правило, простіше (а в деяких архітектурах автоматично перевіряється після кожної операції реєстрації даних).
Joey Adams

9
@burrito Хоча я не погоджуюсь, цикли, обумовлені ненульовими значеннями, зазвичай прогнозуються майже ідеально.
Дункан

14
Збільшення та зменшення однаково швидко, напевно, на всіх платформах (безумовно, на x86). Різниця полягає в тестуванні стану кінця циклу. Щоб побачити, чи лічильник досяг нуля, практично вільний - коли ви зменшуєте значення, в процесорі встановлюється нульовий прапор, і щоб виявити кінцеву умову, вам просто потрібно перевірити цей прапор, тоді як при збільшенні необхідна операція порівняння до завершення умови можна виявити.
лего

7
Звичайно, все це суперечить цим дням, оскільки сучасні компілятори можуть векторизувати та обертати петлі автоматично.
Фея лямбда


362

Матеріально, але я буду користуватися цим:

#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;
}

17
@SAFX - Це було б ідеально ієрогліфи з єгипетськими дужками
mouviciel

1
Це не компілюється. C - це не Паскаль, де інтер'єр do ... while- це список тверджень. В C це блок, так і повинно бути do { ... } while.
user207421

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

Я сподіваюся, що це допомагає зрозуміти складний вираз ^^


98
Ваше друге пояснення невірне. Компілятор побачить a-----bі подумає (a--)-- - b, що не компілюється, оскільки a--не повертає значення.
Тім Ліф

22
Додатково є xі --два окремі жетони.
Roland Illig

23
@DoctorT: він передає лексеру. тільки семантичний пропуск здатний випустити цю помилку. тому його пояснення правильне.
v.oddou

9
Поки ви вважаєте -->, що це оператор (що має на увазі виникнення питання, яке було задано), ця відповідь зовсім не корисна - ви вважаєте, що маркер 2 є -->не просто --. Якщо ви знаєте, що -->це не оператор, у вас, ймовірно, немає проблем із розумінням коду у питанні, тому, якщо у вас зовсім інше питання, я не дуже впевнений, чим це може бути корисним.
Бернхард Баркер

4
Приклад @DoctorT може бути правильним, якщо припустити, що aперевантажив оператор після декременту, який повертає значення. coliru.stacked-crooked.com/a/e1effc351ae79e9f
doc

275

Це точно так само, як

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

для негативних чисел


153
Чи не повинно бути цього for(--x++;--x;++x--)?
Mateen Ulhaq

9
@DoctorT це те, що unsignedдля
Cole Johnson

12
@MateenUlhaq, це неправильно згідно стандарту, вираз --x++має невизначену поведінку відповідно до §1.9.15
WorldSEnder

Якби він використовував unsigned, він би використав%u
Cacahuete Frito

242

У всякому разі, зараз у нас є оператор "йде". "-->"легко запам'ятовується як напрямок, і "в той час, як х іде до нуля", це сенс-прямий.

Крім того, це трохи ефективніше, ніж "for (x = 10; x > 0; x --)" на деяких платформах.


17
Не може бути правдивим завжди, особливо коли значення x від'ємне.
Ганеш Гопаласубраманіан

15
Інша версія не робить те ж саме - з for (size_t x=10; x-->0; )тілом циклу виконується 9,8, .., 0, тоді як в іншій версії 10,9, .., 1. Досить складно вийти з циклу вниз до нуля за допомогою непідписаної змінної в іншому випадку.
Піт Кіркхем

4
Я думаю, що це трохи вводить в оману ... У нас немає оператора в прямому сенсі "йде", оскільки нам потрібен інший, ++>щоб зробити додаткову роботу.
цлми

19
@Josh: насправді переповнення дає не визначену поведінку int, тому воно може так само легко з'їсти вашу собаку, як і xдо нуля, якщо вона почне негативною.
СамБ

3
Це дуже важлива для мене ідіома з тієї причини, яку в коментарі вказав @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

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", хоча.


4
Результат не визначений лише тоді, коли ви збільшуєте / зменшуєте одну і ту ж змінну більше одного разу в одному операторі. Це не стосується цієї ситуації.
Тім Ліф

13
while( x-- > 0 ) actually means while( x > 0)- Я не впевнений, що ви намагалися сказати там, але те, як ви це висловлювали, має на увазі --не має сенсу, що, очевидно, дуже неправильно.
Бернхард Баркер

Щоб повернути точку додому від @Dukeling, ця відповідь не є такою ж, як оригінальна публікація. У початковому дописі xбуде -1після того, як він вийде з циклу, тоді як у цій відповіді xбуде 0.
Марк Лаката

148

Існує простір відсутній між --і >. xє постменшеним, тобто зменшеним після перевірки стану x>0 ?.


42
Простір не бракує - C (++) ігнорує пробіл.

27
@ H2CO3 Це взагалі не вірно. Є місця, де пробіл потрібно використовувати для розділення лексем, наприклад, #define foo()навпаки #define foo ().
Єнс

30
@Jens Як щодо: "Простір не бракує - C (++) ігнорує зайвий пробіл."?
Кевін П. Райс

139

--є декремент оператором і >є більше, ніж оператор.

Два оператори застосовуються як єдиний подібний -->.


11
Вони застосовуються як дві окремі оператори, якими вони є. Вони написані лише оманливо, щоб мати вигляд "єдиного".
підкреслюйте_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

6
За винятком того, що ++> навряд чи можна використовувати (). Оператор "піднімається на ..." буде ++ <, що ніде не виглядає так приємно. Оператор -> - це щасливий збіг обставин.
Флоріан F

2
@BenLeggiero Це може "працювати" в сенсі генерування коду, який щось робить (в той час, як озлоблює читачів, які не люблять нерозумно-розумний код), але семантика відрізняється, оскільки використання попереднього прийому означає, що вона виконає ще одну меншу кількість ітерацій. Як надуманий приклад, він ніколи не виконає тіло циклу, якщо його xзапустити на 1, але while ( (x--) > 0 )буде. {редагувати} Ерік Ліпперт висвітлював обидва у своїх нотатках до випуску C # 4: blogs.msdn.microsoft.com/ericlippert/2010/04/01/…
підкреслення_d

120

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

Те, що правило, по суті, полягає в тому, що переходячи зліва направо, вирази формуються, беручи максимум символів, які формуватимуть дійсне вираження.


5
Це те, про що припускав ОП: що "((a) ->)" був максимальним підходом. Виявляється, початкове припущення ОП було неправильним: "->" не є максимально допустимим оператором.
Девід

4
Також відомий як жадібний розбір, якщо я правильно згадую.
Рой Тінкер

1
@RoyTinker Жадібне сканування. Парсер не має до цього нічого спільного.
користувач207421

27

Чому все ускладнення?

Проста відповідь на початкове запитання:

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

Робить те саме. Не кажучи, що ви повинні це робити так, але це робить те ж саме, і ви б відповіли на питання в одному дописі.

Це x--просто скорочення для вищезазначеного, і >це просто норма, більша ніж operator. Ніякої великої таємниці!

Зараз занадто багато людей ускладнюють прості речі;)


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--;
}

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


5
Це неправильно. Код у питанні робить: 'test-write-Execute' (тестуйте спочатку, запишіть нове значення, виконайте цикл), ваш приклад - 'test-Execute-write'.
v010dya

@ v010dya Виправлено відповідь, тепер це test-write-executeяк у питанні, дякую за вказівку!
Kotauskas

@VladislavToncharov Ваша редакція все ще була помилковою. Дивіться мою.
СС Енн

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

також можна спробувати безліч методів для успішного виконання цієї команди

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.