ch09 함수

암영·2022년 4월 29일
0

c언어

목록 보기
8/21

09-1 함수를 정의하고 선언하기

함수를 만드는 이유

지금까지는 구현하는 함수가 간단해서 int main()에 쑤셔 넣을 수 있었지만, 함수가 복잡해지면 더이상 위의 것 하나로 구현하려면 복잡해지고 수정시에도 일이 커질 수 있다.
divide and conquer!
작은 것부터 하나하남 해결하는게 문제를 빠르게 해결하는 해결책이 된다.

함수의 유형

전달인자,반환값의 유무에따라 네개의 형태로 나뉜다.
유형1:전달인자O/반환값O
유형2:전달인자O/반환값X
유형3:전달인자X/반환값O
유형4:전달인자X/반환값X

  • 유형1:전달인자O/반환값O
    int(반환형) Add(함수의 이름) (int num1,int num2-> 매개변수)
    {
    int result=num1,num2;
    return result; //값의 반환
    }

  • 유형2,3
    void showadd(int num)//인자전달O,반환값X
    {
    printf("덧셈결과 출력 %d \n",num);
    }
    void:반환하지 않는다라는 뜻!

  • 유형4
    전달인자 작성란과 반환형 작성란에 둘다 void를 쓰면된다.

  • 총정리예제

#include <stdio.h>
int add(int num1, int num2)
{
	return num1 + num2;
}

void showaddresult(int num)
{
	printf("덧셈결과 출력 %d \n", num);
}

int readnum(void)
{
	int num;
	scanf_s("%d", &num);
	return num;

}

void howtousethisprog(void)
{
	printf("두개의 정수를 입력하시면 덧셈결과가 출력됩니다 \n");
	printf("자 그럼 두개의 정수를 입력하세요 \n");

}

int main()
{
	int result, num1, num2;
	howtousethisprog();
	num1 = readnum();
	num2 = readnum();
	result = add(num1, num2);
	showaddresult(result);
	return 0;
}
#include <stdio.h>
int Add(int num1, int num2)//인자전달0,반환값0
{
	return num1 + num2;
}
void ShowAddResult(int num)//인자 전달0 반환값x
{
	printf("덧셈결과 출력 %d \n", num);
}
int Readnum(void)//인자전달x반환값0
{
	int num;
	scanf_s("%d", &num);
	return num;
}
void HowToUseThisProg(void) //인자전달,반환값둘다 없음
{
	printf("두개의 정수를 입력하시면 덧셈결과가 출력됩니다 \n");
	printf("자! 그럼 두개의 정수를 입력하세요 \n");

}
int main(void)
{
	int result, num1, num2;
	HowToUseThisProg();
	num1 = Readnum();
	num2 = Readnum();
	result = Add(num1, num2);
	ShowAddResult(result);
	return 0;

}

다양한 함수종류정의하기

#include <stdio.h>
int numbercompare(int num1, int num2);

int main(void)
{
	printf("3과 4중에서 큰수는 %d 이다 \n", numbercompare(3, 4));
	printf("7과 2중에서 큰수는 %d 이다 \n", numbercompare(7, 2));
	return 0;

}

int numbercompare(int num1, int num2)
{
	if (num1, num2)
		return num1;
	else
		return num2;

}
#include <stdio.h>
int Absocompare(int num1, int num2); //절잿값이 큰 정수 반환
int getabsovalue(int num); //전달인자의 절댓값을 반환

int main(void)
{
	int num1, num2;
	printf("두개의 정수입력");
	scanf_s("%d %d", &num1, &num2);
	printf("%d 와 %d 중 절댓값이 큰 정수:%d \n",
		num1, num2, Absocompare(num1, num2));
	return 0;
}

int Absocompare(int num1, int num2)
{
	if (getabsovalue(num1) > getabsovalue(num2))
		return num1;
	else
		return num2;
}

int getabsovalue(int num)
{
	if (num < 0)
		return num * (-1);
	else
		return num;

}

return이 지니는 두가지 의미

1) 함수를 빠져나간다
2) 값을 반환한다.

return에 반환값이 명시되어 있지 않은 경우 1)을 뜻함.

함수의 정의와 그에따른 원형의 선언

컴파일은 위에서 아래로 작동하기 때문에 위에서 아래에 정의된 함수가 적혀있는 경우 에러를 일으킨다

따라서 뒤에 나온다고 알려주기 위해 int add();를 위에 선언해두면 에러가 일어나지 않는다.

  • 선언 종류
    1)매개변수 이름을 포함한 선언
    int add(int num1,int num2);
    2)매개변수 이름을 생략한 표현
    int add(int,int);

