Namespaces
Variants

Arithmetic operators

From cppreference.net

산술 연산자는 피연산자에 표준 수학 연산을 적용합니다.

연산자 연산자 이름 예시 결과
+ 단항 플러스 + a 승진 후 a 의 값
- 단항 마이너스 - a a 의 부정
+ 덧셈 a + b a b 의 합
- 뺄셈 a - b a 에서 b 를 뺀 값
* 곱셈 a * b a b 의 곱
/ 나눗셈 a / b a b 로 나눈 값
% 나머지 a % b a b 로 나눈 나머지
~ 비트 NOT ~a a 의 비트 NOT
& 비트 AND a & b a b 의 비트 AND
| 비트 OR a | b a b 의 비트 OR
^ 비트 XOR a ^ b a b 의 비트 XOR
<< 비트 왼쪽 시프트 a << b a b 만큼 왼쪽 시프트
>> 비트 오른쪽 시프트 a >> b a b 만큼 오른쪽 시프트

목차

오버플로우

부호 없는 정수 연산은 항상 modulo 2 n
으로 수행되며, 여기서 n은 해당 정수의 비트 수입니다. 예를 들어 unsigned int 의 경우, UINT_MAX 에 1을 더하면 0 이 되고, 0 에서 1을 빼면 UINT_MAX 가 됩니다.

부호 있는 정수 산술 연산에서 오버플로가 발생할 경우(결과가 해당 결과 타입에 맞지 않을 때), 그 동작은 정의되지 않습니다: 표현 방식의 규칙(일반적으로 2의 보수)에 따라 wraparound가 발생할 수 있으며, 일부 플랫폼이나 컴파일러 옵션(예: GCC와 Clang의 -ftrapv )으로 인해 트랩이 발생할 수 있거나, 컴파일러에 의해 완전히 최적화되어 제거될 수 있습니다 .

부동소수점 환경

만약 #pragma STDC FENV_ACCESS ON 으로 설정된 경우, 모든 부동 소수점 연산자는 현재 부동 소수점 반올림 방향 을 따르며, math_errhandling 에 명시된 대로 부동 소수점 산술 오류를 보고합니다. 단, 정적 초기화 의 일부인 경우는 예외입니다(이 경우 부동 소수점 예외가 발생하지 않으며 반올림 모드는 가장 가까운 값으로 설정됩니다).

부동 소수점 축약

#pragma STDC FP_CONTRACT OFF 로 설정되지 않는 한, 모든 부동 소수점 연산은 중간 결과가 무한한 범위와 정밀도를 가진 것처럼 수행될 수 있습니다. 즉, 표현식을 작성된 그대로 정확하게 평가했을 때 관찰될 수 있는 반올림 오차와 부동 소수점 예외를 생략하는 최적화가 허용됩니다. 예를 들어, ( x * y ) + z 를 단일 FMA(Fused Multiply-Add) CPU 명령어로 구현하거나 a = x * x * x * x ; tmp = x * x ; a = tmp * tmp 로 최적화하는 것이 허용됩니다.

계약과 무관하게, 부동 소수점 연산의 중간 결과는 해당 타입이 나타내는 범위 및 정밀도와 다를 수 있습니다. 자세한 내용은 FLT_EVAL_METHOD 를 참조하십시오.

단항 연산

단항 산술 연산자 표현식의 형태는 다음과 같습니다

+ 표현식 (1)
- 표현식 (2)
1) 단항 플러스 (승격)
2) 단항 마이너스 (부정 연산자)
expression - 모든 arithmetic type 의 표현식

단항 플러스와 단항 마이너스는 모두 먼저 피연산자에 정수 승격 을 적용한 후

  • 단항 플러스는 승격 후의 값을 반환합니다
  • 단항 마이너스는 승격 후 값의 음수를 반환합니다(NaN의 음수는 다른 NaN인 경우 제외)

표현식의 타입은 승진(promotion) 이후의 타입이며, 값 범주 는 비-좌측값(non-lvalue)입니다.

참고 사항

단항 마이너스 연산자는 일반적인(2의 보수) 플랫폼에서 INT_MIN , LONG_MIN , 또는 LLONG_MIN 에 적용될 때 부호 있는 정수 오버플로로 인해 정의되지 않은 동작을 유발합니다.

