Namespaces
Variants

Implicit conversions

From cppreference.net

표현식이 다른 타입의 값이 예상되는 맥락에서 사용될 때, 변환(conversion) 이 발생할 수 있습니다:

int n = 1L; // 표현식 1L은 long 타입을 가지며, int가 예상됨
n = 2.1; // 표현식 2.1은 double 타입을 가지며, int가 예상됨
char* p = malloc(10); // 표현식 malloc(10)은 void* 타입을 가지며, char*가 예상됨

변환은 다음과 같은 상황에서 발생합니다:

목차

할당에 의한 변환

  • 대입 연산자에서 우변 피연산자의 값은 좌변 피연산자의 비한정 타입으로 변환됩니다.
  • 스칼라 초기화 에서 초기화식의 값은 초기화되는 객체의 비한정 타입으로 변환됩니다.
  • 프로토타입을 가진 함수에 대한 함수 호출 표현식 에서 각 인수 표현식의 값은 해당 매개변수의 비한정 선언 타입으로 변환됩니다.
  • 반환문 에서 return 의 피연산자 값은 함수의 반환 타입을 가진 객체로 변환됩니다.

실제 대입은 변환 외에도 부동 소수점 타입에서 추가 범위와 정밀도를 제거하고 중첩을 금지한다는 점에 유의하십시오; 이러한 특성은 대입에 의한 변환처럼 적용되지 않습니다.

기본 인수 승격

함수 호출 표현식 에서 호출이 수행될 때

1) 프로토타입 없는 함수 (until C23) ,
2) 가변 인자 함수에서, 인자 표현식이 생략 부호 매개변수와 일치하는 후행 인자 중 하나인 경우 variadic function

각 정수 타입 인수는 정수 승격 을 거치며, float 타입의 각 인수는 암시적으로 double 타입으로 변환됩니다.

int add_nums(int count, ...);
int sum = add_nums(2, 'c', true); // add_nums가 세 개의 int로 호출됨: (2, 99, 1)

이 문맥에서 float complex float imaginary double complex double imaginary 으로 승격되지 않습니다.

(C99부터)

일반 산술 변환

다음 산술 연산자의 인수들은 계산이 수행되는 타입인 공통 실수 타입  을 얻기 위해 암시적 변환을 거칩니다:

1) 한 피연산자가 10진 부동 소수점 타입을 가지는 경우, 다른 피연산자는 표준 부동 소수점,

복소수, 또는 허수 타입을 가져서는 안 됩니다.

  • 먼저, 어느 한 피연산자의 타입이 _Decimal128 인 경우, 다른 피연산자는 _Decimal128 로 변환됩니다.
  • 그렇지 않고, 어느 한 피연산자의 타입이 _Decimal64 인 경우, 다른 피연산자는 _Decimal64 로 변환됩니다.
  • 그렇지 않고, 어느 한 피연산자의 타입이 _Decimal32 인 경우, 다른 피연산자는 _Decimal32 로 변환됩니다.
(C23 이후)
2) 그렇지 않고 한 피연산자가 long double , long double complex , 또는 long double imaginary (C99부터) 인 경우, 다른 피연산자는 다음과 같이 암시적으로 변환됩니다:
  • 정수 또는 실수 부동소수점 타입은 long double 으로 변환
  • 복소수 타입은 long double complex 으로 변환
  • 허수 타입은 long double imaginary 으로 변환
(C99부터)
3) 그렇지 않고 한 피연산자가 double , double complex , 또는 double imaginary (C99부터) 인 경우, 다른 피연산자는 다음과 같이 암시적으로 변환됩니다:
  • 정수 또는 실수 부동소수점 타입은 double 으로 변환
(C99부터)
4) 그렇지 않고 한 피연산자가 float , float complex , 또는 float imaginary (C99부터) 인 경우, 다른 피연산자는 다음과 같이 암시적으로 변환됩니다:
  • 정수 타입은 float 으로 변환됨 (가능한 유일한 실수 타입은 float이며, 이는 그대로 유지됨)
