Spec-Zone.ru › C

Операторы присваивания

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

Оператор Название оператора Пример Описание Эквивалент
= основное присваивание a = b a становится равным b Н/Д
+= присваивание с сложением a += b a становится равным сумме a и b a = a + b
-= присваивание с вычитанием a -= b a становится равным результату вычитания b из a a = a - b
*= присваивание с умножением a *= b a становится равным произведению a и b a = a * b
/= присваивание с делением a /= b a становится равным результату деления a на b a = a / b
%= присваивание по модулю a %= b a становится равным остатку от деления a на b a = a % b
&= побитовое присваивание И a &= b a становится равным результату побитового И a и b a = a & b
|= побитовое присваивание ИЛИ a |= b a становится равным результату побитового ИЛИ a и b a = a | b
^= побитовое присваивание XOR a ^= b a становится равным результату побитового XOR a и b a = a ^ b
<<= побитовое присваивание сдвиг влево a <<= b a становится равным результату сдвига a влево на b a = a << b
>>= побитовое присваивание сдвиг вправо a >>= b a становится равным результату сдвига a вправо на b a = a >> b

Простое присваивание

Выражения с оператором простого присваивания имеют вид

lhs = rhs

где

lhs - выражение типа изменяемая lvalue любого полного типа объекта
rhs - выражение любого типа, неявным образом приводимого к lhs или совместимого с lhs

Присваивание выполняет неявное преобразование значения rhs к типу lhs, а затем заменяет значение в объекте, обозначенном lhs, преобразованным значением rhs.

Присваивание также возвращает то же значение, что и хранилось в lhs (чтобы были возможны выражения, такие как a = b = c). Категория значений оператора присваивания - не-lvalue (так что выражения, такие как (a=b)=c недействительны).

rhs и lhs должны удовлетворять одному из следующих условий:

  • оба lhs и rhs имеют совместимый тип структуры или объединения, или..
  • rhs должен быть неявным образом преобразуемым к lhs, что подразумевает
    • оба lhs и rhs имеют арифметические типы, в этом случае lhs может быть volatile-квалифицированным или атомным(с C11)
    • оба lhs и rhs имеют указатель на совместимые (без учета квалификаторов) типы, или один из указателей является указателем на void, и преобразование не добавит квалификаторов к типу, на который указывает указатель. lhs может быть volatile или restrict(с C99)-квалифицированным или атомным(с C11).
    • lhs является (возможно, квалифицированным или атомным(с C11)) указателем, а rhs является константой нулевого указателя, например NULL или значением nullptr_t(с C23)
  • lhs имеет тип (возможно, квалифицированный или атомный(с C11)) _Bool и rhs является указателем или значением nullptr_t(с C23)
(с C99)
  • lhs имеет тип (возможно, квалифицированный или атомный) nullptr_t и rhs имеет тип nullptr_t
(с C23)

Примечания

Если rhs и lhs перекрываются в памяти (например, являются членами одной и той же объединения), поведение не определено, если перекрытие не является точным и типы не совместимы.

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

Побочный эффект обновления lhs определён после вычисления значений, но не побочных эффектов lhs и rhs самих по себе, и вычисления операндов, как обычно, не упорядочены относительно друг друга (поэтому выражения, такие как i=++i; неопределены).

Присваивание удаляет дополнительные диапазон и точность из выражений с плавающей запятой (см. FLT_EVAL_METHOD).

В C++, операторы присваивания являются выражениями lvalue, а не в C.

#include <stdio.h>
 
int main(void)
{
    // integers
    int i = 1, j = 2, k = 3; // initialization, not assignment
 
    i = j = k;   // values of i and j are now 3
//  (i = j) = k; // Error: lvalue required
    printf("%d %d %d\n", i, j, k);
 
    // pointers
    const char c = 'A'; // initialization; not assignment
    const char *p = &c;  // initialization; not assignment
    const char **cpp = &p; // initialization; not assignment
 
//  cpp = &p;   // Error: char** is not convertible to const char**
    *cpp = &c;  // OK, char* is convertible to const char*
    printf("%c \n", **cpp);
    cpp = 0;    // OK, null pointer constant is convertible to any pointer
 
    // arrays
    int arr1[2] = {1,2}, arr2[2] = {3, 4};
//  arr1 = arr2; // Error: cannot assign to an array
    printf("arr1[0]=%d arr1[1]=%d arr2[0]=%d arr2[1]=%d\n",
            arr1[0],   arr1[1],   arr2[0],   arr2[1]);
 
    struct { int arr[2]; } sam1 = { {5, 6} }, sam2 = { {7, 8} };
    sam1 = sam2; // OK: can assign arrays wrapped in structs
 
    printf("%d %d \n", sam1.arr[0], sam1.arr[1]);
}

Вывод:

3 3 3
A
arr1[0]=1 arr1[1]=2 arr2[0]=3 arr2[1]=4
7 8

Составное присваивание

Выражения с операторами составного присваивания имеют вид

lhs op rhs

где

op - один из *=, /= %=, += -=, <<=, >>=, &=, ^=, |=
lhs, rhs - выражения с арифметическими типами (где lhs может быть квалифицированным или атомным), за исключением случаев, когда op является += или -=, которые также принимают типы указателей с теми же ограничениями, что и + и -

Выражение lhs @= rhs точно такое же, как lhs = lhs @ ( rhs ), за исключением того, что lhs оценивается только один раз.

Если lhs имеет атомный тип, операция ведет себя как единая атомная операция чтения-модификации-записи с порядком памяти memory_order_seq_cst.

Для целочисленных атомных типов составное присваивание @= эквивалентно:

T1* addr = &lhs;
T2 val = rhs;
T1 old = *addr;
T1 new;
do { new = old @ val } while (!atomic_compare_exchange_strong(addr, &old, new);
(с C11)
#include <stdio.h>
 
int main(void)
{
    int x = 10; 
    int hundred = 100; 
    int ten = 10; 
    int fifty = 50; 
 
    printf("%d %d %d %d\n", x, hundred, ten, fifty);
 
    hundred *= x; 
    ten     /= x; 
    fifty   %= x; 
 
    printf("%d %d %d %d\n", x, hundred, ten, fifty);
 
    return 0;
}

Вывод:

10 100 10 50
10 1000 1 0

Ссылки

  • Стандарт C17 (ISO/IEC 9899:2018):
    • 6.5.16 Операторы присваивания (с. 72-73)
  • Стандарт C11 (ISO/IEC 9899:2011):
    • 6.5.16 Операторы присваивания (с. 101-104)
  • Стандарт C99 (ISO/IEC 9899:1999):
    • 6.5.16 Операторы присваивания (с. 91-93)
  • Стандарт C89/C90 (ISO/IEC 9899:1990):
    • 3.3.16 Операторы присваивания

См. также

Порядок вычисления операторов

Общие операторы
присваивания инкремента
декремента
арифметические логические сравнения доступа к членам
объекта
прочие

a = b a += b a -= b a *= b a /= b a %= b a &= b a |= b a ^= b a <<= b a >>= b

++a --a a++ a--

+a -a a + b a - b a * b a / b a % b ~a a & b a | b a ^ b a << b a >> b

!a a && b a || b

a == b a != b a < b a > b a <= b a >= b

a[b] *a &a a->b a.b

a(...) a, b (type) a a ? b : c sizeof

_Alignof
(с C11)

См. также

Документация C++ по Операторам присваивания

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/c/language/operator_assignment

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API