본문 바로가기

C

12주차 수업내용

argument/parameter, 리턴값

함수

특정한 작업을 하도록 만들어진 독립적인 단위 모듈

큰 프로그램 하나를 여러 개의 함수로 분할하여 구현하는 구조적 프로그래밍(structured programming) 방식의 기본

 

전달인자(argument) : 함수에 입력되는 것

매개변수, 파라미터 (parameter) : 받은 전달인자

반환값, 리턴값(return value) : 함수에서 출력되는 것

 

 

C 프로그램의 기본 틀

콘솔 기반 C 프로그램은 main() 함수로 시작

  • main()함수는 콘솔 기반 C 프로그램의 시작점(entry point)
  • 하나의 프로그램에 반드시 하나 존재

리턴값이 없는 함수나 매개변수가 없는 함수는 그 위치에 비어 있다는 뜻의 "void"를 씀

매개변수 리스트에 쓰는 "void"는 생략할 수 있지만 리턴형에 쓰는 "void"는 생략 불가

 

리턴값 없음 : 상수 앞 void

매개변수 없음 : 괄호 안 void

int main(void)
{
    코드 작성
    return 0;
}
리턴형 함수이름(매개변수 리스트)
{ // 함수의 시작
    코드 작성
    0을 리턴함. 문장 끝에는 “ ;”을 씀
} // 함수의 끝

 

 

함수의 입력과 출력

입력

  • 매개변수(parameter), argument

출력

  • 반환값(return value), 리턴값
  • 함수가 돌려주는 결과 값

void

  • 리턴값이 없으면 리턴형 쓰는 자리에 void
  • 매개변수가 없으면 매개변수 쓰는 자리에 void
    • 이 경우는 생략 가능하지만 C언어에서는 생략하지 않는 것이 좋음
    • C++에서는 일반적으로 생략

 

C 표준 라이브러리 함수

분류
입출력 함수 printf, scanf, puts, gets_s, putchar, getchar
문자열 함수 atoi, strcmp, strcpy, strlen, strcat
문자 함수 isalnum, isalpha, iscntrl, tolower, toupper
수학 함수 sin, cos, tan, abs, pow, log, sqrt
날짜와 시간 관련 함수 asctime, clock, difftime, localtime, time
메모리 관련 함수 calloc, free, malloc, realloc
유틸리티 함수 exit, qsort, rand, srand, system

C11부터 buffer overflow 문제로 gets 함수가 삭제되고 버퍼의 크기를 설정하는 gets_s 함수가 추가됨. GCC와 Clang은 gets_s 함수를 아직 제공하지 않음

 

입출력 함수

함수명 기능
fclose 파일을 닫음
feof 파일의 끝인지 검출
ferror 파일의 읽기, 쓰기에서 에러가 있었는지 확인
fflush 스트림에 할당되어 있는 버퍼 삭제
fgetc 스트림으로부터 한 문자를 읽어옴
fgetpos 스트림의 현재 파일 포인터를 조사
fgets 스트림으로부터 문자열을 읽어옴
fopen 파일을 연다
fprintf 스트림으로 서식화된 출력
fputc 스트림에 한 문자 출력
fputs 문자열을 스트림으로 출력
fread 파일로부터 읽어옴
freopen 개방되어 있는 스트림을 다른 이름으로 대체
fscanf 파일로부터 서식화된 입력
fseek 파일 포인터 새롭게 설정
fsetpos 현재의 파일 포인터 새롭게 설정
ftell 현재 파일 포인터 조사
fwrite 스트림으로 출력
getc 스트림으로부터 문자 하나를 읽음
getchar 키보드에서 문자 하나를 읽어옴
gets_s 키보드에서 하나의 문자열을 읽어옴
perror 시스템 에러 메시지를 화면에 출력
printf 화면에 서식화된 출력
putc 스트림에 문자 하나를 출력
putchar 화면에 문자 하나를 출력
puts 화면에 문자열 출력
remove 파일 삭제
rename 파일 이름 바꿈
rewind 파일의 시작점으로 파일 포인터 이동
scanf 키보드로부터 서식화된 입력을 받음
sprintf 화면이 아닌 문자열 형태로 서식화된 출력
sscanf 키보드가 아닌 문자열에서 서식화된 입력을 받음

 

 