(C99부터)
5) 그렇지 않으면, 두 피연산자 모두 정수형입니다. 두 피연산자는 모두 정수 승격 을 거친 후; 정수 승격 이후 다음 경우 중 하나가 적용됩니다:
  • 타입이 동일한 경우, 해당 타입이 공통 타입이 됩니다.
  • 그렇지 않으면 타입이 서로 다릅니다:
    • 타입이 동일한 부호 특성을 가지는 경우(둘 다 부호 있음 또는 둘 다 부호 없음), 변환 순위 [1] 가 더 낮은 타입의 피연산자가 다른 타입으로 암시적으로 변환됩니다 [2] .
    • 그렇지 않으면 피연산자들의 부호 특성이 다릅니다:
      • 부호 없는 타입의 변환 순위 가 부호 있는 타입의 순위보다 크거나 같은 경우, 부호 있는 타입의 피연산자가 부호 없는 타입으로 암시적으로 변환됩니다.
      • 그렇지 않으면 부호 없는 타입의 변환 순위 가 부호 있는 타입보다 낮습니다:
        • 부호 있는 타입이 부호 없는 타입의 모든 값을 표현할 수 있는 경우, 부호 없는 타입의 피연산자가 부호 있는 타입으로 암시적으로 변환됩니다.
        • 그렇지 않으면, 두 피연산자 모두 부호 있는 피연산자의 타입에 대응하는 부호 없는 타입으로 암시적 변환을 겪습니다.
  1. 순위 규칙은 아래 정수 승격 을 참조하십시오.
  2. 아래 암시적 변환 의미론 에서 "정수 변환"을 참조하십시오.
1.f + 20000001; // int가 float로 변환되어 20000000.00이 됨
                // 덧셈 후 float로 반올림하여 20000000.00이 됨
(char)'a' + 1L; // 먼저, char 'a'(97)가 int로 승격됨
                // 다른 타입: int와 long
                // 같은 부호성: 둘 다 부호 있음
                // 다른 순위: long이 int보다 높은 순위
                // 따라서 int 97은 long 97로 변환됨
                // 결과는 97 + 1 = 98, 타입은 signed long
2u - 10; // 다른 타입: unsigned int와 signed int
         // 다른 부호성
         // 같은 순위
         // 따라서 signed int 10은 unsigned int 10으로 변환됨
         // 산술 연산이 unsigned 정수로 수행되므로
         // ("Arithmetic operators" 항목 참조), 계산은 (2 - 10)
         // 모듈로 (2의 n승), 여기서 n은 unsigned int의 값 비트 수
         // unsigned int가 32비트이고 객체 표현에 패딩 비트가 없다면
         // 결과는 (-8) 모듈로 (2의 32승) = 4294967288
         // 타입은 unsigned int
5UL - 2ULL; // 다른 타입: unsigned long과 unsigned long long
            // 같은 부호성
            // 다른 순위: unsigned long long의 순위가 더 높음
            // 따라서 unsigned long 5는 unsigned long long 5로 변환됨
            // 산술 연산이 unsigned 정수로 수행되므로
            // ("Arithmetic operators" 항목 참조),
            // unsigned long long이 64비트라면
            // 결과는 (5 - 2) 모듈로 (2의 64승) = 3, 타입은
            // unsigned long long
0UL - 1LL; // 다른 타입: unsigned long과 signed long long
           // 다른 부호성
           // 다른 순위: signed long long의 순위가 더 높음
           // ULONG_MAX > LLONG_MAX인 경우, signed long long은 모든
           // unsigned long을 표현할 수 없으므로, 이는 마지막 경우: 두 피연산자는 모두
           // unsigned long long으로 변환됨 unsigned long 0은 unsigned long long 0으로 변환됨
           // long long 1은 unsigned long long 1로 변환됨 산술 연산이
           // unsigned 정수로 수행되므로
           // ("Arithmetic operators" 항목 참조),
           // unsigned long long이 64비트라면
           // 계산은 (0 - 1) 모듈로 (2의 64승)
           // 따라서 결과는 18446744073709551615 (ULLONG_MAX), 타입은
           // unsigned long long

