저장 클래스
- C 언어에서는 변수와 함수의 이름(식별자)에 대해 저장 클래스라는 속성이 있으며, 이는 저장 기간, 범위, 그리고 연결성을 결정.
- 저장 클래스는 프로그램 내에서 식별자의 존재 기간(저장 기간), 참조 가능 위치(범위), 그리고 연결성을 정의.
저장 클래스 지정자
- C에서는 네 가지 저장 클래스 지정자를 제공 : auto, register, extern, static.
- 자동 저장 기간(auto): 기본적으로 함수의 지역 변수는 자동 저장 기간을 가지며, 함수 블록에 진입할 때 생성되고, 종료할 때 파괴됩니다.
- 정적 저장 기간(static, extern): 정적 저장 기간을 가진 변수와 함수는 프로그램이 실행되는 동안 지속됩니다.
- static: 정적 변수는 정의된 블록 내에서만 참조 가능하지만, 함수 종료 후에도 값을 유지합니다.
- extern: 전역 변수와 함수에 적용되며, 프로그램 내 다른 파일에서도 참조할 수 있도록 합니다.
3. 저장 기간
- 자동 저장 기간: 변수는 블록에 진입할 때 생성되고, 블록이 종료될 때 파괴됩니다. 일반적으로 지역 변수는 자동 저장 기간을 가집니다.
- 정적 저장 기간: 프로그램이 실행되는 동안 계속 유지됩니다. 전역 변수, 함수, 그리고 static 키워드로 선언된 지역 변수가 이에 해당합니다.
4. 범위
- 함수 범위: 함수 내에서만 사용 가능한 식별자 범위입니다. 예: 레이블.
- 파일 범위: 함수 외부에서 선언된 식별자는 파일 끝까지 참조 가능합니다. 예: 전역 변수, 함수 정의.
- 블록 범위: 블록 내에서만 참조 가능한 식별자 범위입니다. 예: 지역 변수.
- 프로토타입 범위: 함수 프로토타입의 매개변수 목록에서만 사용되는 범위입니다.
5. 연결성
- 연결성은 식별자가 다중 소스 파일 프로그램에서 어떻게 접근될 수 있는지를 결정합니다. 예를 들어, extern 키워드를 사용하면 다른 파일에서도 해당 식별자를 사용할 수 있습니다.
6. 소프트웨어 공학적 권장 사항
- 지역 변수는 함수 내에서만 사용되어야 하며, 전역 변수는 특별한 경우를 제외하고는 피해야 합니다.
- 외부 블록의 변수 이름을 내부 블록에서 덮어쓰지 않도록 주의해야 합니다.
// Fig. 5.16: fig 05_16.c
// Scoping. 범위 설정.
#include <stdio.h>
void useLocal(void); // function prototype
void useStaticLocal(void); // function prototype
void useGlobal(void); // function prototype
int x = 1; // global variable, 전역 변수
int main(void)
{
int x = 5; // local variable to main, main 의 지역변수
printf("local x in outer scoe of main is %d\n", x);
{ // start new scope, 새로운 범위 시작
int x = 7; // local variable to new scope , 새로운 범위의 지역 변수
printf("local x in inner scope of main is %d\n", x);
} // end new scope , 새로운 범위 종료
printf("local x in outer scope of main is %d\n", x);
useLocal(); // useLocal has automatic local x
// useLocal 은 자동 지역 x 를 사용한다.
useStaticLocal(); // useStaticLocal has static local x
// useStaticLocal 은 정적 지역 x를 가진다.
useGlobal(); // useGlobal uses global x
// useGlobal 은 전역 x를 사용한다.
useLocal(); // useLocal reintializes automatic local x
// useLocal 은 자동 지역 x를 재초기화한다.
useStaticLocal(); // static local x retains its prior value
// 정적 지역 x는 이전 값을 유지한다.
useGlobal(); // global x also retians its value
// 전역 x도 값을 유지한다.
printf("\nlocal x in main is %d\n", x);
}
// useLocal reinitializes local variable x during each call
// useLocal 은 각 호출시, 지역 변수 x를 재초기화한다.
void useLocal(void)
{
int x = 25; // initialized each time useLocal is called
// useLocal 이 호출 될 때 마다 초기화 된다.
printf("\nLocal x in useLocal is %d after entering useLocal\n", x);
++x;
printf("local x in useLocal is %d before exiting useLocal\n", x);
}
// useStaticLocal initializes static local variable x only the first time
// the function is called; value of x is saved between calls to this
// function
// useStaticLocal 은 함수가 처음 호출될 때만 정적 지역 변수 x를 초기화한다.
// 함수가 호출될 때마다 x의 값이 저장된다.
void useStaticLocal(void)
{
// initialized once
static int x = 50;
printf("\nlocal static x is %d on entering useStaticLocal\n", x);
++x;
printf("local static x is %d on exiting useStaticLocal\n", x);
}
// function useGlobal modifies global variable x during each call
void useGlobal(void)
{
printf("\nglobal x is %d on entering useGlobal\n", x);
x *= 10;
printf("global x is %d on exiting useGlobal\n", x);
}
local x in outer scoe of main is 5
local x in inner scope of main is 7
local x in outer scope of main is 5
Local x in useLocal is 25 after entering useLocal
local x in useLocal is 26 before exiting useLocal
local static x is 50 on entering useStaticLocal
local static x is 51 on exiting useStaticLocal
global x is 1 on entering useGlobal
global x is 10 on exiting useGlobal
Local x in useLocal is 25 after entering useLocal
local x in useLocal is 26 before exiting useLocal
local static x is 51 on entering useStaticLocal
local static x is 52 on exiting useStaticLocal
global x is 10 on entering useGlobal
global x is 100 on exiting useGlobal
local x in main is 5
메인 외부 범위의 지역 x는 5입니다
메인 내부 범위의 지역 x는 7입니다
메인 외부 범위의 지역 x는 5입니다
useLocal에 들어갈 때 지역 x는 25입니다
useLocal에서 나갈 때 지역 x는 26입니다
지역 정적 x는 useStaticLocal에 들어갈 때 50입니다
지역 정적 x는 useStaticLocal에서 나갈 때 51입니다
global x는 useGlobal에 들어갈 때 1입니다
global x는 useGlobal에서 나갈 때 10입니다
useLocal에 들어갈 때 지역 x는 25입니다
useLocal에서 나갈 때 지역 x는 26입니다
지역 정적 x는 useStaticLocal에 들어갈 때 51입니다
지역 정적 x는 useStaticLocal에서 나갈 때 52입니다
global x는 useGlobal에 들어갈 때 10입니다
global x는 useGlobal에서 나갈 때 100입니다
메인에서 지역 x는 5입니다
재귀 : 함수가 자신을 직접 또는 간접적으로 호출하여 문제를 해결하는 프로그래밍 기법
재귀 함수는 기본 사례를 처리하고, 복잡한 문제는 두 부분으로 나눠 해결한다.
함수는 해결할 수 있는 부분과 더 작은 버전의 문제를 재귀적으로 처리하는 부분으로 나누며, 이 과정을 반복하여 문제를 점차 단순화한다. 결국, 모든 재귀 호출이 기본 사례로 수렴하고, 최종적으로 원래 문제의 해답을 얻을 수 있다.

