7강 · 제5장 · 152~173쪽 · 약 70분

함수와 기억 클래스(2)

값/참조에 의한 자료 전달, 지역·전역 변수, auto·static·extern·register와 예제 프로그램 5-1~5-3을 익힙니다. (교재 제5장 152~173쪽)

학습 목표

  • 값에 의한 전달과 참조에 의한 전달을 구분하고, swap 전후 원본이 달라지는 이유를 설명한다
  • 지역변수·전역변수·블록 범위에서 같은 이름이 가려지는 규칙을 말한다
  • auto·static·extern·register의 수명·범위·초깃값을 비교한다
  • 재귀함수의 의미를 알고 factorial 예제를 실행한다
  • 교재 예제 5-7~5-14와 예제 프로그램 5-1~5-3을 실행해 결과를 예측한다

왜 이 강이 필요한가?

6강에서 함수를 정의·호출하고, 값에 의한 전달로 swap이 원본을 바꾸지 못하는 것까지 봤습니다.
7강은 교재 제5장 152~173쪽이며, LMS 강의내용은 자료 전달 방법과 기억 클래스입니다.

  1. 자료 전달 방법 — 값에 의한 전달(5-7)과 참조에 의한 전달(5-8)
  2. 지역변수와 전역변수 — 선언 위치와 유효 범위(5-9~5-11)
  3. 기억 클래스 — auto · static · extern · register(5-12~5-14)
  4. 예제 프로그램 — 대소문자 변환, 재귀 factorial, 값/참조 비교

본문 C 예제는 실행 칸이 붙습니다. scanf가 필요한 곳은 sscanf로 교재 입력을 넣었습니다. 키보드는 맨 아래 종합 실습의 「표준 입력」을 쓰세요. 표기는 int main(void)입니다.


1. 함수의 자료 전달 방법

호출할 때와 돌아올 때, 실매개변수와 형식매개변수 사이에 자료가 오가는 방법은 둘입니다.

방법무엇이 넘어가나함수가 끝나면
값에 의한 전달 (call by value)값의 복사형식매개변수를 바꿔도 원본은 그대로
참조에 의한 전달 (call by reference)주소같은 기억 장소를 가리키므로 원본도 바뀔 수 있음

C의 기본은 값에 의한 전달입니다. 값은 여러 개 넘길 수 있지만, 복사본을 바꿔도 원본은 돌아오지 않습니다. 돌려받는 값은 보통 return 하나입니다.

1.1 값에 의한 자료 전달 (예제 5-7)

fcn1(10, 20)처럼 호출하면 10과 20이 형식매개변수로 복사됩니다. 함수가 쓰는 공간은 호출한 쪽과 다릅니다.

교재 5-7 — swap이 호출한 쪽의 a, b를 바꾸지 못함 (6강에서 본 예제를 다시 확인):

이 예제 실행

실행 결과가 여기에 표시됩니다.

실행: 호출 전 3, 5 → 함수 안 5, 3 → 호출 후 다시 3, 5.
의도는 서로 바꾸는 것이었지만, 복사본만 바뀌었습니다.

1.2 참조에 의한 자료 전달 (예제 5-8)

값이 아니라 그 값이 들어 있는 주소를 넘깁니다. 그러면 실매개변수와 형식매개변수가 같은 기억 장소를 가리킵니다.

  • 호출할 때 실매개변수 앞에 주소 연산자 & — swap(&a, &b)
  • 형식매개변수는 포인터 — int *x, int *y
  • 그 주소의 값에 접근할 때는 *x

scanf("%d", &n)이 &를 쓰는 이유도 같습니다(3강). 포인터 연산의 자세한 규칙은 8~9강입니다. 여기서는 “주소를 넘기면 원본을 바꿀 수 있다”만 잡으면 됩니다.

교재 5-8 — 같은 swap을 주소로 호출:

이 예제 실행

실행 결과가 여기에 표시됩니다.

실행: 호출 전 3, 5 → 함수 안 5, 3 → 호출 후 5, 3. 원본이 바뀝니다.


2. 지역변수와 전역변수