C++에서는 단항 연산자 + 를 배열과 함수 같은 다른 내장 타입과도 사용할 수 있지만, C에서는 그렇지 않습니다.

#include <stdio.h>
#include <complex.h>
#include <limits.h>
int main(void)
{
    char c = 'a';
    printf("sizeof char: %zu sizeof int: %zu\n", sizeof c, sizeof +c);
    printf("-1, where 1 is signed: %d\n", -1);
    // 부호 없는 정수에 대해 산술 연산이 수행되므로 정의된 동작입니다.
    // 따라서 계산은 (-1) 모듈로 (2의 n승) = UINT_MAX가 되며, 여기서 n은
    // unsigned int의 비트 수입니다. unsigned int가 32비트 길이라면
    // (-1) 모듈로 (2의 32승) = 4294967295를 제공합니다
    printf("-1, where 1 is unsigned: %u\n", -1u); 
    // -INT_MIN의 수학적 값이 INT_MAX + 1이므로 정의되지 않은 동작
    // (즉, 부호 있는 int의 최대 가능 값보다 1 더 큰 값)
    //
    // printf("%d\n", -INT_MIN);
    // -LONG_MIN의 수학적 값이 LONG_MAX + 1이므로 정의되지 않은 동작
    // (즉, 부호 있는 long의 최대 가능 값보다 1 더 큰 값)
    //
    // printf("%ld\n", -LONG_MIN);
    // -LLONG_MIN의 수학적 값이 LLONG_MAX + 1이므로 정의되지 않은 동작
    // (즉, 부호 있는 long long의 최대 가능 값보다 1 더 큰 값)
    //
    // printf("%lld\n", -LLONG_MIN);
    double complex z = 1 + 2*I;
    printf("-(1+2i) = %.1f%+.1f\n", creal(-z), cimag(-z));
}

가능한 출력:

sizeof char: 1 sizeof int: 4
-1, where 1 is signed: -1
-1, where 1 is unsigned: 4294967295
-(1+2i) = -1.0-2.0

덧셈 연산자

이항 덧셈 산술 연산자 표현식의 형식은 다음과 같습니다

lhs + rhs (1)
lhs - rhs (2)
1) 덧셈: lhs rhs 는 다음 중 하나여야 함
  • 둘 다 arithmetic types 을 가짐 (복소수 및 허수 포함)
  • 하나는 완전 객체 타입에 대한 포인터이고 다른 하나는 정수 타입을 가짐
2) 뺄셈: lhs rhs 는 다음 중 하나여야 합니다
  • 둘 다 arithmetic types (복소수 및 허수 포함)를 가져야 함
  • lhs 가 완전 객체 타입에 대한 포인터를 가지고, rhs 가 정수 타입을 가져야 함
  • 둘 다 한정자를 무시한 compatible 타입의 완전 객체에 대한 포인터여야 함

산술 덧셈과 뺄셈

두 피연산자 모두 arithmetic types 를 가지면,

  • 먼저, usual arithmetic conversions 이 수행됩니다
  • 그런 다음, 변환 후 피연산자들의 값은 수학의 일반적인 규칙에 따라 더하거나 빼집니다 (뺄셈의 경우, rhs lhs 에서 빼집니다), 단
  • 한 피연산자가 NaN이면 결과는 NaN입니다
  • 무한대 빼기 무한대는 NaN이며 FE_INVALID 가 발생합니다
  • 무한대 더하기 음의 무한대는 NaN이며 FE_INVALID 가 발생합니다

복소수와 허수의 덧셈 및 뺄셈은 다음과 같이 정의됩니다(두 피연산자가 모두 허수인 경우 결과 유형은 허수이고, 한 피연산자가 실수이고 다른 피연산자가 허수인 경우 복소수임을 유의하십시오. 이는 일반적인 산술 변환에 의해 지정됨):

+ 또는 - u iv u + iv
x x ± u x ± iv (x ± u) ± iv
iy ±u + iy i(y ± v) ±u + i(y ± v)
x + iy (x ± u) + iy x + i(y ± v) (x ± u) + i(y ± v)