문자열 함수

함수명 기능
atof 문자열을 double형으로 바꿈
atoi 문자열을 integer형으로 바꿈
atol 문자열을 long형으로 바꿈
memchr 특정 문자가 최초로 발생된 번지를 찾음
memcmp 두 개의 메모리 블럭 비교
memcpy 메모리끼리 복사 - 직접 카피
memmove 메모리끼리 복사 - 임시 저장 후 카피
memset 특정 메모리를 문자로 채움
strcat 두 문자열을 연결
strchr 문자열 내에 특정 문자가 있는지 검사
strcmp 두 개의 문자열 대소 비교
strcoll setlocale함수에 지정된 비교 방식으로두 개의 문자열 비교
strcpy 문자열을 복사
strcspn 문자열에 특정 문자열이 들어있지 않은 문자들이 연속한 길이
strerror 에러 코드의 텍스트 버전 생성
strncat 문자열끼리 지정한 길이만큼 덧붙임
strncmp 문자열의 대소 비교
strncpy 문자열을 정해진 길이만큼 복사
strpbrk 하나의 문자열 내의 문자가 다른 문자열에 하나라도 있는지
strrchr 문자열 내에 특정 문자가 있는지 뒤부터 조사
strspn 문자열 중에서 다른 문자열에 있는 문자들이 연속해있는 길이
strstr 문자열 내에 다른 문자열이 포함되어 있는지 검사
strtod 문자열을 double형으로 바꿈
strtok 문자열에서 token을 찾아냄
strtol 문자열을 long형으로 바꿈
strtoul 문자열을 unsigned long형으로 바꿈

 

수학 함수

함수명 기능
abs 절대값
acos 코사인 함수의 역함수
asin 사인 함수의 역함수
atan 탄젠트 함수의 역함수
atan2 atan과 같은데 더 정확함
ceil double형 변수 x보다 큰 최초의 정수
cos 코사인 함수
cosh 쌍곡선 코사인 함수
div 두 정수를 나누어 몫과 나머지를 구조체로 리턴
exp 지수 함수
fabs 부동 소수점의 절대값
floor double형 변수 x보다 작은 최초의 정수
fmod 실수를 다른 실수로 나눈 나머지
labs long형 정수의 절대값
log 자연 로그
log10 상용 로그
modf 실수를 분해하여 실수부와 정수부로 나눔
pow x의 y승
sin 사인 함수
sinh 쌍곡선 사인 함수
sqrt 제곱근
tan 탄젠트 함수
tanh 쌍곡선 탄젠트 함수

 

 

날짜, 시간 함수

함수명 기능
asctime 구조체에 저장된 시간, 날짜를 문자열로 바꿈
clock 프로그램이 시작된 후 경과된 clock tick
ctime 시간과 날짜를 문자열로 바꿈
difftime 두 시간의 차이를 초 단위로 계산
gmtime GMT 표준 시간으로 바꿈
localtime time함수에 의해 구해진 시간을 tm구조체에 저장
time 시스템의 현재시간 조사

 

 

메모리 관련 함수

함수 기능
calloc 0으로 초기화 하면서 동적으로 메모리 할당
free 동적으로 할당된 메모리 해제
malloc 동적으로 메모리 할당
realloc 할당된 메모리의 크기를 변경

 

 

유틸리티 함수

함수 기능
abort 프로그램을 비정상적으로 종료
assert 논리식이나 정수 값이 거짓이면 프로그램 종료
atexit 종료 처리 함수 등록
bsearch 이진 탐색 수행
exit 프로그램을 정상적으로 종료
getenv 환경변수를 조사해 문자열로 저장
qsort quick sort 수행
raise 프로그램에 신호를 강제적으로 보냄
rand 난수 발생
signal 예외 상황이나 raise함수가 호출될 때 프로그램으로 보내지는 신호
srand 난수 발생기를 초기화
system DOS명령 수행

 

 