결과 타입은 다음과 같이 결정됩니다:

  • 두 피연산자가 모두 복소수인 경우, 결과 타입은 복소수입니다;
  • 두 피연산자가 모두 허수인 경우, 결과 타입은 허수입니다;
  • 두 피연산자가 모두 실수인 경우, 결과 타입은 실수입니다;
  • 두 부동소수점 피연산자가 서로 다른 타입 도메인(복소수 vs 실수, 복소수 vs 허수, 또는 허수 vs 실수)을 가지는 경우, 결과 타입은 복소수입니다.
double complex z = 1 + 2*I;
double f = 3.0;
z + f; // z는 그대로 유지되고, f는 double로 변환되며, 결과는 double complex입니다
(C99부터)

언제나 그렇듯, 부동 소수점 연산자의 결과는 해당 타입이 나타내는 범위와 정밀도보다 더 넓은 범위와 정밀도를 가질 수 있습니다 (참조: FLT_EVAL_METHOD ).

참고: 실수 및 허수 피연산자는 복소수로 암시적으로 변환되지 않습니다. 이렇게 하면 추가 계산이 필요하게 되며, 무한대, NaN 및 부호 있는 0이 포함된 특정 경우에서 바람직하지 않은 결과를 생성하기 때문입니다. 예를 들어, 실수가 복소수로 변환된다면 2.0×(3.0+i∞)는 (2.0+i0.0)×(3.0+i∞) ⇒ (2.0×3.0–0.0×∞) + i(2.0×∞+0.0×3.0) ⇒ NaN+i∞로 계산되어 올바른 결과인 6.0+i∞ 대신 잘못된 결과가 나옵니다. 허수가 복소수로 변환된다면 i2.0×(∞+i3.0)는 (0.0+i2.0) × (∞+i3.0) ⇒ (0.0×∞ – 2.0×3.0) + i(0.0×3.0 + 2.0×∞) ⇒ NaN + i∞로 계산되어 올바른 결과인 –6.0 + i∞ 대신 잘못된 결과가 나옵니다.

(C99부터)

참고: 일반적인 산술 변환과 관계없이, 계산은 항상 다음 규칙에 따라 지정된 것보다 좁은 타입에서 수행될 수 있습니다. as-if 규칙 에 따르면.

값 변환

Lvalue 변환

모든 lvalue 표현식 은 배열 타입이 아닌 모든 타입에 대해, 다음을 제외한 모든 컨텍스트에서 사용될 때

lvalue 변환  을 거칩니다: 타입은 동일하게 유지되지만, const / volatile / restrict -한정자 및 atomic 속성이 있는 경우 이를 상실합니다. 값은 동일하게 유지되지만, lvalue 속성을 상실합니다(주소를 더 이상 취할 수 없음).

lvalue가 불완전한 타입을 가질 경우, 동작은 정의되지 않습니다.

lvalue가 주소가 한 번도 취해지지 않은 자동 저장 기간 객체를 지정하고, 해당 객체가 초기화되지 않은 경우(초기화자로 선언되지 않았고 사용 전까지 할당이 수행되지 않음), 그 동작은 정의되지 않습니다.

이 변환은 객체의 값이 해당 위치에서 메모리 로드되는 방식을 모델링합니다.

volatile int n = 1;
int x = n;            // n에 대한 lvalue 변환으로 n의 값을 읽음
volatile int* p = &n; // lvalue 변환 없음: n의 값을 읽지 않음

배열에서 포인터로의 변환

배열 타입의 lvalue expression (until C99) expression (since C99) 는 다음의 경우를 제외하고 사용될 때

첫 번째 요소에 대한 비-좌측값 포인터로 변환을 겪습니다.

배열이 register 로 선언된 경우, 동작은 정의되지 않습니다.

비-좌측값 배열 또는 그 어떤 요소도 접근할 수 없습니다 (C99까지) , temporary lifetime 을 가집니다 (C99부터) .

int a[3], b[3][4];
int* p = a;      /* &a[0]로의 변환 */
int (*q)[4] = b; /* &b[0]로의 변환 */
struct S
{
    int a[1];
};
struct S f(void)
{
    struct S result = {{0}}; /* C99 이후 {0} */
    return result;
}
void g(void)
{
    int* p = f().a;    /* C99 이전 오류; C99 이후 정상 */
    int n  = f().a[0]; /* C99 이전 오류; C99 이후 정상 */
    f().a[0] = 13;     /* C99 이전 오류; C99 이후 미정의 동작 */
    (void)p, (void)n;
}
int main(void) { return 0; }