// work in progress
// note: take part of the c/language/conversion example

포인터 연산

  • 포인터 P 가 인덱스 I 를 가진 배열의 요소를 가리키는 경우
  • P + N N + P 는 동일한 배열에서 I+N 인덱스를 가진 요소를 가리키는 포인터입니다
  • P - N 는 동일한 배열에서 I-N 인덱스를 가진 요소를 가리키는 포인터입니다

동작은 원본 포인터와 결과 포인터가 모두 동일한 배열의 요소를 가리키거나 해당 배열의 끝을 한 칸 지난 위치를 가리킬 때만 정의됩니다. p가 배열의 첫 번째 요소를 가리킬 때 p-1을 실행하는 것은 정의되지 않은 동작이며 일부 플랫폼에서 실패할 수 있습니다.

  • 포인터 P1 이 인덱스 I (또는 끝의 다음)를 가진 배열 요소를 가리키고, P2 가 동일한 배열의 인덱스 J (또는 끝의 다음)를 가진 요소를 가리키는 경우,
  • P1 - P2 I - J 와 동일한 값을 가지며, 타입은 ptrdiff_t 입니다 (이는 부호 있는 정수 타입으로, 일반적으로 선언 가능한 가장 큰 객체의 크기의 절반 정도입니다)

동작은 결과가 ptrdiff_t 에 맞는 경우에만 정의됩니다.

포인터 연산의 목적상, 어떤 배열의 요소가 아닌 객체에 대한 포인터는 크기가 1인 배열의 첫 번째 요소에 대한 포인터로 취급됩니다.

// work in progress
int n = 4, m = 3;
int a[n][m];     // VLA of 4 VLAs of 3 ints each
int (*p)[m] = a; // p == &a[0] 
p = p + 1;       // p == &a[1] (pointer arithmetic works with VLAs just the same)
(*p)[2] = 99;    // changes a[1][2]

승산 연산자

이항 곱셈 산술 연산자 표현식의 형식은 다음과 같습니다

lhs * rhs (1)
lhs / rhs (2)
lhs % rhs (3)
**참고**: 주어진 지침에 따라 HTML 태그, 속성, 태그 내부의 텍스트(C++ 연산자 기호), C++ 특정 용어(lhs, rhs)는 번역하지 않고 원본을 유지했습니다. 표의 구조와 서식도 완전히 보존되었습니다.
1) 곱셈. lhs rhs 산술 타입 을 가져야 합니다.
2) 나눗셈. lhs rhs 산술 타입 을 가져야 합니다
3) 나머지. lhs rhs 정수 타입 을 가져야 합니다

곱셈

이항 연산자 *는 피연산자들(일반적인 산술 변환 후)을 일반적인 산술 정의에 따라 곱셈을 수행합니다. 단, 다음 예외가 적용됩니다:

  • 피연산자 중 하나가 NaN이면 결과는 NaN입니다
  • 무한대와 0의 곱셈은 NaN을 생성하며 FE_INVALID 가 발생합니다
  • 무한대와 0이 아닌 값의 곱셈은 무한대를 생성합니다 (복소수 인자의 경우에도 동일)

C 언어에서는 적어도 하나의 무한대 부분을 가진 모든 복소수 값 이 NaN을 포함하더라도 무한대로 간주되므로, 일반적인 산술 규칙이 복소수-복소수 곱셈에 적용되지 않습니다. 부동 소수점 연산자의 다른 조합은 다음 표를 따릅니다:

* u iv u + iv
x xu i(xv) (xu) + i(xv)
iy i(yu) −yv (−yv) + i(yu)
x + iy (xu) + i(yu) (−yv) + i(xv) 특수 규칙

무한대 처리 외에도, 복소수 곱셈은 중간 결과가 오버플로되는 것을 허용하지 않습니다. 단, #pragma STDC CX_LIMITED_RANGE ON 으로 설정된 경우는 예외이며, 이 경우 값은 (x+iy)×(u+iv) = (xu-yv)+i(yu+xv) 와 같이 계산될 수 있습니다. 이때 프로그래머는 피연산자의 범위를 제한하고 무한대를 처리할 책임을 집니다.

