본문 바로가기

Books/열혈강의 C 프로그래밍

[열혈C] 05. Part3 Chapter19 함수 포인터와 void 포인터


문법적인 이해에 돕는데 초점을 둘 것이다.
앞으로 공부해야 할 보다 수준 있는 프로그래밍 분야(알고리즘, 네트워크 프로그래밍, 시스템 프로그래밍, win32 프로그래밍 등)를 접하면서 모든 문법적 요소의 필요성을 스스로 느끼게 될 것이다. 여기서 소개하는 함수 포인터와 void 포인터 또한 그렇다!

19장 내용 정리

함수 포인터

프로그래머가 정의한 모든 함수는 프로그램 실행 시 메인 메모리(main memory)에 올라가게 된다. 메모리 상에 올라간 다음에야 실행이 가능하기 때문이다. 이 때 함수의 이름은 메모리상에 존재하는 함수의 위치를 가리키는 주소 값을 의미한다. 즉 "함수의 이름은 메모리상에 존재하는 함수의 위치를 가리키는 포인터"이다.


함수의 이름이 주소 값을 지니는 포인터라면, 함수의 이름과 동일한 타입의 포인터 변수를 선언해서 그 값을 저장하는 포인터를 가리켜 함수 포인터라 한다.


함수 포인터의 포인터 타입

함수 이름의 포인터 타입을 결정짓는 요소는 리턴형전달인자다.

1
2
3
4
5
6
7
8
9
10
11
12
13
// 함수 fct1은 "리턴형이 int이고 전달인자가 int형 변수 하나다."
int fct1(int a)
{
    a++
    return a;
}
 
// 함수 fct2는 "리턴형이 double이고 double형 인자를 두 개 전달 받는 포인터 타입이다."
double fct2(double a, double b)
{
    double add=a+b;
    return add;
}
cs



적절한 함수 포인터의 선언

함수 포인터는 우리가 정의한 함수를 운영체제(윈도우즈, 리눅스)에게 알려 줄 때 유용하게 사용되는 개념이다. 우리가 정의한 함수의 이름을 운영체제에 전달하면 운영체제는 전달된 함수 이름을 이용해서 우리를 대신해 함수를 호출해 준다.


함수 포인터 선언의 두가지 예

1
2
3
4
5
// 함수 포인터 fPtr1은 리턴형이 int이고 int형 인자 1개를 전달받는 모든 함수를 가리킬 수 있는 포인터
int (*fPtr1)(int);
 
// 함수 포인터 fPtr2는 리턴형이 void이고 int형 인자 2개를 전달 받는 모든 함수를 가리킬 수 있는 
void (*fPtr2)(intint);
cs


예제1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <stdio.h>
 
void Add(int a, int b);
void SPrint(char * str);
 
int main(void)
{
    char *string = "Function Pointer";
    int a = 10, b = 20;
 
    // 함수 포인터를 선언과 동시에 초기화.
    void (*fPtr1)(intint= Add;     // fPtr1은 함수 이름 Add가 지니는 값과 같은 값을 지니게 되었다. 차이점(fPtr1은 변수/Add는 상수)
    void (*fPtr2)(char *= SPrint;    // fPtr2는 SPrint와 같은 값을 지니게 된다.
 
    // 함수 포인터에 의한 호출 - 함수의 이름이 지니는 값과 같은 값을 지니기 때문에 당연히 호출 가능하다.
    fPtr1(a, b);
    fPtr2(string);
 
    return 0;
}
 
void Add(int a, int b)
{
    printf("덧셈 결과 : %d\n", a + b);
}
 
void SPrint(char * str)
{
    printf("입력된 문자열 : %s \n", str);
}
 
/*
# 실행 결과
덧셈 결과 : 30
입력된 문자열 : Function Pointer
*/
cs



void형 포인터

void 포인터는 뭐든 담을 수 있는 바구니에 비유할 수 있다. void 포인터 변수를 선언하게 되면 "변수", "함수", "포인터의 주소 값"까지도 저장이 가능하다.

단점은 void 포인터를 가지고는 아무런 일도 하지 못한다. 포인터 자체에 데이터에 대한 정보(몇 byte를 읽어 들어야 하는지)가 없기 때문이다.

1
2
3
4
5
6
7
    char c = 'a';
    int n = 10;
 
    // void 포인터를 선언하는 방법과 대입하는 방법
    void *vp;
    vp = &c;
    vp = &n;
cs


void 포인터는 결국 명시적 형 변환 과정을 거쳐서 사용하게 된다. "일단 주소 값을 저장해 두고, 포인터의 타입은 나중에 결정한다"는 전략을 세울 때 필요한 포인터로서 25장에서 언급하는 '메모리의 동적 할당'에서 유용하게 사용된다.

1
2
3
4
5
6
7
8
9
10
11
    int n = 10;
 
    // void 포인터 선언 및 초기화
    void *vp = &n;
 
    // 오류 발생 : void 포인터는 주소 값을 저장하는 것 이외에 아무것도 할 수 없기 때문이다.
    *vp = 20;        // Error!
    vp++;            // Error!
 
    // 명시적 형 변환 과정으로 void 포인터 사용 
    *((int*)vp) = 20;
cs



main 함수를 통한 인자의 전달

매개 변수 선언에서 선언 char **argv와 선언 char * argv[]는 완전히 같은 것이다.


예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
 
int main(int argc, char **argv)
{
    int i = 0;
    printf("전달된 문자열의 수 : %d \n", argc);
 
    for (i = 0; i < argc; i++)
        printf("%d번째 문자열 : %s \n", i + 1, argv[i]);
    
    return 0;
}
 
/*
# 실행 결과
C:\Users\Jenny\Documents\Visual Studio 2015\Projects\testc\Release
λ testc abcd 1234
전달된 문자열의 수 : 3
1번째 문자열 : testc
2번째 문자열 : abcd
3번째 문자열 : 1234
*/
cs


인자의 형성 과정

위 코드의 실행 결과를 보자. "testc abcd 1234" 명령문을 전달하고 있다.

testc는 실행시킬 .exe 파일을 지정해 주는 것이고, abcd와 1234는 main 함수로 전달되는 인자를 지정해 주는 것이다.


명령 프롬프트상에서 내려진 명령문을 참조하여 문자열을 형성한다. 공백 기준으로 나뉘게 되므로, 총 3개의 문자열을 형성한다.

문자열을 담을 char형 포인터 배열(char* arr[3])을 형성한다. main 함수의 두 번째 인자로 전달이 된다.

main 함수의 첫 번째 인자로 전달되는 것은 문자열의 개수다.


사용자가 입력한 문자열의 개수가 적절치 않을 경우의 프로그램 처리

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdio.h>
 
int main(int argc, char **argv)
{
    if (argc != 3)
    {
        printf("사용 방법 : testc 이름 주민번호 \n");
        return 1;
    }
    printf("이름 : %s \n", argv[1]);
    printf("주민번호 : %s \n", argv[2]);
    return 0;
}
 
/*
# 실행 결과1
C:\Users\Jenny\Documents\Visual Studio 2015\Projects\testc\Release
λ ctestc 홍 길 동 840123-1234567
사용 방법 : testc 이름 주민번호
# 실행 결과2
C:\Users\Jenny\Documents\Visual Studio 2015\Projects\testc\Release
λ testc "홍 길 동" 840123-1234567
이름 : 홍 길 동
주민번호 : 840123-1234567
*/
cs




열혈강의 C 프로그래밍
국내도서
저자 : 윤성우
출판 : 프리렉 2003.12.15
상세보기