[C언어] 15. 함수와 포인터 활용
이번에는 C언어의 함수와 포인터 활용에 대해 알아보자.
함수의 인자전달 방식
값에 의한 호출과 참조에 의한 호출
1) 함수에서 값의 전달
- 값에 의한 호출(call by value)
- 함수 호출 시 실인자의 값이 형식인자에 복사되어 저장
- 함수 외부의 변수를 함수 내부에서 수정할 수 없음
2) 함수에서 주소의 전달
- 주소에 의한 호출(call by address)
- 함수에서 주소를 전달해 호출하는 방식
- 포인터를 매개변수로 사용하면 함수로 전달된 실인자의 주소를 이용해 해당 변수 참조 가능
배열의 전달
1) 배열의 전달
- 함수의 매개변수로 배열 전달
- = 배열의 첫 원소의 주소를 매개변수로 전달(주소에 의한 호출)
- 함수 내부에서 실인자로 전달된 배열의 배열크기를 알 수 없음(포인터 변수처럼 인식)
//함수원형
double sum(double [], int i); //배열 이름 생략 가능
double sum(double *, int i); //포인터 이름 생략 가능
2) 다양한 배열원소 참조 방법
int sum = 0;
int point[] = {95, 88. 76, 54, 85, 33};
int *address = point;
int aryLength = sizeof(point) / sizeof(int);
//배열 출력
for(int i = 0; i < aryLength; i++)
sum += *(point+i);
for(int i = 0; i < aryLength; i++)
sum += *(address++);
for(int i = 0; i < aryLength; i++)
//error: point(배열이름)는 주소 상수
//sum += *(point++);
가변인자
1) 가변인자가 있는 함수머리
...
: 가변인자 매개변수//printf() 함수원형 int printf(const char *_Format, ...); ///... : 가변인자 매개변수
- 가변인자(variable argument) : 함수에서 인자의 수와 자료형이 결정되지 않은 함수 인자 방식
- 처음(앞부분)의 매개변수는 정해져 있고, 중간 이후부터 마지막에 위치한 가변인자만 가능
...
첫 고정 매개변수 : 가변인자를 처리하는 데 필요한 정보를 지정
2) 가변인자가 있는 함수 구현
- 헤더파일
stdadrg.h
필요 - 4단계 : 가변인자 선언 > 가변인자 처리 시작 > 가변인자 얻기 > 가변인자 처리 종료
- 가변인자 선언
- 가변인자 처리 시작
- 가변인자 얻기
- 가변인자 처리 종료
int sum(int numagrs, ...)
{
//1. 가변인자 선언
va_list argp; //va_list로 변수 argp 선언
//2. 가변인자 처리 시작
va_start(argp, numagrs);
//3. 가변인자 얻기
...
total += va_arg(argp, int);
...
//4. 가변인자 처리 종료
va_end(argp);
};
포인터 전달과 반환
함수 매개변수와 반환으로 포인터 사용
1) 주소연산자 &
- 함수에서 매개변수를 포인터로 이용 : 주소에 의한 호출(call by address)
2) 주소값 반환
- 함수의 결과를 포인터로 반환
- 지역변수 주소값의 반환믄 문제가 발생할 수 있음
int *add(int *psum, int a, int b)
{
*psum = a + b;
return psum;
}
상수를 위한 const 사용
1) 키워드 const
- 수정을 원하지 않는 함수의 인자 앞에 키워드
const
삽입 const
의 위치
함수의 구조체 전달과 반환
1) 복소수를 위한 구조체
struct complex
{
double real; //실수
double img; //상수
};
typedef struct complex complex;
2) 인자와 반환형으로 구조체 사용
- 구조체도 자료형이므로 함수의 인자와 반환 값으로 이용 가능
함수 포인터와 void 포인터
함수 포인터
1) 함수 주소 저장 변수
- 포인터 : 하나의 포인터로 여러 변수를 참조하여 읽거나 쓰기 가능
- 함수 포인터(pointer to function) : 함수의 주소값을 저장하는 포인터 변수
- 하나의 함수이름으로 필요에 따라 여러 함수를 사용가능
- 반환형, 인자목록의 수와 각각의 자료형이 일치하는 함수의 주소를 저장할 수 있는 변수
- 선언 시 함수원형에서 함수이름을 제외한 반환형과 인자목록의 정보 필요
//함수 포인터 변수 선언
반환자료형 (*함수 포인터변수이름)(자료형1 매개변수명1, 자료형2 매개변수명2, ...);
반환자료형 (*함수 포인터변수이름)(자료형1, 자료형2, ...);
void add(double*, double, double);
//이름이 pf1인 함수 포인터 선언, add의 주소값 대입
//대입시 add, &add 가능, add() 불가능
void (*pf1)(double *z, double x, double y) = add;
2) 함수 포인터를 이용한 함수 호출
함수 포인터 배열
1) 함수 포인터 배열 개념
- 함수포인터 배열(array of function pointer) : 함수 포인터가 원소인 배열
int (*pfunary[3])(int, int);
//배열 pfunary가 가리키는 함수의 반환값은 int, 인자목록이 (int, int)
2) 함수 포인터 배열 선언
반환자료형 (*배열이름[배열크기])(자료형1 매개변수명1, 자료형2 매개변수명2, ...);
반환자료형 (*배열이름[배열크기])(자료형1, 자료형2, ...);
void add(double*, double, double);
void subtract(double*, double, double);
void multiply(double*, double, double);
void devide(double*, double, double);
//1. 함수 포인터 배열 선언 후 초기화
void(*fpary[4])(double*, double, double);
fpary[0] = add;
fpary[1] = subtract;
fpary[2] = multiply;
fpary[3] = devide;
//2. 함수 포인터 배열 선언과 초기화를 동시에
void void(*fpary[4])(double*, double, double) = {add, subtract, multiply, devide};
void 포인터
1) void 포인터 개념
- 포인터 : 가리키는 대상의 구제적인 자료형의 포인터로 사용하는 것이 일반적
- 주소값 : 참조를 시작하는 주소에 불과, 자료형을 알아야 참조할 범위와 내용 해석 가능
- void 포인터
(void *)
- 자료형을 무시하고 주소값만을 다루는 포인터
- 대상에 상관없이 모든 자료형(일반변수, 배열, 구조체, 함수 등)의 주소를 저장 가능
- 가리키는 변수를 참조하거나 수정이 불가능
- 참조를 위해선 자료형 변환이 필요
int m = 10;
double x = 3.98;
void *p = &m;
int n = *(int *)p; //int *로 변환
n = *p //오류
p = &x;
int y = *(double *)p; //double *로 변환
y = *p; //오류