부당한 오버플로를 허용하지 않음에도 불구하고, 복소수 곱셈은 잘못된 부동 소수점 예외를 발생시킬 수 있습니다 (그렇지 않으면 오버플로가 발생하지 않는 버전을 구현하는 것이 극도로 어렵습니다)

#include <stdio.h>
#include <stdio.h>
#include <complex.h>
#include <math.h>
int main(void)
{
// TODO simpler cases, take some from C++
   double complex z = (1 + 0*I) * (INFINITY + I*INFINITY);
// 교과서 공식으로는
// (1+i0)(∞+i∞) ⇒ (1×∞ – 0×∞) + i(0×∞+1×∞) ⇒ NaN + I*NaN
// 이지만 C는 복소수 무한대를 제공함
   printf("%f + i*%f\n", creal(z), cimag(z));
// 교과서 공식으로는
// cexp(∞+iNaN) ⇒ exp(∞)×(cis(NaN)) ⇒ NaN + I*NaN
// 이지만 C는 ±∞+i*nan을 제공함
   double complex y = cexp(INFINITY + I*NAN);
   printf("%f + i*%f\n", creal(y), cimag(y));
}

가능한 출력:

inf + i*inf 
inf + i*nan

나눗셈

이항 연산자 / 는 첫 번째 피연산자를 두 번째 피연산자로 나누는데(일반적인 산술 변환 후), 다음 예외를 제외하고 일반적인 산술 정의를 따릅니다:

  • 일반적인 산술 변환 후의 타입이 정수형일 때, 결과는 대수적 몫(분수가 아님)이며, 구현에서 정의된 방향으로 반올림됨 (C99 이전) 영을 향해 절사됨 (C99 이후)
  • 한 피연산자가 NaN인 경우 결과는 NaN임
  • 첫 번째 피연산자가 복소수 무한대이고 두 번째 피연산자가 유한한 경우, / 연산자의 결과는 복소수 무한대임
  • 첫 번째 피연산자가 유한하고 두 번째 피연산자가 복소수 무한대인 경우, / 연산자의 결과는 영임

C 언어에서는 복소수 값 중 적어도 한 부분이 무한대인 경우 다른 부분이 NaN이더라도 항상 무한대로 간주되므로, 일반적인 산술 규칙이 복소수-복소수 나눗셈에는 적용되지 않습니다. 부동 소수점 연산자의 다른 조합은 다음 표를 따릅니다:

/ u iv
x x/u i(−x/v)
iy i(y/u) y/v
x + iy (x/u) + i(y/u) (y/v) + i(−x/v)

무한대 처리 외에도, 복소수 나눗셈은 중간 결과가 오버플로되는 것을 허용하지 않습니다. 단, #pragma STDC CX_LIMITED_RANGE ON 으로 설정된 경우에는 예외이며, 이 경우 값은 (x+iy)/(u+iv) = [(xu+yv)+i(yu-xv)]/(u 2
+v 2
)
와 같이 계산될 수 있습니다. 이때 프로그래머는 피연산자의 범위를 제한하고 무한대를 처리할 책임을 집니다.

부당한 오버플로를 허용하지 않음에도 불구하고, 복소수 나눗셈은 허위 부동 소수점 예외를 발생시킬 수 있습니다 (그렇지 않으면 오버플로가 발생하지 않는 버전을 구현하는 것이 극도로 어렵습니다)

두 번째 피연산자가 0인 경우, 동작은 정의되지 않습니다. 단, IEEE 부동 소수점 연산이 지원되고 부동 소수점 나눗셈이 수행되는 경우에는 다음이 적용됩니다:

  • 0이 아닌 숫자를 ±0.0으로 나누면 부호가 맞는 무한대가 생성되고 FE_DIVBYZERO 가 발생합니다
  • 0.0을 0.0으로 나누면 NaN이 생성되고 FE_INVALID 가 발생합니다

나머지

이항 연산자 %는 첫 번째 피연산자를 두 번째 피연산자로 나눈 나머지를 반환합니다(일반적인 산술 변환 후).