함수에서 포인터로의 변환

함수 지정자 표현식은 다음을 제외한 어떤 문맥에서 사용될 때

식으로 지정된 함수에 대한 비좌측값 포인터로의 변환을 겪습니다.

int f(int);
int (*p)(int) = f; // f로의 변환
(***p)(1); // f에 대한 반복 역참조 및 &f로의 변환

암시적 변환 의미론

암시적 변환은 할당에 의한 것처럼  또는 일반적인 산술 변환  인지 여부에 관계없이 두 단계로 구성됩니다:

1) 값 변환 (해당하는 경우),
2) 아래 나열된 변환 중 하나(대상 유형을 생성할 수 있는 경우).

호환 가능한 타입

모든 유형의 값을 호환 가능한 유형 으로의 변환은 항상 no-op이며 표현을 변경하지 않습니다.

uint8_t (*a)[10];         // uint8_t가 unsigned char에 대한 typedef인 경우
unsigned char (*b)[] = a; // 이 포인터 타입들은 호환됩니다

정수 승격

정수 승격은 int의 순위 보다 작거나 같은 순위를 가진 임의의 정수 타입 값이나 비트 필드 타입 _Bool (C23까지) bool (C23부터) , int , signed int , unsigned int 의 값을 int 또는 unsigned int 타입의 값으로 암시적으로 변환하는 것입니다.

만약 int 가 원본 타입의 전체 값 범위(또는 원본 비트 필드의 값 범위)를 표현할 수 있다면, 값은 int 타입으로 변환됩니다. 그렇지 않으면 값은 unsigned int 로 변환됩니다.

비트 정밀 정수 타입의 비트 필드에서 가져온 값은 해당 비트 정밀 정수 타입으로 변환됩니다. 그렇지 않으면, 비트 정밀 정수 타입은 정수 승격 규칙에서 제외됩니다.

(C23부터)

정수 승격은 부호를 포함한 값을 보존합니다:

int main(void)
{
    void f(); // 구식 함수 선언
              // C23부터 void f(...)는 승환(promotions)에 대해 동일한 동작을 가짐
    char x = 'a'; // int에서 char로의 정수 변환
    f(x); // char에서 다시 int로의 정수 승환
}
void f(x) int x; {} // 함수는 int를 기대함

rank 는 모든 정수 타입 의 속성이며 다음과 같이 정의됩니다:

1) 모든 부호 있는 정수형의 순위는 서로 다르며 정밀도에 따라 증가합니다: signed char 의 순위 < short 의 순위 < int 의 순위 < long int 의 순위 < long long int 의 순위
2) 모든 부호 있는 정수형의 순위는 해당 부호 없는 정수형의 순위와 동일합니다
3) 모든 표준 정수형의 순위는 동일한 크기를 가지는 확장 정수형 또는 비트 정밀 정수형 (C23부터) 의 순위보다 높다(즉, __int64 의 순위 < long long int 의 순위이지만, long long 의 순위 < __int128 의 순위인 이유는 (1) 규칙 때문임)
4) char 의 랭크는 signed char 의 랭크와 동일하며, unsigned char 의 랭크와도 동일합니다.
5) _Bool (until C23) bool (since C23) 의 랭크는 다른 모든 표준 정수형의 랭크보다 낮습니다
6) 열거형의 순위는 호환되는 정수형의 순위와 동일합니다
7) 랭킹은 이행적(transitive)입니다: T1의 랭크 < T2의 랭크이고 T2의 랭크 < T3의 랭크이면 T1의 랭크 < T3의 랭크입니다.
8) 비트 정밀 부호 있는 정수 타입의 순위는 더 적은 너비를 가진 표준 정수 타입이나 더 적은 너비를 가진 비트 정밀 정수 타입의 순위보다 커야 한다.
9) 동일한 너비를 가진 확장 정수 타입에 대한 비트 정밀 정수 타입의 순위는 구현에서 정의된다.
(C23부터)
10) 위에서 다루지 않은 확장 정수형의 상대적 순위에 관한 모든 측면은 구현에서 정의됩니다.

