Skip to Main Content

C언어 기초 가이드 STEP 2: 포인터&함수

코딩 학습 가이드는 소프트웨어학과 송오영교수님이 검수하였습니다.

인수 전달 방법

인자 전달의 기본방식은 값의 '복사'입니다. 값을 복사해서 인수로 전달하면 호출하는 함수의 값은 바뀌지 않습니다.
함수를 호출할 때 단순히 값을 복사해서 전달하는 함수호출을 값에 의한 전달(Call-by-value)이라고 합니다.

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
 
void add(int a);    // 함수 선언
 
int main(void)
{
    int num = 10;
 
    add(num);    // 함수 호출
    printf("num : %d\n", num);
 
    return 0;
}
 
void add(int a)    // 함수 정의
{
    a = a + 5;
}

실행결과

num : 10


함수가 반환된 이후에도 호출한 함수에 있는 변수의 값은 변하지 않았습니다. 왜냐하면 매개변수 a의 메모리 주소에 저장된 값을 main 함수로 넘겨주지 않았기 때문입니다.

참조에 의한 전달은 인수로 변수의 값을 전달하는 것이 아닌, 주소값을 전달합니다.
주소값을 전달하는 함수호출을 참조에 의한 전달(Call-by-reference)이라고 합니다.
이 방법은 인수로 전달된 변수의 값을 함수 내에서 변경할 수 있습니다.

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
 
void add(int *a);    // 함수 선언
 
int main(void)
{
    int num = 10;
 
    add(&num);    // 함수 호출
    printf("num : %d\n", num);
 
    return 0;
}
 
void add(int *a)    // 함수 정의
{
    *= *+ 5;
}

실행결과

num : 15


변수의 주소값을 인자로 받아 해당 변수에 직접 접근합니다. add 함수의 매개변수인 a는 num을 가리키고, *a는 num을 의미합니다.
따라서 main 함수에 있는 num을 사용할 수 있으며 그 값을 바꾸는 것도 가능합니다.

배열을 함수인자로 전달하기

배열을 통째로 전달하는 방법은 없습니다. C언어는 매개변수로 배열이 선언되는 것을 허용하지 않아서 함수의 인자로 배열을 전달하지 못합니다.
배열을 통째로 전달하지 않는 대신 배열의 주소값을 전달할 수 있습니다.
앞서 배열의 이름은 포인터라고 했습니다. 배열의 이름이 배열의 첫번째 원소를 가리키는 포인터로 사용할 수 있습니다.

함수에서 배열을 매개변수로 사용하려면 ( ) (괄호) 안에서 매개변수 이름 뒤에 [ ] (대괄호)를 붙이거나 매개변수를 *(포인터)로 지정해줍니다.

형식

반환형 함수이름(자료형 매개변수[])
{

}

반환형 함수이름(자료형 *매개변수)
{

}

 
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>
 
void arrprintf(int *arg);
 
int main(void)
{
    int arr[3= { 102030 };
    
    arrprintf(arr);    // 배열의 이름이 지니고 있는 주소값 전달
 
    return 0;
}
 
void arrprintf(int *arg)
{
    int i;
 
    for (i = 0; i < 3; i++)
        printf("%d ", arg[i]);    // arg라는 이름을 배열의 이름처럼 사용
 
    printf("\n");
}

실행결과

10 20 30
 

void arrprintf(int *arg)

void arrprintf(int arg[])

int *arg와 int arg[]는 동일한 선언입니다.

int arg[]가 배열이 인자로 전달된다는 느낌을 더 강하게 줍니다.
따라서 배열의 주소값이 인자로 전달될 때는 int arg[] 형태의 선언을 주로 사용합니다.

함수 포인터

배열의 이름이 배열의 시작 주소값을 의미하듯, 함수 이름함수가 정의되어 있는 메모리의 시작 위치입니다.
함수 이름도 포인터이므로 printf에서 출력해보면 메모리 주소가 나옵니다.

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
 
void hello(void);
 
int main(void)
{
    printf("%p\n", hello);
 
    return 0;
}
 
void hello(void)
{
    printf("Hello, world!\n");
}

실행결과

008613BB

함수 포인터 변수를 선언하는 방법은 다음과 같습니다.

반환값자료형 (*함수포인터이름) (매개변수 자료형)
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
 
int add(int num1, int num2);
 
int main(void)
{
    int (*fptr)(intint);    // int형 반환값, int형 매개변수 두 개가 있는 함수 포인터 fptr 선언
 
    fptr = add;                      // add 함수의 메모리 주소를 함수 포인터 fptr에 저장
    printf("%d\n", fptr(1020));    // 함수 포인터로 add 함수 호출
 
    return 0;
}
 
int add(int num1, int num2)    // int형 반환값, int형 매개변수 두 개
{
    return num1 + num2;
}

실행결과

30

 

함수가 반환형이 int형이고 매개변수로 int형 변수가 두 개 선언되었으니, 이 함수의 주소 값 저장을 위한 함수 포인터 변수는 다음과 같이 선언합니다.

 

 굳이 함수 포인터를 쓰지 않아도 함수를 직접 호출하여 같은 기능을 하는 코드를 만들 수 있습니다.
       하지만 함수 포인터는 함수를 따로 만들고, 나중에 함수를 호출하는 곳에서 연산 방법을 함수로 구현할 때 쓸 수 있습니다.
       또한 하나의 프로그램이 여러 개의 파일로 분리되어 있을 때 다른 파일에 있는 정적 함수를 호출하는 방법으로 함수 포인터를 쓸 수도 있습니다.