다양한 종류의 함수 정의하기

예제1) 하나의 함수내에 둘이상의 return문이 존재하는 경우

#include <stdio.h>
int numbercompare(int num1, int num2);

int main()
{
	printf("3과 4중에서 큰수는 %d 이다 \n ", numbercompare(3, 4));
	printf("7과 2중에서 큰수는 %d 이다 \n ", numbercompare(7, 2));
	return 0;
}

int numbercompare(int num1, int num2)
{
	if (num1 > num2)
		return num1;
	else
		return num2;
}

참고로 위예제는 완전하지 못하다. 동일한 값의 두 정수가 전달 되었을때, 값이 동일함을 알리지 못하는 구조로 정의되어있기 떄문이다.

예제2) 절댓값이 큰 수를 반환하는 함수

#include <stdio.h>
int absocompare(int num1, int num2);//전달값이 큰 정수로 반환
int getabsovalue(int num); //전달인자의 절댓값을 반환

int main()
{
	int num1, num2;
	printf("두개의 정수 입력:");
	scanf_s("%d %d", &num1, &num2);
	printf("%d와 %d 중 절댓값이 큰정수 %d \n",
		num1, num2, absocompare(num1, num2));
	return 0;
}

int absocompare(int num1, int num2)
{
	if (getabsovalue(num1) > getabsovalue(num2))
		return num1;
	else
		return num2;
}

int getabsovalue(int num)
{
	if (num < 0)
		return num * (-1);
	else
		return num;
}

이예제를 통해 알수 있는점
: main을 포함한 모든함수는 함수의 조건및 상황에 관계없이 다른 함수를 호출할수 있음!

문제

9-1

세개의 정수를 인자로 받아서 그중 가장 큰수를 반환하는 함수와 가장 작은 수를 반환하는 함수를 정의해보자. 그리고 이 함수들을 호출하는 적절한 main 함수도 작성해보자.

정답

#include <stdio.h>
int max(int n1, int n2, int n3)
{
	if (n1 > n2)
		return (n1 > n3) ? n1 : n3; //삼항연산자를 활용하면 간결하게 정리가능
	else
		return (n2 > n3) ? n2 : n3;
}

int min(int n1, int n2, int n3)
{
	if (n1 < n2)
		return (n3 > n1) ? n1 : n3;
	else
		return(n2 < n3) ? n2 : n3;

}

int main()
{
	int num1, num2, num3;
	printf("세개의 정수 입력:");
	scanf_s("%d %d %d", &num1, &num2, &num3);
	printf("가장 작은수: %d \n", min(num1, num2, num3));
	printf("가장 큰수: %d \n", max(num1, num2, num3));

}

9-2

섭씨(celcius)온도를 입력하면 화씨(fahrenheit)온도를 반환하는 celtofah라는 이름의 함수와 그 반대로 화씨온도를 입력하면 섭씨온도를 반환하는 fahtocel이라는 이름의 함수를 정의하고 이 두함수를 호출하는 예제를 완성해보자. 참고로 섭씨오 화씨간의 온도변환의 공식은 다음과 같다
fal=1.8xcel=32

정답

#include <stdio.h>
double celtofah(double cel);
double fahtocel(double fah);

int main (void)
{
	int sel;
	double num;
	printf("1섭씨>화씨 2.화씨>섭씨 \n");
	printf("선택\n");
	scanf_s("%d", &sel);
	
	if (sel == 1)
	{
		printf("섭씨입력");
		scanf_s("%lf", &num);
		printf("섭씨 %f 도는 화씨 %f도 \n", num, celtofah(num));
	}
	else if (sel == 2)
	{
		printf("화씨입력");
		scanf_s("%lf", &num);
		printf("화씨 %f 도는 섭씨 %f도 \n ", num, fahtocel(num));

	}
	else
		printf("선택오류 \n");
}
double celtofah(double cel)
{
	double fah;
	fah = 1.8 * cel + 32;
}
double fahtocel(double fah)
{
	return (fah - 32) / 1.8;
}
//주의점 1.함수선언하고  꼭 콜론 붙이기!

09-3