참고: 정수 승격은 오직

  • 일반적인 산술 변환 (위 참조)의 일부로,
  • 기본 인수 승격 (위 참조)의 일부로,
  • 단항 산술 연산자 + - 의 피연산자로,
  • 단항 비트 연산자 ~ 의 피연산자로,
  • 시프트 연산자 << >> 의 양쪽 피연산자로.

불리언 변환

모든 스칼라 타입의 값은 암시적으로 _Bool (C23 이전) bool (C23 이후) 으로 변환될 수 있습니다. 값이 0인 정수 상수 표현식과 비교하여 동일한 값들 (C23 이전) 이 영(산술 타입의 경우), 널(포인터 타입의 경우)이거나 nullptr_t 타입을 가진 값들 (C23 이후) 0 (C23 이전) false (C23 이후) 으로 변환되며, 다른 모든 값들은 1 (C23 이전) true (C23 이후) 으로 변환됩니다.

bool b1 = 0.5;              // b1 == 1 (0.5 converted to int would be zero)
bool b2 = 2.0*_Imaginary_I; // b2 == 1 (but converted to int would be zero)
bool b3 = 0.0 + 3.0*I;      // b3 == 1 (but converted to int would be zero)
bool b4 = 0.0 / 0.0;        // b4 == 1 (NaN does not compare equal to zero)
bool b5 = nullptr;          // b5 == 0 (since C23: nullptr is converted to false)
(C99 이후)

정수 변환

모든 정수형 타입의 값은 암시적으로 다른 정수형 타입으로 변환될 수 있습니다. 앞서 설명한 승격(promotions)과 불린 변환(boolean conversions)을 제외한 규칙은 다음과 같습니다:

  • 대상 타입이 값을 표현할 수 있는 경우, 값은 변경되지 않습니다.
  • 그렇지 않고 대상 타입이 unsigned인 경우, 값에서 2 b
    (여기서 b 는 대상 타입의 값 비트 수)을 결과가 대상 타입에 맞을 때까지 반복적으로 빼거나 더합니다. 즉, unsigned 정수는 모듈로 연산을 구현합니다.
  • 그렇지 않고 대상 타입이 signed인 경우, 동작은 구현에 따라 정의됩니다(시그널 발생을 포함할 수 있음).
char x = 'a'; // int → char, 결과는 변경되지 않음
unsigned char n = -123456; // 대상이 부호 없는 타입이므로 결과는 192 (즉, -123456+483*256)
signed char m = 123456;    // 대상이 부호 있는 타입이므로 결과는 구현에 따라 정의됨
assert(sizeof(int) > -1);  // assert 실패:
                           // 연산자 >가 -1을 size_t로 변환을 요청하며,
                           // 대상이 부호 없는 타입이므로 결과는 SIZE_MAX

실수 부동소수점-정수 변환

모든 실수 부동 소수점 유형의 유한 값은 암시적으로 모든 정수 유형으로 변환될 수 있습니다. 위의 불리언 변환에서 다루는 경우를 제외하고 규칙은 다음과 같습니다:

  • 소수 부분은 버려집니다(0 방향으로 절사).
  • 결과 값이 대상 타입으로 표현 가능한 경우, 해당 값이 사용됩니다
  • 그렇지 않으면, 동작은 정의되지 않습니다.
int n = 3.14; // n == 3
int x = 1e10; // 32비트 int에 대한 정의되지 않은 동작

모든 정수형의 값은 암시적으로 모든 실수 부동소수점 형식으로 변환될 수 있습니다.

  • 값이 대상 타입으로 정확히 표현될 수 있는 경우, 변경되지 않습니다.
  • 값이 표현될 수 있지만 정확히 표현될 수 없는 경우, 결과는 가장 가까운 높은 값 또는 가장 가까운 낮은 값 중 구현에서 정의한 선택입니다(단, IEEE 산술이 지원되는 경우 가장 가까운 값으로 반올림됨). 이 경우 FE_INEXACT 가 발생하는지 여부는 명시되지 않습니다.
  • 값이 표현될 수 없는 경우, 동작은 정의되지 않습니다(단, IEEE 산술이 지원되는 경우 FE_INVALID 가 발생하고 결과 값은 명시되지 않음).

