C언어/C언어 심화 & 특수

[C99] Compound Literals, C에서의 이름없는 임시 객체

카루-R 2020. 3. 21. 10:37
반응형

C++11에서 이름없는 임시 객체 (그리고 이동시맨틱) 때문에 머리가 많이 터졌었죠. 그런데 C에서 비슷한 느낌의 Compound Literals라는 걸 만들었습니다. "한 번만 쓰고 버리는" 변수인데, 배열/구조체/공용체 등에서 사용 가능합니다. 이름없는 임시객체와 비슷한 느낌입니다.

(타입){초기화 리스트}

이렇게 사용합니다. 타입에는 배열/구조체/공용체가, 초기화 리스트는 일반 배열 사용하듯이 쓰면 됩니다.

C++에서 int(5)이렇게 작성하면 5가 담긴 int형 임시 객체를 생성하는 것 처럼 이것도 마찬가지입니다.

(int[]){1, 2, 3, 4, 5} 라고 쓰면 1, 2, 3, 4, 5가 담긴 int형 배열을 만들어냅니다.

C++에서는 클래스를 만들고 함수를 호출할 때 이동 시맨틱을 통해

func(MyClass{1, 2, 3}); 이런식으로 임시객체를 만들어 호출할 수 있었습니다.

하지만 C에서는 그게 불가능하고 미리 선언을 한 후 포인터를 넘겼어야 했죠.

이제 가능합니다.

#include <stdio.h>

typedef struct {
    char name[20];
    int phone;
} Friend;

void print_friend(const Friend* frd) {
    printf("My friend's name is %s.\n", frd->name);
    printf("Their phone number: %d.\n", frd->phone);
}

int main(void)
{
    print_friend(&(Friend){"Kim Jeong En", 12345678});
}

print_friend를 자세히 보시면 Friend타입의 {"KJE", 12345678}의 값을 갖는 임시객체를 만들고,

그 임시객체의 주소를 넘겼습니다. 이렇게 하면 함수 호출 시에도 값이 모두 복사되는 사태를 막을 수 있지요.

함수 내부에서는 그 포인터를 받아 임시객체에 접근하여 값을 출력합니다.

My friend's name is Kim Jeong En.
Their phone number: 12345678.

물론 이렇게 designated initializer을 이용한 생성도 가능합니다.

print_friend(&((Friend){ .phone = 12345678, .name = "Kim il seong"}));

임시객체는 어딘가에 이미 할당되었기 때문에 그걸 가져오려면 참조를 이용해야 합니다. C에서는 포인터라 부르죠.

// C++
auto&& frd_cpp = Friend{"MSG", 1234};

// C
Friend* frd_c = &(Friend){"MSG", 1234};

C++에서는 r-value 참조를 이용하였지만 C에서는 포인터를 사용합니다.

일반 포인터 변수 생성하듯이 (){}꼴 앞에 &만 붙여주면 됩니다.

예제를 하나 더 볼까요?

#include <stdio.h>

int func(int* p) {
    printf("%d\n", p[0]);
    0[p] = 1000;
    return p[0];
}

int main() {
    int result = func((int[]){1, 2, 3, 4, 5});
    printf("%d\n", result);
}

배열을 선언하지 않고 즉석에서 임시객체로 만들어 넘기는 모습입니다. func에서 {1, 2, 3, 4, 5}를 넘기고 그것의 첫번째 요소를 출력하고, 1000을 대입하여 반환한 값을 다시 출력하죠.

1
1000

이처럼 Compound Literals를 사용하면 구조체나 공용체, 배열등을 함수로 넘길 때 미리 선언할 필요 없이 함수에 바로 전달할 수 있습니다.

반응형