인자로 전달된 수만큼의 피보나치 수열을 출력하는 함수를 정의해보자. 예를 들어서 프로그램 사용자가 5를 입력하면 0에서부터 시작해서 총 5개의 피보나치 수열을 출력해야 한다. 참고로 피보나치 수열은 다음과 같다.
0,1,1,2,3,5,8,13,21,34...
이렇듯 피보나치 수열은 0과 1에서 시작한다. 그리고 세번쨰이후의 수열부터는 이전의 두값의 합으로 구성된다. 즉,세번째 수는 0과 1의 합으로 이뤄져서 1이되고, 네번째 수는 1과 1의 합으로 이루어져서 2가 된다.

정답

#include <stdio.h>

void showfibonaci(int num)
{
	int f1 = 0, f2 = 1, f3, i;
	if (num == 1)
		printf("%d", f1);
	else
		printf("%d %d", f1, f2);

	for (i = 0; i < num - 2; i++)
	{
		f3 = f1 + f2;
		printf("%d", f3);
		f1 = f2;//f2의 값을 f1로 옮김..?(옮김이라는 표현이 맞을까??)
		f2 = f3;//그래서 ex 0,1>1,1로 변경되어 다음 수열계산가능

	}
}

int main()
{
	int n;
	printf("출력하고자하는 피보나치 수열의 갯수");
	scanf_s("%d", &n);
	if (n < 1)
	{
		printf("1이상의 값을 입력하시오");
		return -1;
	}

	showfibonaci(n);
	return 0;
}

09-2 변수의 존재기간과 접근범위1:지역변수

지역변수(Local Variable)

'지역':중광호에 의해 선언되는 영역
-> 중괄호내에 선언되는 변수는 모두 지역변수이다.
특징: 선언된 지역내에세민 유효함.
! 지역변수는 외부에 선언된 동일한 이름의 변수를 가리게 된다.

#include<stdio.h>
int simplefunone(void)
{
	int num = 10;
	num++;
	
	printf("simplefunone num: %d \n", num);
	return 0; //simplefunone이 유효한 마지막 문장

}

int simplefuntwo(void)
{
	int num1 = 20;
	int num2 = 30;
	num1++, num2++;
	printf("num1&num2: %d %d \n", num1, num2);
	return 0;
}

int main()
{
	int num = 17;
	simplefunone();
	simplefuntwo();
	printf("main num :%d \n", num);
	return 0; //main num이 유효한 마지막 문장
}
int simplefunone(void)
{
	int num = 10;
	num++;
	
	printf("simplefunone num: %d \n", num);
	return 0; //simplefunone이 유효한 마지막 문장

}

변수 num은 simplefunone이라는 함수 중괄호 안에 선언되었으므로 이 함수를 빠져나가기 직전까지만 유효하다: 함수를 벗어나면 자동으로 소멸되기 때문.
따라서 simplefunone 함수가 호출될때마다 메모리가 할당되고 소멸되며, 선언 지역안에서만 유효하기 때문에 선언된 지역이 다르면 변수 이름이 같아도 문제가 되지 않는다.

지역변수의 할당과 소멸

위의 예제를 바탕으로 살펴보기
지역변수가 할당되는 메모리의 특징
1) 지역변수는 스택(stack) 이라는 메모리 영역에 할당된다
2)지역변수는 접시 쌓듯이 할당된다.

다양한 형태의 지역변수

지역변수는 반복문이나 조건문에도 선언이 가능하다.
(굿노트로 작성해서 이미지로 올리기)

#include <stdio.h>
int main()
{
	int cnt;
	for (cnt = 0; cnt < 3; cnt++)
	{
		int num = 0;
		num++;
		printf("%d 번째 반복,지역변수 num은 %d \n", cnt + 1, num);

	}
	if (cnt == 3)
	{
		int num = 7;
		num++;
		printf("if문 내에 존재하는 지역변수 num은 %d \n", num);

	}
	return 0;
}

7행에 선언된 지역변수 num은 for문 중괄호내에 선언 되었기 때문에 for문 내에서 유효한 지역변수가된다. for문에 의한 반복이 중괄호내에서 이루어 지는 것이아니라, 중괄호의 진입과 탈출이 반복해서 이루어 지는 것 이기때문에 반복이 이루어 질때마다 변수 num은 메모리상 할당되고 소멸된다. 따라서 10행의 출력결과는 항상 1이다.

3)지역변수는 외부에 선언된 동일한 이름의 변수를 가리게 된다.

예제

#include<stdio.h>

int main()
{
	int num = 1;

	if (num == 1)
	{
		int num = 7;
		num += 10;
		printf("if문 내 지역변수 num %d \n", num);
	}
	printf("main함수 내 지역변수num : %d \n", num);
	return 0;

}

