Що робить ,
оператор в C?
Що робить ,
оператор в C?
Відповіді:
Вираз:
(expression1, expression2)
Спочатку оцінюється вираз1, потім оцінюється вираз2, а значення виразу2 повертається для всього виразу.
i
значень 5, 4, 3, 2 або 1. Це просто 0. Це практично марно, якщо вирази не мають побічних ефектів.
i = b, c;
еквівалентна (i = b), c
тому, що присвоєння =
має більший пріоритет, ніж оператор коми ,
. Оператор комами має найнижчий пріоритет із усіх.
expression1, expression2;
спочатку expression1
оцінюється, імовірно, за його побічними ефектами (наприклад, викликом функції), потім є точка послідовності, потім expression2
оцінюється і значення повертається…
Я бачив найчастіше в while
циклі:
string s;
while(read_string(s), s.len() > 5)
{
//do something
}
Вона зробить операцію, потім зробить тест на основі побічного ефекту. Інший спосіб - це зробити так:
string s;
read_string(s);
while(s.len() > 5)
{
//do something
read_string(s);
}
while (read_string(s) && s.len() > 5)
. Очевидно, що це не буде працювати, якщо read_string
не має значення повернення (або не має значущого). (Редагувати: Вибачте, не помітили, скільки років було цій публікації.)
while (1)
із break;
заявою в тілі. Намагання змусити частину коду, що випадає, під час тестування в той час, або тест на час роботи, часто є марною витратою енергії і ускладнює розуміння коду.
while(1)
і break
;
Оператор комами оцінить лівий операнд, відкине результат, а потім оцінить правий операнд, і це буде результат. Идиоматично використання , як зазначено на засланні, коли ініціалізації змінних , які використовуються в for
циклі, і це дає наступний приклад:
void rev(char *s, size_t len)
{
char *first;
for ( first = s, s += len - 1; s >= first; --s)
/*^^^^^^^^^^^^^^^^^^^^^^^*/
putchar(*s);
}
В іншому випадку існує не так багато великого використання цього оператора коми , хоча легко зловжити для генерації коди , який важко читати і підтримувати.
Від проекту стандарту С99 граматика така:
expression:
assignment-expression
expression , assignment-expression
а в пункті 2 сказано:
Лівий операнд оператора коми оцінюється як вираз порожнечі; після її оцінки є пункт послідовності. Потім оцінюється правий операнд; результат має свій тип та значення. 97) Якщо робиться спроба змінити результат оператора комами або отримати доступ до нього після наступної точки послідовності, поведінка не визначена.
Зноска 97 говорить:
Оператор з комами не дає значення .
а це означає, що ви не можете призначити результат оператора комами .
Важливо зауважити, що оператор з комами має найнижчий пріоритет, і тому є випадки, коли використання ()
може призвести до значних змін, наприклад:
#include <stdio.h>
int main()
{
int x, y ;
x = 1, 2 ;
y = (3,4) ;
printf( "%d %d\n", x, y ) ;
}
матиме такий вихід:
1 4
Оператор з комами поєднує два вирази будь-якої сторони його в одне, оцінюючи їх обидва в порядку зліва направо. Значення правої частини повертається як значення цілого виразу.
(expr1, expr2)
це як, { expr1; expr2; }
але ви можете використовувати результат expr2
у виклику функції або призначенні функції.
Це часто бачимо в for
циклах для ініціалізації або підтримки декількох змінних на зразок цієї:
for (low = 0, high = MAXSIZE; low < high; low = newlow, high = newhigh)
{
/* do something with low and high and put new values
in newlow and newhigh */
}
Окрім цього, я використовував це лише "в гніві" в іншому випадку, коли завершував дві операції, які завжди повинні поєднуватися в макросі. У нас був код, який скопіював різні двійкові значення в байт-буфер для надсилання в мережу, і покажчик підтримується там, де ми потрапили:
unsigned char outbuff[BUFFSIZE];
unsigned char *ptr = outbuff;
*ptr++ = first_byte_value;
*ptr++ = second_byte_value;
send_buff(outbuff, (int)(ptr - outbuff));
Де були значення short
s або int
s, ми це зробили:
*((short *)ptr)++ = short_value;
*((int *)ptr)++ = int_value;
Пізніше ми прочитали, що це насправді не є дійсним C, оскільки (short *)ptr
воно більше не є l-значенням і не може бути збільшене, хоча наш компілятор на той час не заперечував. Щоб виправити це, ми розділили вираз на два:
*(short *)ptr = short_value;
ptr += sizeof(short);
Однак такий підхід покладався на всіх розробників, які пам’ятають постійно ставити обидва твердження. Ми хотіли функцію, де можна було передати у вихідний вказівник, значення та тип значення. Це C, а не C ++ із шаблонами, ми не могли мати функцію приймати довільний тип, тож ми зупинилися на макросі:
#define ASSIGN_INCR(p, val, type) ((*((type) *)(p) = (val)), (p) += sizeof(type))
Використовуючи оператор комами, ми змогли використовувати це у виразах або як твердження, як ми хотіли:
if (need_to_output_short)
ASSIGN_INCR(ptr, short_value, short);
latest_pos = ASSIGN_INCR(ptr, int_value, int);
send_buff(outbuff, (int)(ASSIGN_INCR(ptr, last_value, int) - outbuff));
Я не припускаю, що жоден із цих прикладів є гарним стилем! Дійсно, я, мабуть, пам’ятаю Кодекс Стіва МакКоннелла Повна порада щодо навіть використання операторів коми в for
циклі: для читабельності та ремонтопридатності цикл повинен контролюватися лише однією змінною, а вирази в самій for
рядку повинні містити лише код керування циклом, не інші зайві біти ініціалізації чи обслуговування циклу.
Він викликає оцінку декількох висловлювань, але використовує лише останнє як отримане значення (rvalue, я думаю).
Так...
int f() { return 7; }
int g() { return 8; }
int x = (printf("assigning x"), f(), g() );
має призвести до того, що x буде встановлено на 8.
Єдине місце, що я бачив, як це корисно, - це коли ви пишете прикольний цикл, де ви хочете зробити кілька речей в одному з виразів (можливо, вираз init або циклічний вираз. Щось на кшталт:
bool arraysAreMirrored(int a1[], int a2[], size_t size)
{
size_t i1, i2;
for(i1 = 0, i2 = size - 1; i1 < size; i1++, i2--)
{
if(a1[i1] != a2[i2])
{
return false;
}
}
return true;
}
Вибачте мене, якщо є якісь синтаксичні помилки або якщо я змішав що-небудь, що не є суворим С. Я не стверджую, що оператор - це хороша форма, але для цього ви можете його використовувати. У вищенаведеному випадку я б, мабуть, використовував while
цикл замість цього, так що декілька виразів на init та циклі будуть більш очевидними. (І я би ініціалізував i1 та i2 inline, а не декларував, а потім ініціалізував .... bla bla bla.)
Я відроджую це просто для вирішення питань від @Rajesh та @JeffMercado, які, на мою думку, є дуже важливими, оскільки це один із найкращих хітів пошукової системи.
Візьмемо для прикладу наступний фрагмент коду
int i = (5,4,3,2,1);
int j;
j = 5,4,3,2,1;
printf("%d %d\n", i , j);
Він надрукує
1 5
Справа i
розглядається так, як пояснюється більшістю відповідей. Усі вирази оцінюються в порядку зліва направо, але присвоюється лише останній i
. Результат (
виразу ) is
1`.
j
Випадок слід різним правилам пріоритету , оскільки ,
має найнижчий пріоритет оператора. З - за цих правил, компілятор бачить присвоювання-вираз, константа, постійна ... . Вирази знову оцінюються в порядку зліва направо, і їх побічні ефекти залишаються видимими, тому j
є 5
результатом j = 5
.
Між іншим, int j = 5,4,3,2,1;
заборонено мовними специфікаціями. Ініціалізатор очікує присвоєння самовираження тому прямий ,
оператор не допускаються.
Сподіваюся, це допомагає.