나의 IT일지
함수와 변수 본문
- 함수란 나열된 여러 명령어의 묶음으로, 자주 사용하는 명령을 묶어놓고 이름만 호출하는 것으로 편하게 사용하기 위해 사용한다.
- 함수를 사용할 때에는 3개의 순서가 있는 데, 함수를 사용하겠다고 컴파일러에게 발표하는 함수선언, 함수를 사용하는 함수호출, 함수를 제작하는 함수정의가 있다.
- 함수는 반환형(돌려줄 값(리턴값)의 자료형), 매개변수(전달하는 값을 저장하기 위한 변수), 실행문, return문(값을 호출한 곳으로 반환하는 분기문)으로 구성되어 있다.
함수와 변수의 관계
변수는 함수의 선언 위치, 적용범위에 따라 구분이 된다.
변수 종류 | 적용 범위 | 선언 위치 | 적용 시작 | 적용 종료 |
지역변수 | 함수 블록 내에서만 사용 | 함수 내부에 선언 | 코드 블록 시작 | 코드블록의 실행종료 |
전역변수 | 전체 소스코드에서 사용 | 함수 외부에 선언 | 프로그램 시작 | 프로그램 실행종료 |
정적변수 | 함수가 종결되어도 계속 사용 | 함수 내부에 선언 | 선언한 후 | 프로그램 실행종료 |
매개변수 | 함수 블록 내에서만 사용 | 함수 블록에 선언 | 함수선언 이후 | 선언함수의 코드블럭 실행종료 |
외부변수 | 전체 소스코드에서 사용 | 다른 소스 파일에 선언 | 프로그램 시작 | 프로그램 실행종료 |
지역변수는 변수를 사용하기 위해서는 초기값을 선언하거나 입력함수를 사용해야 하지만, 전역변수의 경우에는 초기화나 입력함수를 사용하지 않아도, 해당 프로그램을 운영 할 수 있다.
전역변수와 지역변수의 예시
#include<stdio.h>
int x = 2;//전역변수
void f1();
void main() {
int y = 3;//지역변수
printf("x=%d\n", x);
printf("y=%d\n", y);
f1();
}
void f1() {
printf("f1 호출\n");
printf("x=%d\n", x);
printf("y=%d\n", y);
}
위의 코드를 실행하면 "식별자 y가 정의되어 있지 않았습니다"라는 경고문이 나오면서 오류가 발생한다. 분명 main함수에 변수 y를 정의했는데, 저런 문구가 발생하는 것일까? 이는 변수 y가 지역변수이기 때문이다. 변수 y는 지역변수여서, main함수에서만 사용이 가능한 것이다. 그에 반해, 변수 x는 전역변수이기 때문에, main함수뿐만 아니라 f1함수에서도 사용이 가능한 것이다.
따라서 위의 코드가 실행되기 위해서는 변수 y가 전역변수로 만들던지, "printf("y=%d\n", y)"부분을 삭제하면 된다.
추가적으로, 지역변수는 선언을 하면, 실행 전까지는 값을 저장해야 하지만, 전역변수는 선언과 동시에 해당 자료형에 따라 값이 초기화가 된다.
#include<stdio.h>
int a ;
void func1() {
int a = 50;
printf("func1()에서 a의 값 ==> %d\n", a);
}
int main(){
func1();
printf("main()함수의 a의 값 ==> %d\n", a);
return 0;
}
매개변수의 예시
#include<stdio.h>
void v_test();
int g_a; //전역변수
void main() {
int l_a; //지역변수
g_a = 10;
l_a = 10;
printf("1.global=%d, local = %d\n", g_a, l_a);
v_test(l_a);
printf("3.global=%d, local = %d\n", g_a, l_a);
}
void v_test(int l_a) {//매개변수
g_a = 20;
l_a = 20;
printf("2.global=%d, local = %d\n", g_a, l_a);
}
- "1.printf"에서는 g_a 변수는 전역변수이고 l_a는 지역변수로, "g_a = 10, l_a = 10"이기에 "global = 10, local = 10"이 출력된 다.
- "2.printf"에서는 g_a 변수는 전역변수이고 l_a는 매개변수로, main 함수에서 "l_a = 10"으로 매개변수의 값인 10을 전달받았지만, "l_a = 20"으로 다시 저장이 되었기 때문에, "g_a = 20, l_a = 20"이므로 "global = 20, local = 20"이 출력된다.
- "3.printf"에서는 g_a 변수는 전역변수이고 l_a는 매개변수로, v_test함수에서 변수 l_a은 20으로 저장이 되었지만, 이는 v_test함수 내에서만 적용이 된다. 즉 main함수의 변수 l_a의 값은 10이므로, "global = 20, local = 10"이 출력된다.
위의 결과를 해석하면, 매개변수는 지역변수처럼 매개변수가 적용되는 함수 안에서만 적용이 된다. 즉, v_test함수의 l_a변수와 main함수의 l_a변수는 서로 다른 변수가 되는 것이다. 실제로 두 변수의 주소를 출력하면 다른 변수임을 알 수 있다.
또한 전역변수는 함수의 매개변수에 포함되지 않는 이상, 모든 함수를 통해 값이 변경될 수 있다는 것이다. 즉, v_test함수의 매개변수가 (int g_a, int l_a)로 변경이 된다면, v_test함수의 g_a변수는 매개변수가 되며, 전역변수인 g_a변수와 서로 다른 변수가 되는 것이기에, "3.printf"의 출력결과는 "global = 20, local = 10"가 아닌 "global = 10, local = 10"이 될 것이다.
정적변수의 예시
#include<stdio.h>
void static_viest();
void main() {
static_viest();
static_viest();
static_viest();
}
void static_viest() {
int sum = 0;
static int s_sum = 0;
sum += 10;
s_sum += 10;
printf("sum:%d,s_sum :%d\n", sum, s_sum);
}
위의 결과를 보면, 지역변수는 함수가 실행될 때마다, 변수의 값이 초기화되는 것을 알 수 있다. 하지만 정적변수는 함수가 실행이 되더라도, 변수의 값이 초기화가 되어 있지 않는 것을 확인 할 수 있다. 즉, 정적변수는 초기화를 제외한 "="연산자를 통해 저장된 값을 변화 시키지 않는 변수로, 정적변수를 만드는 방법은 "static"예약어를 사용해서 생성할 수 있다.
외부변수의 예시
#include<stdio.h>
extern int i, j;
extern void sum();
void main() {
printf("i:%d\n", i);
printf("j:%d\n", j);
sum();
printf("프로그램 종료\n");
}
#include<stdio.h>
int i = 5, j = 4, k;
void sum() {
k = i + j;
printf("%d + %d = %d\n", i, j, k);
}
위의 코드를 보면, main코드에 사용할 sum함수를 선언하고 있지만 sum함수를 정의하지 않았다. 그럼에도 위의 코드는 정상적으로 실행할 수 있다. 이는 변수 i, 변수 j, sum함수가 외부 소스코드에 저장되어 있고, "extern선언"으로 외부 소스코드에 있는 변수 i, 변수 j, sum함수를 main소스코드로 호출했기 때문에 실행이 가능한 것이다.
즉, extern를 사용하면, 외부에서 선언한 전역변수, 전역함수(외부변수, 외부함수)를 다른 소스코드와 공유할 수 있게 된다.
번외) void
void란 사전적으로 비어있는 공간을 뜻한다. 즉 매개변수 값을 받을 필요가 없거나, 리턴값을 변환할 필요가 없을 때 사용한다.
- 매개변수 값을 받을 필요가 없는 함수의 경우에는 함수명(void) 또는 함수명()으로 사용함, 주로 값을 미리 받지 않아도 되는 함수일 때 사용.
- 리턴값을 변환할 필요가 없는 함수의 경우에는 void 함수명으로 사용함, 주로 화면에 출력하는 함수만 있는경우와 함수에 리턴값이 없어도 되는 경우에 사용.
이때, 우리는 "void main()"을 주로 사용해 왔다. 이는 매개변수를 받을 필요 없으며, 리턴값을 받을 필요가 없기에 사용한 것이지만, 오류가 발생하기 쉽기에, 사용하는 것을 권장하지는 않는다.
반환타입 | 매개변수 | 함수 |
o | o | 반환형 함수명(매개변수){ 매개변수 있거나 없는 명령어들 return 반환값 } |
o | x | 반환형 함수명(void){ 매개변수 없는 명령어들 return 반환값 } |
x | o | 반환형 함수명(매개변수){ 매개변수 있거나 없는 명령어들 } |
x | x | 반환형 함수명(void){ 매개변수 없는 명령어들 } |