// Fig. 5.18: fig 05_18.c
// Recursive factorial function.
#include <stdio.h>
unsigned long long int factorial(unsigned int number);
int main(void)
{
// during each iteration, calculate
// factorial(i) and display result
// 각 반복에서, 팩토리얼을 계산하고 결과를 표시
for (unsigned int i = 0; i <= 21; ++i) {
printf("%u != %llu\n", i, factorial(i));
}
}
// recursive definition of fuction factorial
// 함수 팩토리얼의 재귀적 정의
unsigned long long int factorial(unsigned int number)
{
// base case
if (number <= 1) {
return 1;
}
else { // recursive step, 재귀 단계
return (number * factorial(number - 1));
}
}
0 != 1
1 != 1
2 != 2
3 != 6
4 != 24
5 != 120
6 != 720
7 != 5040
8 != 40320
9 != 362880
10 != 3628800
11 != 39916800
12 != 479001600
13 != 6227020800
14 != 87178291200
15 != 1307674368000
16 != 20922789888000
17 != 355687428096000
18 != 6402373705728000
19 != 121645100408832000
20 != 2432902008176640000
21 != 14197454024290336768
- 재귀 호출 단계: 21! -> 20! -> 19! -> ... -> 2! -> 1!
- 반환 단계: 1! -> 2! -> 3! -> ... -> 20! -> 21!
부호 없는 long long int를 사용하더라도, 21을 넘는 팩토리얼을 계산할 수 없다.
// Fig. 5.19: fig05_19.c
// Recursive fibonacci function, 재귀 피보나치 함수
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
unsigned long long int fibonacci(unsigned int n); // function prototype
int main(void)
{
unsigned int number; // number input by user
// obtain integer from user
printf("%s", "Enter an integer: ");
scanf("%u", &number);
// calculate fibonacci value for number input by number
// 사용자 입력 숫자에 대한 피보나치 값 계산
unsigned long long int result = fibonacci(number);
// display result
printf("Fibonacci(%u) = %llu\n", number, result);
}
// Recursive definition of function fibonacci
// 피보나치 함수의 재귀 정의
unsigned long long int fibonacci(unsigned int n)
{
// base case
if (0 == n || 1 == n) {
return n;
}
else { // recursive step
return fibonacci(n - 1) + fibonacci(n - 2);
}
}
Enter an integer: 0
Fibonacci(0) = 0
Enter an integer: 1
Fibonacci(1) = 1
Enter an integer: 2
Fibonacci(2) = 1
Enter an integer: 3
Fibonacci(3) = 2
Enter an integer: 10
Fibonacci(10) = 55
Enter an integer: 20
Fibonacci(20) = 6765
Enter an integer: 30
Fibonacci(30) = 832040
Enter an integer: 40
Fibonacci(40) = 102334155

