대입 연산자
대입 연산자 (Assignment operators)
대입 연산자는 왼쪽 변수의 값을 오른쪽 값으로 바꾸는 이항 연산자예요. = 하나만 떠올리기 쉽지만, C에는 += 같은 복합 대입 연산자도 있어요. 또 "배열은 대입할 수 없다"는 규칙도 이 연산자에서 나와요. 어떤 것이 대입 가능하고 어떤 것이 안 되는지, 반환 값은 무엇인지 정리해 볼게요.
본문
대입 연산자와 복합 대입 연산자는, 왼쪽에 있는 변수를 오른쪽에 있는 값을 사용해 수정하는 이항 연산자예요.
| 연산자 | 연산자 이름 | 예 | 설명 | 동등 표현 |
|---|---|---|---|---|
= |
기본 대입 | a=b |
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 |
&= |
비트 AND 대입 | a&=b |
a가 a와 b의 비트 AND와 같아짐 | a=a&b |
|= |
비트 OR 대입 | a|=b |
a가 a와 b의 비트 OR과 같아짐 | a=a|b |
^= |
비트 XOR 대입 | a^=b |
a가 a와 b의 비트 XOR과 같아짐 | a=a^b |
<<= |
비트 왼쪽 시프트 대입 | a<<=b |
a가 a를 b만큼 왼쪽 시프트한 값과 같아짐 | a=a<<b |
>>= |
비트 오른쪽 시프트 대입 | a>>=b |
a가 a를 b만큼 오른쪽 시프트한 값과 같아짐 | a=a>>b |
단순 대입 (Simple assignment)
단순 대입 연산자 표현식의 형태는 다음과 같아요.
| 형태 |
|---|
lhs = rhs |
대입은 rhs의 값에서 lhs의 타입으로 암시적 변환을 수행하고, 그다음 lhs가 지정하는 객체의 값을 변환된 rhs 값으로 바꿔치기해요.
대입은 또한 lhs에 저장된 것과 같은 값을 반환해요 (그래서 a=b=c 같은 표현식이 가능해요). 대입 연산자의 값 카테고리는 비-lvalue예요 (그래서 (a=b)=c 같은 표현식은 무효예요).
rhs와 lhs는 다음 중 하나를 만족해야 해요.
-
lhs와 rhs가 모두 호환되는
struct또는union타입이거나... -
rhs가 lhs로 암시적으로 변환 가능해야 해요. 이는 다음을 함의해요.
- lhs와 rhs가 모두 산술 타입인 경우. 이때 lhs는
volatile한정 또는atomic(C11부터)일 수 있어요. - lhs와 rhs가 모두 (한정자를 무시하고) 호환되는 타입에 대한 포인터이거나, 둘 중 하나가
void에 대한 포인터인 경우. 그리고 변환이 가리켜지는 타입에 한정자를 추가하지 않는 경우. lhs는volatile또는restrict(C99부터) 한정 또는atomic(C11부터)일 수 있어요. - lhs가 (한정 또는
atomic(C11부터)일 수 있는) 포인터이고 rhs가NULL같은 널 포인터 상수 또는nullptr_t값(C23부터)인 경우 - lhs가 (한정 또는
atomic일 수 있는)_Bool타입이고 rhs가 포인터 또는nullptr_t값(C23부터)인 경우 (C99부터) - lhs가 (한정 또는
atomic일 수 있는)nullptr_t타입이고 rhs가nullptr_t타입인 경우 (C23부터)
- lhs와 rhs가 모두 산술 타입인 경우. 이때 lhs는
참고 (Notes)
rhs와 lhs가 메모리에서 겹치면(예: 같은 union의 멤버들), 겹침이 정확하고 타입이 호환되는 경우가 아니면 동작은 정의되지 않아요.
배열은 대입할 수 없지만, 구조체로 감싼 배열은 같은(또는 호환되는) struct 타입의 다른 객체에 대입할 수 있어요.
lhs를 갱신하는 부작용은 값 계산들 **다음에 시퀀싱(sequenced after)**되지만, lhs와 rhs 자체의 부작용은 아니에요. 그리고 피연산자의 평가는 평소처럼 서로에 대해 비시퀀싱(unsequenced)돼요 (그래서 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
복합 대입 (Compound assignment)
복합 대입 연산자 표현식의 형태는 다음과 같아요.
| 형태 |
|---|
| lhs op rhs |
여기서
| op | - | *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, |= 중 하나 |
|---|---|---|
| lhs, rhs | - | 산술 타입의 표현식 (lhs는 한정 또는 atomic일 수 있음). 단 +=와 -=가 연산자일 때는 +와 -와 같은 제약으로 포인터 타입도 받아들여요 |
표현식 lhs@=rhs는 lhs = lhs@(rhs)와 정확히 같아요. 단 lhs가 한 번만 평가된다는 점이 달라요.
lhs가
atomic타입이면, 그 연산은 메모리 순서memory_order_seq_cst를 가진 단일 원자 읽기-수정-쓰기(read-modify-write) 연산처럼 동작해요. 정수 원자 타입의 경우 복합 대입@=는 다음과 동등해요: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
더 알아보기
- 대입 연산자는 할당 시 부작용·시퀀싱 규칙을 다루는 평가 순서 문서와 이어져요.
- 구조체 대입과 원자 대입은 구조체·원자 연산 문서에서 더 깊게 볼 수 있어요.
- cppreference의 Assignment operators 원문에서 표준 절 번호를 더 확인할 수 있어요.