사용자 정의 함수

직접 만드는 함수

 

 

함수명

동사, 모두 소문자와 단어 사이 언더스코어(_)

  • add(), add_number(), delete_name(), check_for_errors(), dump_data_to_file()

동사, 의미가 바뀌는 부분은 대문자, 나머지는 소문자(lower camel case)\

  • add(), addNumber(), deleteName()

동사, 첫 글자와 의미가 바뀌는 부분은 대문자, 나머지는 소문자(upper camel case)

  • AddNumber(), DeleteName()

 

함수 정의, 호출, 선언

함수 정의

  • 함수 만들기
  • 이름, 매개변수, 리턴형, 기능
void display(void)
{
	printf("안녕");
}

 

함수 호출

  • 함수 사용하기
  • 이름, 매개변수
display();

 

함수 선언

  • 함수의 사용법
  • 이름, 매개변수, 리턴형
  • 컴파일러에게 함수에 대한 정보를 미리 줌
  • 함수 정의의 첫번째 줄을 세미콜론으로 끝나는 문장으로 만듦
void display(void);

 

 

return문

결과 값을 호출한 함수로 반환

return문을 만나면 함수의 나머지 부분에 상관없이 함수의 실행을 종료하고 호출한 함수로 넘어감

  • return(수식이나 값);
  • return 다음에 괄호는 생략해도 됨
  • return 1;
  • return x;
  • return (x*2);
  • return (x+y);

 

함수 호출(call)

함수를 만드는 것을 함수를 정의(definition) 한다고 함

함수를 만든(정의) 후 함수를 사용(호출)

함수를 호출할 때는 함수 이름과 괄호 안에 argument를 개수만큼 써야 함

argument가 없을 때는 빈 괄호만 쓰면 됨

  • display();
  • double_number(3);
  • add(2, 3);
  • vending(1);

 

함수의 정의와 선언(원형, prototype)

덧셈기 함수의 정의(구현)

int add(int x, int y) // 함수의 머리(header)
{ // 함수의 본체 시작
	return (x+y); // 함수의 본체(body)
} // 함수의 본체 끝

 

함수의 머리 부분을 다음과 같이 하나의 문장 (세미콜론으로 끝남)으로 만드는 것을 함수 선언 또는 함수 원형(prototype)이라 함

  • int add(int x, int y);
  • int add(int, int);
  • 매개변수 x와 y는 생략할 수 있지만 생략하지 않는 것이 더 좋음

 

함수 선언이 귀찮다면?

함수 선언문을 한 줄 작성하는 것이 귀찮다면 view()함수의 정의를 main()함수 전에 하면 됨

함수 정의는 선언을 겸하고 있으므로 따로 선언할 필요가 없음

이 방법은 함수들끼리 호출하는 상황에서는 오류를 발생시킬 수 있으므로 권장하지 않음

#include <stdio.h>

void view(void)	//함수 정의
{
	printf("view함수\n");
}

int main(void)
{
	printf("메인 함수 : view호출 전\n");
	view();	//함수 호출
	printf("메인 함수 : view호출 후\n");
	return 0;
}

 

 

main()함수의 리턴 값

return문을 만나면 함수의 나머지 부분에 상관없이 함수의 실행을 종료하고 호출한 함수로 넘어감

main()함수의 경우 return문을 만나면 프로그램이 끝남

return문은 하나의 값만 되돌려줌

  • 포인터를 사용해야 둘 이상의 값을 return할 수 있음

지금까지 예제에서 main()함수의 리턴 값은 0

main()함수의 리턴 값은 프로그램이 끝나면서 OS에 전달되는 값

  • 프로그램이 정상적으로 종료되면 보통 0을 리턴
  • 비정상적으로 종료할 때는 1을 리턴하는 것이 관례

stdlib.h 파일을 보면 다음과 같이 정의되어 있음

  • #define EXIT_SUCCESS 0
  • #define EXIT_FAILURE 1

 

재귀(recursive) 함수

함수가 함수 내에서 자기 자신을 호출 할 수 있는데 이것을 재귀 호출(recursive call)

