[Для запису я змінив цю відповідь досить суттєво, оскільки її прийняли і проголосували далі. Але це все ще говорить однаково.]
Цей код є глибоко, можливо навмисно, заплутаним. Він містить вузьковідхилений екземпляр жахливої невизначеної поведінки . В основному неможливо визначити, чи людина, яка побудувала це питання, була дуже, дуже розумною чи дуже-дуже дурною. І "урок", який цей код може передбачити, щоб навчити або перевірити вас про те, а саме - що оператор unary plus не робить багато - безумовно, недостатньо важливий, щоб заслужити такий вид підривної помилки.
Є два заплутані аспекти коду, дивна умова:
while(+(+k--)!=0)
і дементована заява, яку вона контролює:
k=k++;
Я спершу висвітлюю другу частину.
Якщо у вас є така змінна, kяку ви хочете збільшити на 1, C дає вам не один, не два, не три, а чотири різних способи зробити це:
k = k + 1
k += 1
++k
k++
Незважаючи на цю щедрість (чи, можливо, через це), деякі програмісти плутаються та кашлюють подібні контури
k = k++;
Якщо ви не можете зрозуміти, що це має робити, не хвилюйтеся: ніхто не може. Цей вираз містить дві різні спроби змінити kзначення ( k =частину та k++частину), і оскільки в C немає правила сказати, яка з спроб модифікацій "перемагає", такий вираз формально не визначений , це означає не тільки те, що він не має визначеного значення, але вся програма, що містить його, є підозрілою.
Тепер, якщо ви подивитеся дуже уважно, ви побачите, що в цій конкретній програмі рядок k = k++насправді не виконується, тому що (як ми вже побачимо) умова контролю спочатку не відповідає, тому цикл працює 0 разів . Отже, ця конкретна програма насправді не може бути визначеною - але вона все ще патологічно заплутана.
Дивіться також ці канонічні відповіді на всі запитання щодо невизначеної поведінки подібного роду.
Але ви не питали про k=k++частину. Ви запитали про першу заплутану частину, про +(+k--)!=0умову. Це виглядає дивно, тому що це дивно. Ніхто ніколи і ніколи не писав такого коду в справжній програмі. Тож немає причин вчитися розуміти це. (Так, її правда, вивчення меж системи може допомогти вам дізнатися про її точні моменти, але в моїй книзі є досить чітка грань між творчими роздумами, що провокують думки, проти грубих, образливих досліджень, і це вираження дуже чітко неправильна сторона цього рядка.)
У будь-якому випадку давайте вивчимо +(+k--)!=0. (А зробивши це, давайте забудемо все про це.) Будь-який вираз, подібний цьому, повинен бути зрозумілий зсередини. Я припускаю, ви знаєте що
k--
робить. Він приймає kпоточне значення і "повертає" його до решти виразу, і воно більш-менш одночасно зменшує k, тобто зберігає кількість k-1назад у k.
Але що тоді +робити? Це одинарний плюс, а не двійковий плюс. Це просто як одинарний мінус. Ви знаєте, що двійковий мінус робить віднімання: вираз
a - b
віднімає b від a. І ви знаєте, що одинарний мінус заперечує речі: вираз
-a
дає вам мінус a. Те, що +робить Унарі , це ... в основному нічого. +aдає вам aзначення, змінивши позитивні значення на позитивні та негативні значення на негативні. Отже вираз
+k--
дає тобі все k--, що тобі дали, тобто kстаре значення.
Але ми не закінчили, бо маємо
+(+k--)
Це просто бере все, що +k--вам подарувало, і знову застосовується +до нього неоднаково. Таким чином, він дає вам все +k--, що вам дав, що все k--, що вам дав, що було kстарим значенням.
Отже, врешті-решт, умова
while(+(+k--)!=0)
робить точно так само, як і набагато звичайніший стан
while(k-- != 0)
зробив би. (Це також робить те саме, що while(+(+(+(+k--)))!=0)було б зробити ще складнішим на вигляд умовою . І ці круглі дужки насправді не потрібні; це також робить те саме, що while(+ + + +k--!=0)було б зроблено.)
Навіть з'ясовуючи, що таке "нормальний" стан
while(k-- != 0)
робить - це щось хитро. У цьому циклі відбуваються такі дві речі: Оскільки цикл потенційно працює кілька разів, ми збираємось:
- продовжуйте робити
k--, щоб робити все kменше і менше, але також
- продовжуйте робити тіло петлі, що б це не робило.
Але ми виконуємо цю k--частину одразу, перш ніж (або в процесі) вирішити, чи робити іншу поїздку через цикл. І пам'ятайте, що k--"повертає" старе значення k, перш ніж декламентувати його. У цій програмі початкове значення kдорівнює 0. Отже k--, буде повернути старе значення 0, а потім оновити kдо -1. Але тоді решта умови != 0- але, як ми щойно побачили, ми вперше перевірили умову, отримали 0. Так що ми не будемо робити жодних поїздок через цикл, тому не будемо намагатися виконати проблемне твердження k=k++взагалі.
Іншими словами, в цьому конкретному циклі, хоча я сказав, що "відбуваються такі дві речі", виявляється, що річ 1 відбувається один раз, а річ 2 відбувається нульовий раз.
У будь-якому випадку, я сподіваюся, що зараз достатньо зрозуміло, чому цей поганий привід для програми закінчується друком -1 як кінцевим значенням k. Зазвичай я не люблю відповідати на такі питання вікторини - це схоже на обман - але в цьому випадку, оскільки я так голосно не згоден з усією сутністю вправи, я не заперечую.