실행결과
if문 내 지역변수num:17
maim함수내 지역변수 num:1

실행결과2(9행주석처리시)
if문내 지역변수 num:11
main함수 내 지역 변수:11

if문 내부에 동일한 이름의 변수가 선언 되었다 -> if 문 내에서는 main 함수 num이 가려진다. 따라서 10행에 접근하는 num은 if문내의 num, if문 내 변수 선언이 없으면 5행의 num이 접근한다.

지역변수의 일종인 매개변수!

매개변수는 지역변수이다! 주의)역은 성립하지 않는다. 모든 지역변수가 매개변수는 아님.

+)자동변수=지역변수 선언된 영역을 벗어나면 자동적으로 소멸되기 때문에 붙여진 이름.

09-3 변수의 존재기간과 접근범위2: 전역변수, static변수, register변수

1. 전역 변수의 이해와 선언 방법

전역 변수
: 어디서든 접근 가능한 변수
특징:

  • 프로그램 시작과 동시에 메모리 공간에 할당되어 종료시 까지 존재함
  • 별도의 값으로 초기화 하지 않으면 0으로 초기화 됨
  • 프로그램 전체 영역 어디서든 접근 가능

예제

#include <stdio.h>

void Add(int val);
int num; //전역변수는 기본적으로 0으로 초기화 됨
int main () {

	printf("num : %d \n", num);
	Add(3);
	printf("num:%d \n", num);
	num++;
	printf("num:%d\n", num);
	return 0;
}
void Add(int val)
{
	num += val;
}

2. 전역변수와 동일한 이름의 지역변수가 선언되면?

A:해당 지역 내에서는 전역변수가 가리워지고, 지역변수로의 접근이 이뤄집니다.

#include <stdio.h>

int Add(int val);
int num=1; //전역변수는 기본적으로 0으로 초기화 됨
int main () {

	int num = 5;
	printf("num : %d \n",Add(3));
	printf("num:%d \n", num+9);
	return 0;
}
int Add(int val)
{
	int num = 9;
	num += val;
	return num;
}

실행결과

num:12
num:14

+ 이런 상황 자체를 만들지 않는게 좋음. 가급적이면 변수이름은 달리 해야함.

3. 전역변수의 사용

전역변수의 수가 증가하면 그만큼 프로그램은 복잡해지며, 좋은 구조의 프로그램과는 거리가 멀어지게 됨. 따라서 전역변수의 선언은 그만큼 신중해야함.

4. 지역변수에 static 선언

사실 전역변수와 지역변수 모두에 static 선언을 할수 있으나 여기서는 지역변수에 static선언을 하는 것에 대해서만 설명한다.

원래 지역변수 특성

  • 선언된 함수 내에서만 접근가능
  • 함수 내에서 선언된 지역변수는 해당 함수가 반환하면 소멸됨.

지역변수에 static선언을 할때 가지게 되는 특성

  • 선언된 함수 내에서만 접근이 가능(지역변수 특성)
  • 딱 1회 초기화 되고 프로그램 종료시까지 메모리공간에 존재(전역변수 특성)
#include <stdio.h>

void SimpleFunc() {
	static int num1 = 0; //static선언한 지역변수, 초기화 하지 않으면 0으로 초기화됨
	int num2 = 0;
	num1++, num2++;
	printf("static: %d local %d\n", num1, num2);
}

int main(void) {
	int i;
	for (i = 0; i < 3; i++) {
		SimpleFunc();
	}
	return 0;
}

결과

static: 1 local 1
static: 2 local 1
static: 3 local 1

stiatic 지역변수 사용

static 지역변수는 전역변수보다 안정적임
전역변수를 static변수로 대체 할 수 있다면 대체해서 프로그램 안전성을 높여야함.
변수에 접근하는 영역이 하나의 함수로 제한되면 static 지역변수의 선언을 고려해야함.

register 변수

선언 방식

register int num=3;

특징
위 와 같이 선언되면 변수 num은 CPU내에 존재하는 '레지스터'라는 메모리 공간에 저장될 확률이 높아짐.
따라서 컴파일러는 이 선언을 토대로 레지스터의 활용여부를 결정.
무조건 저장되는게 아니라 컴파일러의 결정에 달림. register 선언을 했어도 불필요하다고 판단되면 레지스터에 할당이 되지 않고 선언을 안했어도 필요하다고 판단되면 레리스터에 할당함.

profile
just do! -얼레벌레 굴러가는 공대생

0개의 댓글