나머지의 부호는 몫 a/b 가 결과 타입으로 표현 가능할 경우, ( a / b ) * b + a % b == a 가 성립하도록 정의됩니다.

두 번째 피연산자가 0인 경우, 동작은 정의되지 않습니다.

a/b 가 결과 타입으로 표현 불가능한 경우, a/b a%b 의 동작은 모두 정의되지 않음 (이는 INT_MIN %- 1 가 2의 보수 시스템에서 정의되지 않음을 의미함)

참고: 나머지 연산자는 부동 소수점 타입에서 작동하지 않으며, 이 기능은 라이브러리 함수 fmod 에서 제공됩니다.

비트 논리 연산

비트 연산자 표현식의 형식은 다음과 같습니다

~ rhs (1)
lhs & rhs (2)
lhs | rhs (3)
lhs ^ rhs (4)
**참고사항:** - HTML 태그와 속성은 번역하지 않음 - 원본 형식 유지 - ` `, `
`, `` 태그 내 텍스트는 번역하지 않음
- C++ 관련 용어는 번역하지 않음
- 정밀성과 전문성을 유지
**번역 결과:**
표의 내용이 C++ 비트 연산자 구문으로, 모든 텍스트가 번역 제외 대상(HTML 태그, 코드 태그 내부, C++ 용어)에 해당하므로 원본 그대로 유지됩니다.
1) 비트별 NOT
2) 비트 AND
3) 비트 OR 연산
4) 비트별 XOR

여기서

lhs , rhs - 정수형 표현식

먼저, 연산자 & , ^ , 그리고 | 는 두 피연산자에 대해 usual arithmetic conversions 를 수행하고, 연산자 ~ 는 단일 피연산자에 대해 integer promotions 를 수행합니다.

그런 다음, 해당하는 이진 논리 연산자들이 비트 단위로 적용됩니다; 즉, 결과의 각 비트는 피연산자들의 해당 비트에 적용된 논리 연산(NOT, AND, OR, 또는 XOR)에 따라 설정되거나 지워집니다.

참고: 비트 연산자는 일반적으로 비트 집합과 비트 마스크를 조작하는 데 사용됩니다.

참고: 부호 없는 타입(승진 후)의 경우, 표현식 ~E 는 결과 타입으로 표현 가능한 최댓값에서 원래 E 의 값을 뺀 것과 동일합니다.

#include <stdio.h>
#include <stdint.h>
int main(void)
{
    uint32_t a = 0x12345678;
    uint16_t mask = 0x00f0;
    printf("Promoted mask:\t%#010x\n"
           "Value:\t\t%#x\n"
           "Setting bits:\t%#x\n"
           "Clearing bits:\t%#x\n"
           "Selecting bits:\t%#010x\n"
           , mask
           , a
           , a | mask
           , a & ~mask
           , a & mask
    );
}

가능한 출력:

Promoted mask:  0x000000f0
Value:          0x12345678
Setting bits:   0x123456f8
Clearing bits:  0x12345608
Selecting bits: 0x00000070

시프트 연산자

비트 시프트 연산자 표현식의 형식은 다음과 같습니다

lhs << rhs (1)
lhs >> rhs (2)
**참고:** 주어진 지침에 따라 HTML 태그, 속성, 태그 내부의 C++ 연산자(<<, >>)와 lhs/rhs 같은 C++ 용어는 번역하지 않고 원형을 유지했습니다. 표의 구조와 서식도 완전히 보존되었습니다.
1) lhs rhs 비트만큼 왼쪽 시프트
2) lhs rhs 비트만큼 오른쪽 시프트

여기서

lhs , rhs - 정수형 표현식

먼저, integer promotions 이 각 피연산자에 대해 개별적으로 수행됩니다 (참고: 이는 일반 산술 변환을 수행하는 다른 이항 산술 연산자들과 다릅니다). 결과의 타입은 승격 후 lhs 의 타입입니다.

rhs 가 음수이거나 승격된 lhs 의 비트 수보다 크거나 같은 경우 동작은 정의되지 않습니다.