기억 클래스는 변수를 기억 공간의 어디에, 언제까지, 어떻게 초기화할지입니다. 자료형이 “몇 바이트·어떤 값”이라면, 기억 클래스는 유효 범위와 존속 기간(수명) 입니다.

선언 위치에 따라:

지역변수 (local)전역변수 (global)
선언함수·블록 안함수 밖(파일)
보이는 곳그 블록/함수만선언 이후 프로그램 전체
매개변수지역변수의 성질—

같은 이름이면 지역이 전역을 가립니다. 전역은 파일 앞쪽에 두는 것이 좋습니다. 선언보다 앞에서는 아직 효력이 없기 때문입니다.

전역은 꼭 필요할 때만 씁니다. 어디서든 바뀌면 결과를 추적하기 어렵습니다. 지역을 쓰면 함수가 독립적이고, 디버깅이 쉽고, 기억 공간을 아낄 수 있습니다. 재귀도 지역변수가 있어야 구현이 자연스럽습니다.

2.1 예제 5-9 — 함수마다 다른 i

이 예제 실행

실행 결과가 여기에 표시됩니다.

실행: main i = 10 → fcn1 i = 20 → main i = 10. fcn1의 i = 20은 main의 i를 건드리지 않습니다.

2.2 예제 5-10 — 블록 안의 같은 이름

안쪽 중괄호는 새로운 블록입니다. 거기서 선언한 x는 바깥 x를 가리고, 블록이 끝나면 사라집니다. 바깥 y는 안쪽에서도 같은 변수입니다.

이 예제 실행

실행 결과가 여기에 표시됩니다.

실행: A : 2, 4 → B : 5, 5 → A : 2, 5. 안쪽 x = 5는 바깥 x를 바꾸지 않고, y++만 바깥에 남습니다.

2.3 예제 5-11 — 전역변수

함수 밖 int x;는 초기화하지 않으면 0입니다. 모든 함수에서 보입니다.

이 예제 실행

실행 결과가 여기에 표시됩니다.

실행: 1) x = 0 → 2) x = 1.


3. 기억 클래스의 종류

수명·초기화·범위를 정하는 지정자입니다.

형식:  기억클래스  자료형  변수명;
예:    auto int n;
       static int s;
       extern char s[];
       register int i;

3.1 자동변수 (auto)

함수(블록)가 시작될 때 스택에 만들어지고, 끝나면 사라집니다. auto는 적어도 되고, 지역변수는 보통 생략합니다. 초깃값은 쓰레기 값이므로 반드시 초기화하세요.

3.2 정적변수 (static) — 예제 5-12

static이면 컴파일 때 고정 공간이 생기고, 함수가 끝나도 값이 남습니다. 초기화는 한 번이며, 안 적으면 0입니다.

함수 안의 static은 전역처럼 값이 유지되지만, 그 함수 안에서만 보입니다. 다른 파일이 extern으로 가져갈 수 없습니다.

이 예제 실행

실행 결과가 여기에 표시됩니다.

실행: s는 1,2,3,4,5로 늘고 a는 매번 1입니다.

3.3 외부변수 (extern) — 예제 5-13

함수 밖에 둔 전역과 같습니다. 초깃값 0, 프로그램이 끝날 때까지 유지.
다른 점은 다른 파일에서 extern으로 그 이름을 가리킬 수 있다는 것입니다. 여러 .c가 변수를 공유할 때 씁니다.

교재는 5-13.c(정의)와 5-13_1.c(extern char s[];) 두 파일입니다. 이 사이트 실행기는 한 파일만 컴파일하므로, 아래는 한 파일로 합친 실행용입니다. 의미는 “s는 파일 전체에서 공유된다”입니다.

이 예제 실행

실행 결과가 여기에 표시됩니다.

두 파일로 쓸 때의 골격은 다음과 같습니다.

/* 5-13.c — 정의 */
char s[100];
void extern_ex(void);

/* 5-13_1.c — 다른 파일에서 참조 */
extern char s[];
void extern_ex(void) {
    printf("%s\n", s);
}

로컬에서 두 파일을 함께 링크할 때: clang 5-13.c 5-13_1.c -o out

