포인터 붕괴

KWANHO PARK·2025년 10월 20일
  • 아래는 printf() 함수 포인터를 만든 코드다.
// funcPointer.c
#include <stdio.h>

int main(void)
{
    int (*fp)(const char *format, ...);

    fp = printf;

    fp("printf()'s function pointer\n");

    int num = 123;
    fp("num : %d\n", num);
    int num1 = 456;
    fp("num : %d, num1 : %d\n", num, num1);

    int char_count = fp("Character Count\n");
    fp("Count : %d\n", char_count);

    return 0;
}
  • 흔히 함수명은 함수의 주소라고 알고 있다. 그래서 * 없이 그냥 fp = printf; 이렇게 주소를 할당할 수 있다.

궁금증

  • 그렇다면 *fp = printf; 이렇게 하면 어떻게 될까?
  • fp = &printf;는?
  • *fp = &printf;는?

일단 어셈블리 코드를 한 번 확인해보자

  • 일단 궁금한 코드 추가 후 컴파일
// funcPointer.c
#include <stdio.h>

int main(void)
{
    int (*fp)(const char *format, ...);

    fp = printf;
	*fp = printf;
	fp = &printf;
	*fp = &printf;
    
    .
    .
	.
    
    return 0;
}
  • funcPointer.asm 코드

결론 : 모두 똑같다

이유가 뭘까?

포인터 붕괴

  • 함수명은 함수의 주소라고 알고 있는데, 엄밀하게는 컴파일 단계에서 함수명이 포인터로 암시적 형변환(implicit conversion)이 일어난 것이다. 이를 포인터 붕괴(decay)라고 한다.

예외

  • & (주소 연산자): &printf처럼 함수 이름이 주소 연산자의 피연산자로 쓰일 때. 이때는 당연히 주소 값을 구해야 하므로 붕괴되지 않고, 결과적으로는 붕괴된 것과 같은 주소 값을 내놓음. 즉 fp = &printf;에서는 printf가 포인터로 붕괴되는게 아니라 printf의 주소를 직접 가져와서 fp = printf;와 동일한 결과가 나오게 되는 것

  • sizeof 연산자: C언어 표준은 함수라는 대상 자체에는 sizeof 연산자를 사용할 수 없다고 명시하고 있음. 즉, sizeof(printf)처럼 함수 이름에 sizeof를 사용하는 것은 불가능. sizeof를 쓸 때 printf라는 이름은 포인터로 자동 변환되지 않고 함수 그 자체로 남음. 따라서 함수의 시작 주소를 가져오게끔 sizeof(&printf)라고 써야함

궁금증2

  • *fp("function pointer call\n"); 이렇게 호출해도 될까?

  • 코드를 추가하고 컴파일해보자

// funcPointer.c
#include <stdio.h>

int main(void)
{
    int (*fp)(const char *format, ...);

    fp = printf;
    
    .
    .
	.
    
    *fp("function pointer call\n");
    
    return 0;
}
  • 아래에 보이듯이 에러가 뜬다
C:\Users\User\Desktop\Xpanner\Firmware\Source\Test>cl funcPointer.c
x86용 Microsoft (R) C/C++ 최적화 컴파일러 버전 19.44.35217
Copyright (c) Microsoft Corporation. All rights reserved.

funcPointer.c
funcPointer.c(25): error C2100: 'int' 형식의 피연산자를 역참조할 수 없습니다.

이유

  • printf()는 함수에 전달된 글자 개수를 반환한다.
  • *fp("function pointer call\n"); 을 하면 "function pointer call\n"의 글자 개수에 포인터 연산을 하라는 뜻
  • 그래서 error C2100: 'int' 형식의 피연산자를 역참조할 수 없습니다. 이런 에러가 뜬다.

해결 방법

(*fp)("function pointer call\n"); 이렇게 *fp를 괄호로 감싸서 먼저 fp가 가리키는 함수를 불러오도록 하면 printf("function pointer call\n");와 동일하게 동작할 수 있다.

0개의 댓글