재귀 호출을 허용하는 함수를 재귀 함수(recursive function)

 

구현하고자 하는 작업이 함수 자신을 순환 반복하여 구현되어질 수 있다면 재귀 함수를 이용할 수 있음

재귀 함수는 재귀적 특성의 알고리즘에서 쉬운 문제 해결 방법 제공

함수의 반복 호출로 인한 시간과 메모리 공간의 효율성이 떨어지는 단점

  • 스택 오버플로우(stack overflow)에 주의

재귀 함수는 반복문을 이용한 함수로 변환이 가능

피보나치 수열, 하노이탑 문제, 이진 트리 순회(binary tree traversal), 프랙탈 곡선(fractal curve), 이항 계수(binominal coefficient) 등에 사용

 

 

매개변수를 전달하는 방법

C 언어에서는 기본적으로 값에 의한 호출(call by value)

실매개변수의 을 형식매개변수로 전달

  • 실매개변수를 형식매개변수로 전달할 뿐 함수 내부에서 형식매개변수가 변경되더라도 실매개변수는 변경되지 않음

형식매개변수가 변하면 실매개변수도 변하게 하려면 포인터를 이용하여 call by address로 구현해야 함

값에 의한 호출 (call by value) 주소에 의한 호출 (call by address)
실매개변수의 값을 형식매개변수로 전달 실매개변수의 주소를 형식매개변수로 전달

 

 

 

call by address

형식매개변수가 변하면 실매개변수도 변하게 하려면 주소 연산자와 포인터를 이용하여 call by address로 구현해야 함

실매개변수의 주소로 함수를 호출

  • c=sum(&a,&b);

call by address에서는 sum()함수의 정의를 다음과 같이 수정

#include <stdio.h>
int sum(int *pa, int *pb);
int main(void)
{
	int a = 2, b = 5, c=0;
	printf("sum()호출 전 a=%d b=%d c=%d\n", a, b, c);
	c = sum(&a, &b);
	printf("sum()호출 후 a=%d b=%d c=%d\n", a, b, c);
	                                      //4,10,14
	return 0;
}
int sum(int *pa, int *pb)
{	//a의 주소가 포인터 pa로 전달됨
	*pa = *pa + 2;
	*pb = *pb + 5;
	return(*pa + *pb);
}

sum() 호출 전 a=2 b=5 c=0

sum() 호출 후 a=4 b=10 c=14

 

call by value 예

#include <stdio.h>

int sum(int x, int y);

int main(void)
{
	int a = 2, b = 5, c=0;
	printf("sum()호출 전 a=%d b=%d c=%d\n", a, b, c);
	c = sum(a, b);
	printf("sum()호출 후 a=%d b=%d c=%d\n", a, b, c); //2,5,14
	return 0;
}
int sum(int a, int b)
{
	a = a + 2;
	b = b + 5;
	printf("sum()함수 내 a=%d b=%d a+b=%d\n", a, b, a + b);
	return(a + b);
}

sum()호출 전 a=2 b=5 c=0

sum()함수 내 a=4 b=10 a+b=14

sum()호출 후 a=2 b=5 c=14

 

 

반복되는 부분은 함수로 구현

두 수를 더하여 출력하는 일을 프로그램을 통해서 3번 반복

프로그램 내에서 반복적으로 사용하는 부분은 따로 함수로 구현해 놓은 후 그 함수를 호출하는 방식으로 프로그램을 작성하는 것이 여러 가지로 유리

#include <stdio.h>
int main(void)
{
	int num1=1,num2=2,num3=3;
	int num4=4,num5=5,num6=6;
	int sum;
    
	sum=num1+num2;
    
	printf("두 수 %d과 %d를 더한 결과는 %d입니다.\n",num1, num2, sum);
	sum=num3+num4;
	printf("두 수 %d과 %d를 더한 결과는 %d입니다.\n",num3, num4, sum);
	sum=num5+num6;
	printf("두 수 %d과 %d를 더한 결과는 %d입니다.\n",num5, num6, sum);
	return 0;
}
#include <stdio.h>
void add(int x, int y);
int main(void)
{
	int num1 = 1, num2 = 2, num3 = 3;
	int num4 = 4, num5 = 5, num6 = 6;

	add(num1, num2);
	add(num3, num4);
	add(num5, num6);
	return 0;
}