부호 없는 lhs 의 경우, LHS << RHS 의 값은 LHS * 2 RHS
의 값을 반환 타입의 최대값에 1을 더한 값으로 모듈로 감소시킨 것입니다(즉, 비트 왼쪽 시프트가 수행되고 대상 타입에서 벗어난 비트들은 버려집니다). 음수가 아닌 값을 가진 부호 있는 lhs 의 경우, LHS << RHS 의 값은 LHS * 2 RHS
lhs 의 승격된 타입으로 표현 가능할 때 해당 값이며, 그렇지 않으면 동작은 정의되지 않습니다.

부호 없는 lhs 와 음수가 아닌 값을 가진 부호 있는 lhs 의 경우, LHS >> RHS 의 값은 LHS / 2 RHS
의 정수 부분입니다. 음수인 LHS 의 경우, LHS >> RHS 의 값은 구현에 따라 정의되며, 대부분의 구현에서 이는 산술 우측 시프트를 수행합니다(결과가 음수로 유지되도록). 따라서 대부분의 구현에서 부호 있는 LHS 를 우측 시프트할 때 새로운 상위 비트들은 원본의 부호 비트로 채워집니다(즉, 음수가 아니었으면 0으로, 음수였으면 1로 채워짐).

#include <stdio.h>
enum {ONE=1, TWO=2};
int main(void)
{
    char c = 0x10;
    unsigned long long ulong_num = 0x123;
    printf("0x123 << 1  = %#llx\n"
           "0x123 << 63 = %#llx\n"   // 오버플로우 시 부호 없는 숫자의 상위 비트가 잘림
           "0x10  << 10 = %#x\n",    // char가 int로 승격됨
           ulong_num << 1, ulong_num << 63, c << 10);
    long long long_num = -1000;
    printf("-1000 >> 1 = %lld\n", long_num >> ONE);  // 구현 정의 동작
}

가능한 출력:

0x123 << 1  = 0x246
0x123 << 63 = 0x8000000000000000
0x10  << 10 = 0x4000
-1000 >> 1 = -500

참고문헌

  • C17 표준 (ISO/IEC 9899:2018):
  • 6.5.3.3 단항 산술 연산자 (p: 64)
  • 6.5.5 승법 연산자 (p: 66)
  • 6.5.6 가법 연산자 (p: 66-68)
  • 6.5.7 비트 시프트 연산자 (p: 68)
  • 6.5.10 비트 AND 연산자 (p: 70)
  • 6.5.11 비트 배타적 OR 연산자 (p: 70)
  • 6.5.12 비트 포괄적 OR 연산자 (p: 70-71)
  • C11 표준 (ISO/IEC 9899:2011):
  • 6.5.3.3 단항 산술 연산자 (p: 89)
  • 6.5.5 승법 연산자 (p: 92)
  • 6.5.6 가법 연산자 (p: 92-94)
  • 6.5.7 비트 시프트 연산자 (p: 94-95)
  • 6.5.10 비트 AND 연산자 (p: 97)
  • 6.5.11 비트 배타적 OR 연산자 (p: 98)
  • 6.5.12 비트 포괄적 OR 연산자 (p: 98)
  • C99 표준 (ISO/IEC 9899:1999):
  • 6.5.3.3 단항 산술 연산자 (p: 79)
  • 6.5.5 승법 연산자 (p: 82)
  • 6.5.6 가법 연산자 (p: 82-84)
  • 6.5.7 비트 시프트 연산자 (p: 84-85)
  • 6.5.10 비트 AND 연산자 (p: 87)
  • 6.5.11 비트 배타적 OR 연산자 (p: 88)
  • 6.5.12 비트 포괄적 OR 연산자 (p: 88)
  • C89/C90 표준 (ISO/IEC 9899:1990):
  • 3.3.3.3 단항 산술 연산자
  • 3.3.5 승법 연산자
  • 3.3.6 가법 연산자
  • 3.3.7 비트 시프트 연산자
  • 3.3.10 비트 AND 연산자
  • 3.3.11 비트 배타적 OR 연산자
  • 3.3.12 비트 포괄적 OR 연산자

참고 항목

연산자 우선순위

일반 연산자
대입 증가
감소
산술 논리 비교 멤버
접근
기타

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부터)
(C23 이전)

alignof
(C23부터)

C++ 문서 for Arithmetic operators