이 변환의 결과는 대상 타입이 나타내는 것보다 더 넓은 범위와 정밀도를 가질 수 있습니다 ( FLT_EVAL_METHOD 참조).

부동 소수점에서 정수 변환 시 FE_INEXACT 제어가 필요한 경우, rint nearbyint 함수를 사용할 수 있습니다.

double d = 10; // d = 10.00
float f = 20000001; // f = 20000000.00 (FE_INEXACT)
float x = 1 + (long long)FLT_MAX; // 정의되지 않은 동작

실수 부동소수점 변환

모든 실수 부동 소수점 타입의 값은 다른 실수 부동 소수점 타입으로 암시적으로 변환될 수 있습니다.

  • 값이 대상 타입으로 정확히 표현될 수 있는 경우, 변경되지 않습니다.
  • 값이 표현될 수 있지만 정확히 표현할 수 없는 경우, 결과는 가장 가까운 높은 값 또는 가장 가까운 낮은 값이 됩니다(즉, 반올림 방향은 구현에 따라 정의됨). 단 IEEE 연산이 지원되는 경우 가장 가까운 값으로 반올림됩니다.
  • 값이 표현될 수 없는 경우, 동작은 정의되지 않음 .

이 변환의 결과는 대상 타입이 나타내는 것보다 더 넓은 범위와 정밀도를 가질 수 있습니다 ( FLT_EVAL_METHOD 참조).

double d = 0.1; // d = 0.1000000000000000055511151231257827021181583404541015625
float f = d;    // f = 0.100000001490116119384765625
float x = 2 * (double)FLT_MAX; // 정의되지 않음

복소수 타입 변환

모든 복소수 타입의 값은 다른 복소수 타입으로 암시적으로 변환될 수 있습니다. 실수부와 허수부는 각각 실수 부동소수점 타입에 대한 변환 규칙을 따릅니다.

double complex d = 0.1 + 0.1*I;
float complex f = d; // f is (0.100000001490116119384765625, 0.100000001490116119384765625)

허수 타입 변환

모든 허수 타입의 값은 다른 허수 타입으로 암시적으로 변환될 수 있습니다. 허수부는 실수 부동소수점 타입에 대한 변환 규칙을 따릅니다.

double imaginary d = 0.1*_Imaginary_I;
float imaginary f = d; // f is 0.100000001490116119384765625*I

실수-복소수 변환

모든 실수 부동소수점 타입의 값은 모든 복소수 타입으로 암시적으로 변환될 수 있습니다.

  • 결과의 실수부는 실수 부동소수점 타입에 대한 변환 규칙에 따라 결정됩니다.
  • 결과의 허수부는 양의 영(또는 비-IEEE 시스템에서 부호 없는 영)입니다.

모든 복소수 타입의 값은 모든 실수 부동소수점 타입으로 암시적으로 변환될 수 있습니다.

  • 실수부는 실수 부동소수점 타입에 대한 규칙을 따라 변환됩니다.
  • 허수부는 버려집니다.

참고: 복소수에서 실수로의 변환에서 허수부의 NaN은 실수 결과로 전파되지 않습니다.

double complex z = 0.5 + 3*I;
float f = z;  // the imaginary part is discarded, f is set to 0.5
z = f;        // sets z to 0.5 + 0*I

실수-허수 변환

모든 허수 타입의 값은 모든 실수 타입(정수 또는 부동소수점)으로 암시적으로 변환될 수 있습니다. 대상 타입이 _Bool (C23 이전) bool (C23 이후) 인 경우를 제외하고 결과는 항상 양의(또는 부호 없는) 영입니다. 이 경우 부울 변환 규칙이 적용됩니다.

모든 실수 타입의 값은 모든 허수 타입으로 암시적으로 변환될 수 있습니다. 결과는 항상 양의 허수 영입니다.

double imaginary z = 3*I;
bool b = z;  // Boolean conversion: sets b to true 
float f = z; // Real-imaginary conversion: sets f to 0.0 
z = 3.14;    // Imaginary-real conversion: sets z to 0*_Imaginary_I

복소수-허수 변환

