입력을 받아 어떤 기능을 수행한 후 결과를 반환하는 것
특정 작업을 처리하도록 작성한 프로그램 단위
•
반복되는 일정한 작업을 함수로 표현하면 good
<함수의 구분>
1.
라이브러리 함수 : 이미 개발환경에 포함되어있는 함수(printf(), scanf() ... )
2.
사용자 정의 함수 : 필요에 의해 개발자가 직접 만든 함수 → 함수 선언(function declaration), 함수 호출(function call), 함수 정의(function definition)이 필요
함수 선언(정의)와 호출
// 함수 원형 선언시 매개변수 개수와 자료형, 순서가 중요하기 때문에 변수이름은 경우에 따라 생략이 가능함
int add(int a, int b) // 반환형int 함수이름add (매개변수목록 int a, int b)
{
int sum = a + b; // 함수가 수행할 문장들
return (sum); // return (반환값)
}
int sum = add(3, 6); // 함수 호출. return 9
C
복사
•
반환형 : int, float, double 등 다양한 자료형 가능
•
반환값이 없다면 void를 적어주세요
•
return 문장을 통해 반환값을 전달 + 함수 종료
•
반환형이 void이면 return문을 생략 or return만 적어줌
•
매개변수는 변수 뿐 아니라 일/이차원 배열 모두 사용 가능합니다.
◦
한번에 많은 변수를 함수의 매개변수로 전달할 때에는 배열을 사용하면 효과적이겠죠
int summary (int ary[] int SIZE){
...
}
C
복사
int summary (int *ary int SIZE){
...
}
C
복사
void printAry(double ary[][]){
...
}
C
복사
void printAry(double* ary[]){
...
}
C
복사
tip
이차원 배열 ary에는 행과 열이 있죠, 이차원 배열의 행과 열 수 는 다음과 같이 구할 수 있습니다.
int rowSize = sizeof(ary)/sizeof(ary[0]); // 행
int colSize = sizeof(ary[0])/sizeof(ary[[0][0]) // 열
C
복사
재귀함수
함수 내부에서 자신을 다시 호출하는 함수
반드시 끝나는 지점이 존재해야 한다.
재귀함수 계속적 호출시 시간이 오래걸리며 메모리의 사용도 많아짐
함수 실행시에는, 함수 실행이 끝나면 컴파일러는 리턴값은 어디에 저장할 것인지, 어떤 문장을 다음으로 실행할 것인지 등등 많은 정보를 메모리에 저장하기 때문에 함수의 호출이 너무 많아지면 메모리 사용이 너무 많아질 수 있습니다.
⇒ 따라서 재귀함수는 꼭 필요할 때에만 사용해주세요
int function(int n) {
...
....
return function(n-1);
}
[예제 1. 1부터 n까지 출력하는 재귀함수]
#include <stdio.h>
#include <stdlib.h>
// 1부터 n까지 출력하는 재귀함수
void printNum(int);
int main() {
int n = 5;
printNum(n);
return 0;
}
void printNum(int n) {
if (n == 0) { printf("\n"); return; } // 종료조건
printNum(n - 1);
printf("%d ", n);
}
C
복사
라이브러리 함수
C언어에는 사용자가 쉽게 사용 가능한 다양한 라이브러리 함수가 존재합니다.
라이브러리 함수는
•
해당 함수가 정의된 헤더파일을 #include <stdio.h> 와 같이 삽입해주어야하지만
•
따로 선언/정의할 필요는 없습니다
1.
난수 라이브러리 함수 rand()
•
난수를 생성
•
이 함수의 원형은 헤더파일 <stdlib.h>에 정의
•
0~32767사이의 정수 중 임의의 정수 반환
2.
수학 라이브러리 함수
•
헤더파일 < math.h > 를 삽입해 수학관련 함수 사용가능
double sin(double x)
double sqrt(double x)
double log(double x)
int abs(int x)
3.
문자 라이브러리 함수
•
헤더파일 < ctype.h >를 삽입해 문자 관련 함수 사용 가능
int isalpha(char) 알파벳이 맞는지 확인
int isuper(char) 알파벳이 대문자가 맞는지 확인
int isspace(char) 공백 문자인지 확인
int isdigit(char) 숫자인지 확인
함수의 인자 전달 방식
함수의 인자전달 방식에는 값에 의한 호출(call by value)와 참조에 의한 호출(call by reference) 방식이 있음
call by value
함수 호출 시 매개변수 값을 복사해 저장함
따라서 함수 내부에서 외부에 있는 변수의 값을 수정할 수 없다
int function(int a);
call by reference
함수의 매개변수로 포인터를 사용한다.
포인터의 주소를 통해 변수를 직접적으로 참조할 수 있다.
따라서 함수 내부에서 함수 외부에 있는 변수의 값을 수정할 수 있다
int function (int *a)
매개변수로 배열을 사용할 때
매개변수로 배열을 사용 == 배열의 첫 번째 원소를 참조 매개변수(call by reference)로 전달하는 것과 같다!
== 매개변수에 배열형태로 기술해도 포인터 변수로 인식됨
int function(int arr[]); → int function(int *arr);
간접연산자 *로도 배열원소를 참조할 수 있다
int i, sum = 0;
int ary[] = {1, 2, 3, 4, 5};
int *addr = ary;
int size = sizeof(ary)/sizeof(int); // 배열의 크기 계산
C
복사
// 가능
for(i=0; i<size; i++){
sum += *(ary+i);
}
C
복사
// 가능
for(i=0; i<size; i++){
sum += *(addr++);
}
C
복사
// 오류
for(i=0; i<size; i++){
sum += *(ary ++);
}
C
복사
매개변수로 다차원 배열 사용하는 경우
첫번째 대괄호를 제외한 다른 모든 크기는 반드시 기술되어야 합니다.
int sum(int ary[][3], int, int);
int ary[][3] = {{1, 2, 3}, {4, 5, 6}};
int rowsize = sizeof(ary)/sizeof(ary[0]);
int colsize = sizeof(ary[0])/sizeof(ary[0][0]);
C
복사
포인터 전달과 반환
함수의 매개변수와 반환값으로 포인터도 사용 가능
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int* add(int*, int, int); // 매개변수와 반환값이 모두 포인터
int main() {
int m, n, sum;
scanf("%d %d", &m, &n);
printf("%d\n", *add(&sum, m, n));
return 0;
}
int* add(int* sum, int a, int b) {
*sum = a + b;
return sum;
}
C
복사
함수의 반환값으로 구조체 이용
구조체? 구조화된 데이터를 통합적으로 관리
struct student{
int student_number;//학번
char student_name[30];//학생이름
}
struct student change(struct student s){
s.student_number = -s.student_number;
return s;
}
C
복사
const의 사용
const를 통해 정적변수로 선언사면 참조되는 변수는 수정될 수 없습니다.
void sum(int *result, const int *a, const int *b) {
*result = *a + *b; // 가능
*a += 1; // 오류
*b += 1; // 오류
}
C
복사
함수 포인터
함수포인터란 함수의 주소값을 저장하는 포인터변수
함수 포인터 ← 반환형, 매개변수 수, 매개변수 자료형 등이 일치하는 함수의 주소를 저장
즉, 하나의 함수 이름으로 여러 함수를 호출해 사용할 수 있다.
void (*pFun) (int *, int, int);
함수포인터가 원소인 배열 : 함수 포인터 배열
void add(int*, int, int);
void multiply(int*, int, int);
C
복사
void (*pFunAry[2]) (int*, int, int);
pFunAry[0] = add;
pFunAry[1] = multiply;
C
복사
void (*pFunAry[2])(int*, int, int) = {add, multiply};
C
복사
void 포인터?
주소값은 참조의 시작 주소
자료형을 알아야 참조할 범위(int라면 시작주소부터 4바이트겠죠?)를 알고, 그 값을 해석할 수 있는데
void포인터는 자료형 고려 없이 주소값만을 다루는 포인터
즉, void포인터는 자료형의 종류에 상관 없이 주소 저장이 가능
•
void포인터는 일반 변수 포인터 뿐만 아니라 배열, 구조체, 함수 주소도 저장할 수 있다.
char ch = 'A';
int num = 1;
double PI = 3.13;
void *vp; // void 포인터 변수 vp 선언
vp = &ch;
vp = #
vp = &PI;
C
복사
•
그러나, void 포인터가 가리키는 변수를 참조 or 수정 할수는 없다
•
void 포인터로 변수를 참조하려면 자료형 변환을 해주어야 한다.
int num = 5;
double x = 1.3;
void *p = #
n = *p; // 오류
int n = *(int*)p; // int*로 변환해주어야함
p = &x;
m = *p; // 오류
int m = *(double *)p; // double *로 변환해주어야함
C
복사