3.4 레지스터 변수 (register) — 예제 5-14

CPU 레지스터에 두면 메모리보다 빠를 수 있어, 반복 카운터에 쓰라는 것이 교재의 설명입니다. 성질은 자동변수와 같고, 전역으로는 못 씁니다. 레지스터 개수는 적으므로 요즘은 컴파일러가 알아서 넣고, register는 거의 무시됩니다.

교재 제약: 레지스터 폭을 넘는 형은 못 쓰고, 메모리에 없으므로 &변수는 오류라고 합니다. 현대 clang은 register를 무시해서 &가 되는 경우가 많습니다. 아래는 sizeof와 1~10의 합만 실행합니다.

이 예제 실행

실행 결과가 여기에 표시됩니다.

맥에서 흔히 num1 size = 4, num2 size = 8, 합 num2 = 55. 교재는 sizeof를 %d로 찍지만, 이 사이트는 %zu를 씁니다(2강).

3.5 한눈에 보기

클래스예약어위치기억초깃값수명범위
자동auto 또는 생략함수·블록 안동적(스택)미정(쓰레기)벗어나면 소멸그 블록
정적static함수·블록 안정적0프로그램 끝까지그 블록만
정적(전역)static함수 밖정적0끝까지그 파일
외부(정의) / extern함수 밖 등정적0(정의)끝까지정의·참조한 파일
레지스터register함수·블록 안동적미정소멸그 블록

동적 = 필요할 때 자리를 잡음. 정적 = 실행 내내 같은 자리. 소멸 = 블록을 벗어나면 값 없음. 불멸 = 프로그램이 끝날 때까지 유지.


4. 예제 프로그램

예제 5-1 — 대·소문자 바꾸기

영문 대문자면 +32로 소문자, 소문자면 −32로 대문자(ASCII 차이 32, 2강). 숫자·기호는 그대로입니다.

이 예제 실행

실행 결과가 여기에 표시됩니다.

교재 결과: CSknou1004@KNOU.AC.KR

예제 5-2 — 재귀함수로 factorial

재귀함수는 자기 자신을 호출하는 함수입니다. n! = n × (n-1)!, 1! = 1. 교재는 1~8만 받습니다.

이 예제 실행

실행 결과가 여기에 표시됩니다.

5 → 120. line을 "4"로 바꾸면 24입니다.

예제 5-3 — 값 전달 vs 참조 전달

이 예제 실행

실행 결과가 여기에 표시됩니다.

값 호출 후에도 2, 4, 6. 참조 호출 후 0, 0, 0. by_value 안에서만 바꾸고 쓰지 않으면 컴파일러가 “안 쓰는 매개변수” 경고를 낼 수 있습니다. 그 경고가 곧 “원본과 무관한 복사본”이라는 뜻입니다.


5. 지금 실습할 것

아래는 값/참조 swap과 static 카운터를 한 파일에 모았습니다.

  1. 그대로 실행해 값 호출 후 원본이 그대로인지, 주소 호출 후 바뀌는지 확인
  2. static_test를 5번 호출해 s만 증가하는지 보기
  3. (선택) 「표준 입력」에 문자열을 넣고 예제 5-1처럼 대소문자를 뒤집어 보기
  4. factorial의 5를 6으로 바꿔 720인지 확인

코드 실습 (C)

실행 결과가 여기에 표시됩니다.

확인 퀴즈

1. 값에 의한 전달(call by value)에서 형식매개변수를 바꾸면?
2. 참조에 의한 전달에서 호출할 때 실매개변수 앞에 붙이는 것은?
3. 예제 5-8처럼 swap(&a, &b)를 호출한 뒤 a=3, b=5 이면?
4. 같은 범위에 이름이 같은 지역변수와 전역변수가 있으면?
5. 함수 밖 int x; 를 초기화하지 않으면 초깃값은?
6. 함수 안 static int s; 의 특징으로 바른 것은?
7. extern을 쓰는 주된 이유는?
8. register 변수에 대한 설명으로 바른 것은?
9. 재귀함수란?
10. 예제 5-3에서 by_value(x,y,z) 후 x,y,z는? (처음 2,4,6)

스스로 점검