1. 함수의 인자 전달 방식
- 값에 의한 호출과 참조에 의한 호출 방식
1. 함수에서 값의 전달
C언어는 함수의 인자 전달 방식이 기본적으로 값에 의한 호출(call by value) 방식
Call by value (값에 의한 호출) : 인자로 받은 값을 복사하여 처리
복사하여 처리하기 인자로 받은 값이 보존 / 복사를 하기 때문에 메모리 사용량이 늘어남
2. 함수에서 주소의 전달
C언어에서 포인터를 매개변수로 사용하면 함수로 전달된 실인자의 주소를 이용하여 그 변수를 참조할 수 있다. 이와 같이 함수에서 주소를 전달해 호출하는 방식을 주소에 의한 호출 (call by address)
[call by address = call by reference]
Call by reference (참조에 의한 호출) : 인자로 받은 값의 주소를 참조하여 직접 해당 값에 영향을 줌
복사하지 않고 직접 참조 하기에 빠름 / 직접 참조를 하기 때문에 원래 값이 영향을 받음.
- 함수에서 인자와 반환값으로 배열을 주고 받는 활용
함수의 매개변수로 배열을 전달하는 것은 배열의 첫 원소의 주소를 매개변수로 전달하는 주소의 의한 호출과 같음.
함수 내부에서 실인자로 전달된 배열의 배열크기를 알 수 없음. 그렇기에 배열크기도 하나의 인자로 사용해야 함.
- 가변인자의 필요성과 사용방법
가변인자 (variable argument) : 함수에서 인자의 수와 자료형이 결정되지 않은 함수 인자 방식
가변인자가 있는 함수 구현
가변인자를 구현하려면 가변인자 선언, 가변인자 처리 시작, 가변인자 얻기, 가변인자 처리 종료 4단계가 필요
+ 헤더파일 stdarg.h가 필요
1. 가변인자 선언 : 변수 선언 처럼 가변인자로 처리할 변수를 하나 만드는 일 (va_list)
2. 가변인자 처리 시작 : 위에서 선언된 변수에서 마지막 고정 인자를 지정해 가변인자의 시작 위치를 알리는 방법 ( va_start() )
3. 가변인자 얻기 : 가변인자 각각의 자료형을 지정하여 가변인자를 반환 받는 절차
(va_arg()의 호출로 반환된 인자로 원하는 연산을 처리)
4. 가변인자 종료 : 가변인자에 대한 처리를 끝내는 단계 ( va_end() )
#include <stdio.h>
#include <stdarg.h>
int sum(int numagrs, ...)
{
// 가변인자 선언
// va_list 가변인자변수;
va_list argp;
// 가변인자 처리시작
// va_start(가변인자변수, 가변인자_이전_첫_고정인자)
va_start(argp, numagrs);
// 가변인자 얻기
// va_arg(가변인자변수, 반환될_자료형);
int total += va_arg(argp, int);
// 가변인자 처리종료
// va_end(가변인자변수);
va_end(argp);
}
#include <stdio.h>
#include <stdarg.h> // 가변인자를 위한 헤더파일
double avg(int, ...); // int 이후는 가변인자
int main(void)
{
printf("평균 %.2f\n", avg(5, 1.2, 2.1, 3.6, 4.3, 5.8));
printf("평균 %.2f\n", avg(6, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0));
return 0;
}
// 가변인자 ... 시작 전 첫 고정 매개변수는 가변인자를 처리하는데 필요한 정보 지정
double avg(int numagrs, ...) // 매개변수 numargs 는 가변인자 수를 제공
{
double total = 0;
va_list argp; // 1. 가변인자 변수 선언
va_start(argp, numagrs); // 2. numargs 이후의 가변인자 처리 시작
for (int i = 0; i < numagrs; i++) // 3. 가변인자 얻기
{
total += va_arg(argp, double); // 지정하는 double 형으로 가변인자 하나를 반환
}
va_end(argp); // 4. 가변인자 처리종료
return total / numagrs;
}
/*
평균 3.40
평균 3.50
*/
2. 포인터 전달과 반환
- 매개변수 전달과 반환으로 포인터 사용
함수에서 매개변수를 포인터로 사용하면 결국 주소에 의한 호출 (Call by address)
#include <stdio.h>
void add(int*, int, int);
int main(void)
{
int m = 0, n = 0, sum = 0;
printf("두 정수 입력: ");
scanf("%d %d", &m, &n);
add(&sum, m, n);
printf("두 정수 합: %d\n", sum);
return 0;
}
// call by address
void add(int* psum, int a, int b)
{
*psum = a + b;
}
/*
두 정수 입력: 15 85
두 정수 합: 100
*/
- 포인터 인자전달 시 키워드 const의 이용
수정을 원하지 않는 함수의 인자 앞에 키워드 const를 삽입
키워드 const는 인자인 포인터 변수가 가리키는 내용을 수정할 수 없도록 한다.
- 함수에서 구조체 전달과 반환
#include <stdio.h>
typedef struct complex // 복소수 구현 구조체
{
double real; // 실수
double img; // 허수
}complex;
void printcomplex(complex com);
complex pcomplexvalue(complex com);
void pcomplexaddress(complex* com);
int main(void)
{
complex comp = {5.8, 7.2}; // 복소수 선언
complex pcomp;
printcomplex(comp);
pcomp = pcomplexvalue(comp);
printcomplex(pcomp);
pcomplexaddress(&pcomp);
printcomplex(pcomp);
return 0;
}
// 복소수 출력 함수
void printcomplex(complex com)
{
printf("복소수 = %5.1f + %5.1fi \n", com.real, com.img);
}
// 켤레 복소수 반환 함수 (call by value)
complex pcomplexvalue(complex com)
{
com.img = -com.img;
return com;
}
// 복소수 매개변수를 Call by address로 켤레 복소수로 바꾸는 함수
void pcomplexaddress(complex* com)
{
com->img = -com->img;
}
/*
복소수 = 5.8 + 7.2i
복소수 = 5.8 + -7.2i
복소수 = 5.8 + 7.2i
*/
3. 함수 포인터와 void 포인터
- 함수 포인터의 필요성과 사용
함수 포인터 (pointer to function) : 함수의 주소값을 저장하는 포인터 변수 / 함수를 가리키는 포인터
- 함수 포인터 배열의 사용
함수 포인터 배열 (array of function pointer) : 함수 포인터가 원소인 배열
#include <stdio.h>
void add(double*, double, double);
void subtract(double*, double, double);
void multiply(double*, double, double);
void devide(double*, double, double);
int main(void)
{
char op[4] = {'+', '-', '*', '/'};
void (*fpary[4])(double*, double, double) = {add, subtract, multiply, devide};
double m, n, result;
printf("사칙연산을 수행할 실수 2개를 입력 >>> ");
scanf("%lf %lf", &m, &n);
for (int i = 0; i < 4; i++)
{
fpary[i](&result, m, n);
printf("%.2lf %c %.2lf == %.2lf\n", m, op[i], n, result);
}
return 0;
}
void add(double* z, double x, double y)
{
*z = x + y;
}
void subtract(double* z, double x, double y)
{
*z = x - y;
}
void multiply(double* z, double x, double y)
{
*z = x * y;
}
void devide(double* z, double x, double y)
{
*z = x / y;
}
/*
사칙연산을 수행할 실수 2개를 입력 >>> 150 15
150.00 + 15.00 == 165.00
150.00 - 15.00 == 135.00
150.00 * 15.00 == 2250.00
150.00 / 15.00 == 10.00
*/
- void 포인터의 필요성과 사용
주소값이란 참조를 시작하는 주소에 불과하며 자료형을 알아야 참조할 범위와 내용을 해석할 방법을 알 수 있는 것
void 포인터는 자료형을 무시하고 주소값만을 다루는 포인터
=> void 포인터는 대상에 상관없이 모든 자료형의 주소를 저장할 수 있는 만능 포인터로 사용할 수 있음
char ch = 'A';
int data = 5;
double value = 34.76;
void *vp;
vp = &ch; // ch의 주소만을 저장
vp = &data; // data의 주소만을 저장
vp = &value // value의 주소만을 저장
void 포인터는 모든 주소를 저장할 수 있지만 가리키는 변수를 참조하거나 수정이 불가능하다.
void 포인터는 자료형 정보는 없이 임시로 주소만을 저장하는 포인터이다.
void 포인터로 변수를 참조하기 위해서는 자료형 변환이 필요하다.
'C Programming' 카테고리의 다른 글
[C언어로 배우는 프로그래밍 기초 Perfect 3판] Chapter 10. 변수 유효범위 프로그래밍 연습 (0) | 2022.07.07 |
---|---|
[C언어로 배우는 프로그래밍 기초 Perfect 3판] Chapter 10. 변수 유효범위 (0) | 2022.07.06 |
[C언어로 배우는 프로그래밍 기초 Perfect 3판] Chapter 13. 구조체와 공용체 - 프로그래밍 연습 (0) | 2022.07.04 |
[C언어로 배우는 프로그래밍 기초 Perfect 3판] Chapter 13. 구조체와 공용체 (0) | 2022.07.04 |
[C언어로 배우는 프로그래밍 기초 Perfect 3판] Chapter 08 프로그래밍 연습 (0) | 2022.06.29 |