프롤로그
ELITE HACKER Bootcamp 3rd 3주차 수업 공부 내용
aws 우분투 서버 하나 파서 연습
함수의 개념, 용도, 선언, 정의(프로토타입), 매개변수, 반환값, 가변인자, 스코프, 재귀 함수, 가변인자
함수의 스코프
스코프..scope... 해석하자면 "범위" 라는 의미이다.
뭐 전역 변수(global scope), 지역 변수(local scope), 블록 변수(block scope) 막 이런식으로 얘기하는데 헷갈리지 않는 방법이 있다.
{ } 이 중괄호 사이 안에서 선언한 변수는 딱 거기서만 사용할 수 있다.
전역 변수 같은 경우는 중괄호 {} 안에 있는 게 아니라 바깥에 있기 때문에 어느 곳에서나 사용할 수 있다.
그런데, 일반 함수를 선언하고 그 안에서 변수가 사용되면, 해당 함수는 { } 안에서 전부다 코드가 실행되기 때문에 다른 곳에서 사용할 수 없다.
블록 변수도 마찬가지. 블록 안에서 선어되었기에, 중괄호를 탈출하면 더이상 사용하지 못한다.
그런데
{
int a = 3;
{
int b = 5;
}
}
이런식으로 있다고 치면, b쪽에서는 a를 사용할 수 있다. a의 범위 안에 있는 모든 곳에서는 다 사용가능하니까, 다만 b는 중괄호 내부의 중괄호에서 사용되기 때문에 a쪽에서는 사용할 수가 없다.
(1) 전역변수 (global scope)
그래서 전역변수 사용되는 범위만 확인해볼 것이다.
#include <stdio.h>
int num = 10;
void test(){
printf("test에서 num : %d\n", num);
}
int main(){
printf("main에서 num : %d\n", num);
test();
return 0;
}
test()에서도, main()에서도 num이라는 변수 사용하였다. 그리고 실행이 되었다.
test()안에서도, main() 안에서도 따로 선언을 하지않았는데, 어디에서든 접근 할 수 있기 때문에 사용이 가능한 거다.
(2) 지역 변수(local scope)
보통은 함수 내부에서 사용된 변수를 의미한다.
#include <stdio.h>
void test(){
int num = 10;
printf("test에서 num : %d\n", num);
}
int main(){
test();
printf("main에서 num : %d\n", num);
return 0;
}
test()에서 선언한 Num이라는 변수는 Main에서 사용이 불가능하다.
이렇게 된다. 반대로 main에서 선언한 것을 test에서 사용한다면?
#include <stdio.h>
void test(){
printf("test에서 num : %d\n", num);
}
int main(){
int num = 10;
test();
printf("main에서 num : %d\n", num);
return 0;
}
역시나 안 된다.
(3) 블록 변수(block scope)
#include <stdio.h>
int main(){
int a = 0;
if(a==0){
int b = 10;
printf("내부 블록 : %d\n", b);
}
printf("외부 블록 : %d\n", b);
}
내부에서 선언된 b 는 같은 함수 내부에 있어도 블록 하나 밖에서 사용됐다고 바로 실행 불가능이 되어버렸다.
그렇다면 a의 경우는?
#include <stdio.h>
int main(){
int a = 0;
if(a==0){
int b = 10;
printf("내부 블록 : %d\n", a);
}
printf("외부 블록 : %d\n", a);
}
바로 사용이 가능하다. 왜냐면 중괄호를 벗어나지 않았으니까!
(4) 변수 섀도잉
중괄호 내부에서 변수를 하나 선언했는데, 그 안에서 중괄호 안으로 들어가서 똑같은 이름의 변수를 선언한다면 어떻게 될까?
#include <stdio.h>
int main(){
int a = 0;
if(a==0){
int a = 100;
printf("내부 블록 : %d\n", a);
}
printf("외부 블록 : %d\n", a);
}
중괄호 내부에서 int a를 또 선언해버렸다. 그러면 우선순위는 내부가 먼저기 때문에 내부에서 a는 100이 출력되지만 외부에서는 바로 0이 출력되는 것을 볼 수 있다.
이렇게 내부 스코프의 변수가 외부 스코프의 변수를 가려버리게 되는 것을 변수 섀도잉이라고 한다.
#include <stdio.h>
int a = 50;
static void test(){
int a = 10;
printf("test : %d\n", a);
}
int main(){
printf("전역 : %d\n", a);
int a = 0;
if(a==0){
int a = 100;
printf("내부 블록 : %d\n", a);
}
printf("외부 블록 : %d\n", a);
test();
}
a를 출력을 시도하지만, 전부 다 다른 값이 나온다.
재귀 함수
재귀함수(recursive function)은, 자기 자신을 호출하는 함수이다.
재귀는 반복적인 문제를 해결하거나 특정 알고리즘을 구현할 때 사용되며, 복잡한 문제를 더 작은 문제로 분할하여 해결하는 데 유용하다.
실제로 퀵정렬, 병합 정렬들은 재귀함수로 데이터들을 나눈 후에 다시 재조립하는 방식으로 이루어지기도 한다.
재귀 함수는 문제를 해결할 때 기본 조건(base case)과 재귀 호출로 구성된다.
재귀 함수로 들어갈 때 계속 하다가 들어가게 되면 무한 루프에 빠지게 되는데, 문제는 이 무한 루프가 메모리를 계속 타고타고타고 넘어가기 떄문에 터질수가 있다. 그렇기에 재귀함수가 끝나는 조건이 반드시 하나는 존재해야만 하는데, 이를 기본 조건이라고 한다.
즉, 기본조건은 끝내는 조건을 하나 반드시 두는 것이다.
그리고 재귀 호출은 자기 자신을 다시 호출 시키는 것
예시를 하나 들어보자.
팩토리얼 함수이다.
#include <stdio.h>
// 팩토리얼을 계산하는 재귀 함수
int factorial(int n) {
if (n == 0) {
return 1; // 종료 조건: 0!은 1
}
return n * factorial(n - 1); // 재귀 호출
}
int main() {
int num = 5;
printf("%d! = %d\n", num, factorial(num)); // 출력: 5! = 120
return 0;
}
팩토리얼이란, 1부터 자기자신까지 사이의 모든 정수를 모두 곱한 값이다.
5가 주어졌다면 1씩 내려가면서 계속 곱하고, 또 1씩 내려가면서 곱하고...를 재귀함수로 구현한 것이다.
0까지 내려갔을 때(1에서 종료 시켜도 됨!) 기본 종료 조건으로 1을 리턴하도록 설정해두고, 밑에 factorial로 계속 자기 자신 호출시킨다.
이 재귀함수라는 개념이 정말로 어렵다.
문제를 작은 하위 문제로 나누어 생각할 수 있어 코드가 직관적이고 이해하기 쉬운 것도 맞고, 특정 알고리즘(예: 트리 탐색, 그래프 탐색 등)에서 간결하게 문제를 표현할 수 있는 것도 맞는데, 생각하기가 참 어렵다.
반복적으로 함수가 호출되므로, 호출 스택이 커져 메모리를 많이 사용할 수 있으며, 효율이 떨어질 수 있고, 종료 조건이 잘못되거나 없으면 무한히 자기 자신을 호출하여 스택 오버플로우가 발생할 수 있기에 치명적이다.
그리고 솔직히, 팩토리얼 구하는 것만 봐도 그냥 for 문 돌리면 빠르지, 저렇게 들어가면 더 느릴수밖에 없지 않겠는가..아 물론 퀵정렬 같은 고급 정렬 알고리즘에서는 다름!
개인적으로 이 재귀 부분이랑 DP부분은 티어가 아무리 낮다고 하더라도 쉽게 생각하고 접근하기가 어려운 것 같다.
에필로그
이번에 좀 짧다. 근데 다음장에 바로 포인터 나올 거고, 그리고 나서 함수포인터, 배열 포인터 등 모든 개념들이 어렵게 나와있기 때문에 길어질 수 있다.
이럴 줄 알았으면 그냥 저번 글에 다 적을 걸 했나...했는데
시간이 없어서...
당장 오늘부터 수목금 한국통신학회 포스터발표 가야한다...
공부할 수 있을지모르겠다.
'KnockOn' 카테고리의 다른 글
[KnockOn] Linux/Ubuntu C언어 문자열 (0) | 2024.11.23 |
---|---|
[KnockOn] Linux/Ubuntu C언어 포인터 (0) | 2024.11.21 |
[KnockOn] Linux/Ubuntu C언어 함수 -1 (2) | 2024.11.19 |
[KnockOn] Linux/Ubuntu C언어 배열 (0) | 2024.11.17 |
[KnockOn] Linux/Ubuntu C언어 조건문, 반복문 (2) | 2024.11.15 |