모든 허수 타입의 값은 모든 복소수 타입으로 암시적으로 변환될 수 있습니다.

  • 결과의 실수부는 양의 영입니다.
  • 결과의 허수부는 해당 실수 타입에 대한 변환 규칙을 따릅니다.

모든 복소수 타입의 값은 모든 허수 타입으로 암시적으로 변환될 수 있습니다.

  • 실수부는 버려집니다.
  • 결과의 허수부는 해당 실수 타입에 대한 변환 규칙을 따릅니다.
double imaginary z = I * (3*I); // the complex result -3.0+0i loses real part
                                // sets z to 0*_Imaginary_I
(C99 이후)

포인터 변환

객체 타입에 대한 모든 포인터로부터 다음 의미론을 통해 암시적으로 변환될 수 있는 void 포인터:

  • 객체에 대한 포인터가 void 포인터로 변환된 후 다시 원래 포인터로 변환되면, 그 값은 원본 포인터와 동일하게 비교됩니다.
  • 다른 보장은 제공되지 않습니다.
int* p = malloc(10 * sizeof(int)); // malloc은 void*를 반환합니다

비한정 타입에 대한 포인터는 해당 타입의 한정된 버전에 대한 포인터로 암시적으로 변환될 수 있습니다(다시 말해, const , volatile , 그리고 restrict 한정자를 추가할 수 있습니다). 원본 포인터와 결과 포인터는 비교 시 동일합니다.

int n;
const int* p = &n; // &n은 int* 타입을 가짐

값이 0 인 모든 정수 상수 표현식 과 함께, 값이 0인 정수 포인터 표현식을 void * 타입으로 캐스팅한 것은 모든 포인터 타입(객체 포인터와 함수 포인터 모두)으로 암시적으로 변환될 수 있습니다. 결과는 해당 타입의 널 포인터 값이며, 해당 타입의 어떤 널이 아닌 포인터 값과도 비교 시 같지 않음이 보장됩니다. 이러한 정수 또는 void * 표현식을 널 포인터 상수 라고 하며, 표준 라이브러리는 이 상수의 한 정의를 매크로 NULL 로 제공합니다.

int* p = 0;
double* q = NULL;

참고 사항

부호 있는 정수 오버플로는 모든 산술 연산자에서 정의되지 않은 동작이지만, 정수 변환에서 부호 있는 정수 형식의 오버플로는 단순히 지정되지 않은 동작입니다.

반면, 모든 산술 연산자(및 정수 변환)에서 부호 없는 정수 오버플로는 잘 정의된 연산이며 모듈로 연산 규칙을 따르지만, 부동 소수점에서 정수 변환에서 부호 없는 정수 오버플로는 정의되지 않은 동작입니다: 부호 없는 정수로 변환될 수 있는 실수 부동 소수점 타입의 값은 개구간 ( -1 , Unnn_MAX + 1 ) 에 속하는 값들입니다.

unsigned int n = -1.0; // 정의되지 않은 동작

포인터와 정수 간 변환( 포인터에서 _Bool (C23까지) bool (C23부터) 로의 변환과 (C99부터) 값이 0인 정수 상수 표현식에서 포인터로의 변환은 제외), 객체에 대한 포인터 간 변환(한쪽이 void 포인터인 경우 제외), 그리고 함수에 대한 포인터 간 변환(함수들이 호환 가능한 타입을 가진 경우 제외)은 절대 암시적으로 이루어지지 않으며 캐스트 연산자 가 필요합니다.

함수 포인터와 객체 포인터( void * 포함) 또는 정수 간에는 (암시적 또는 명시적) 변환이 존재하지 않습니다.

참고문헌

  • C23 표준 (ISO/IEC 9899:2024):
  • 6.3 변환 (p: 44-50)
  • C17 표준 (ISO/IEC 9899:2018):
  • 6.3 변환 (p: 37-41)
  • C11 표준 (ISO/IEC 9899:2011):
  • 6.3 변환 (p: 50-56)
  • C99 표준 (ISO/IEC 9899:1999):
  • 6.3 변환 (p: 42-48)
  • C89/C90 표준 (ISO/IEC 9899:1990):
  • 3.2 변환

참고 항목

C++ documentation for Implicit conversions