- 연산자 피연산자 평가 순서: C 언어에서 연산자의 피연산자가 평가되는 순서는 연산자 우선순위와 결합 규칙에 따라 결정되며, 이는 컴파일러에 따라 달라질 수 있다. 예를 들어, &&, ||, ? :, 쉼표( , ) 연산자의 피연산자는 왼쪽에서 오른쪽으로 평가되지만, 다른 연산자의 경우 평가 순서는 명시되지 않아 예측하기 어렵다. 따라서 이러한 순서에 의존하는 프로그램은 이식성 문제가 발생할 수 있습니다.
- 피보나치 수 계산의 지수 복잡성: 피보나치 수를 재귀적으로 계산할 때, 각 재귀 호출이 기하급수적으로 증가하여 지수 복잡성을 가지게 됩니다. 예를 들어, 20번째 피보나치 수를 계산하려면 약 100만 번, 30번째 피보나치 수를 계산하려면 약 10억 번의 재귀 호출이 필요합니다. 이는 성능에 큰 부담을 주며, 더 나은 알고리즘이 필요할 수 있습니다.
- 재귀 대 반복: 재귀와 반복은 각각의 장단점이 있으며, 재귀는 더 자연스럽고 이해하기 쉬운 프로그램을 작성하는 데 유리할 수 있습니다. 그러나 재귀는 함수 호출 오버헤드와 메모리 사용량이 많아 성능에 부정적인 영향을 줄 수 있습니다. 반복은 일반적으로 성능 면에서 더 효율적입니다.
- 재귀의 단점: 재귀는 반복적으로 함수 호출을 발생시켜 오버헤드가 크고, 메모리 소모도 큽니다. 하지만, 문제를 자연스럽게 해결하고 이해하기 쉬운 프로그램을 작성하는 경우에는 재귀가 선호될 수 있습니다.
'c' 카테고리의 다른 글
| C data structure #1 (0) | 2024.08.20 |
|---|---|
| C File Processing #2 (0) | 2024.08.15 |
| C File Processing #1 (0) | 2024.08.15 |
| C Formatted Exercises (0) | 2024.08.14 |
| C Formatted Input/Output #2 (0) | 2024.08.14 |