void add(int x, int y)
{
	printf("두 수 %d과 %d를 더한 결과는 %d입니다.\n", x, y, x + y);
}

 

 

모듈화 프로그래밍 연습 : 모듈화 전

임의의 개수의 수를 입력 받아 합, 평균, 최대값, 최소값을 출력하는 프로그램

우선 모듈화 하지 않고 main()함수에 전체 기능을 모두 구현

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <limits.h> //INT_MIN, INT_MAX가 정의되어 있음
int main(void)
{
	int i, num, sum = 0, total, max = INT_MIN, min = INT_MAX;
	// max는 int형으로 표현할 수 있는 가장 작은 값 INT_MIN으로 초기화
	// min은 int형으로 표현할 수 있는 가장 큰 값 INT_MAX로 초기화
	printf("┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n");
	printf("┃ 임의의 개수의 수의 합, 평균, 최대값, 최소값을 구하는 프로그램┃\n");
	printf("┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n");
	printf("계산하려는 수는 몇 개입니까=>");
	scanf("%d", &total);
	for (i = 1; i <= total; i++) {
		printf("%d번째 수를 입력하세요=", i);
		scanf("%d", &num);
		sum += num;
		max = (num > max) ? num : max;
		min = (num < min) ? num : min;
	}
	printf("합:%d 평균:%.2f 최댓값:%d 최솟값:%d\n",
		sum, (double)sum / total, max, min);
	return 0;
}

 

모듈화 프로그래밍 연습 : 모듈화 후

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <limits.h>
void start(void);//함수 선언
int max_number(int a, int b); //함수 선언
int min_number(int a, int b); //함수 선언
int main(void)
{
	int i, num, sum = 0, total;
	int max = INT_MIN, min = INT_MAX;
	start();//함수 호출
	printf("계산하려는 수는 몇 개입니까=>");
	scanf("%d", &total);
	for (i = 1; i <= total; i++) {
		printf("%d번째 수를 입력하세요=", i);
		scanf("%d", &num);
		sum += num;
		max = max_number(num, max);//함수 호출
		min = min_number(num, min);//함수 호출
	}
	printf("합:%d 평균:%.2f 최대값:%d 최소값:%d\n",
		sum, (double)sum / total, max, min);
	return 0;
}

void start(void)//함수 정의
{
	printf("┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n");
	printf("┃ 합, 평균, 최대값, 최소값 ┃\n");
	printf("┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n");
}
int max_number(int a, int b)//함수 정의
{
	return((a > b) ? a : b);
}
int min_number(int a, int b)//함수 정의
{
	return((a < b) ? a : b);
}

 


소스를 헤더 파일(.h)과 C 파일(.c)로 나누기

 

헤더 파일(.h)과 C 파일(.c)로 나누어서 구현

헤더 파일에는 전처리기와 함수 선언

C 파일에는 나머지 소스와 만든 헤더 파일을 포함시킴

 

다음 소스를 C 파일에서는 제거하고 헤더 파일 score.h에 저장

 

Header File 추가

솔루션 탐색기의 [헤더파일]-[추가]-[새 항목]

 

헤더파일 이름: score.h

 

score.c + score.h

추가된 score.h에 잘라놓았던 소스를 붙여넣기

 

score.c에 #include "score.h"

솔루션 탐색기의 score.c를 더블 클릭하여 소스 추가

score.h에 저장되어 있는 전처리기와 함수선언을 현재의 소스에 포함시킴

#include "score.h"

'C' 카테고리의 다른 글

14주차 수업내용  (1) 2024.06.04
13주차 수업내용  (0) 2024.05.28
11주차 수업내용  (0) 2024.05.14
10주차 수업내용  (0) 2024.05.07
9주차 수업내용  (0) 2024.04.30