자동변수의 생존기간
- 자동변수는 함수가 호출되어 변수 선언문이 실행될 때 메모리에 기억공간이 할당된다.
-> 세 개의 자동변수를 사용하는 함수의 예(매개변수==자동변수)int sum(int a, int b) { int res; //메모리가 stack에서 생성 res=a+b; return res; }
- 자동변수는 함수가 리턴되고 나면 더 이상 사용되지 않는 기억공간이므로 리턴될 때 메모리에서 사라진다.
기억공간의 할당과 회수
- 자동변수의 할당과 회수는 하나의 함수 내에서도 발생
-> 함수 내에서 새로운 블록을 열고 변수를 선언하면 그 블록이 끝날 때 변수는 사라진다. 결국 변수의 사용범위는 블록 내부이다.#include <stdio.h> int main() { int a=10, b=20; //메인함수 블록에 선언된 변수 printf("바꾸기 전 a : %d, b : %d\n", a, b); { //새로운 블록의 시작 int temp; //안쪽 블록에 선언된 변수 temp = a; //temp는 이 블록에서만 사용가능, 나가면 사용 불가능 a=b; b=temp; } //블록이 끝나면서 temp변수는 사라진다. printf("바꾼 후 a : %d, b : %d\n", a, b); return 0; }
중첩된 블록에서 같은 이름의 변수를 사용할 경우
- 중첩된 블록에서 같은 이름의 변수를 선언하면 가장 가까운 블록에 선언된 변수에 우선권이 있다.
#include <stdio.h> int main() { int val=10; //1번 각 변수의 사용범위는 { // 변수가 선언된 블록의 int val=20; //2번 끝까지이다. { val++; //가장 가까이 선언된 2번 변수를 사용 } printf("val : %d\n", val); } printf("val : %d\n", val); return 0; } ->출력결과 val : 21 //2번 출력 val : 10 //1번 출력
자동변수가 메모리에 할당되는 방법
- 자동변수는 함수의 호출 순서에 따라 할당되고 리턴될 때 회수된다.
따라서 가장 나중에 할당된 자동변수가 가장 먼저 사라진다.
-> 메인함수 A, B로 구성된 함수들의 호출과 리턴 예
- 자동변수는 함수의 호출과 리턴 규칙에 맞게 메모리의 스택 영역에 할당된다.
값을 복사해서 넘겨준다(call by value)
- 일반적인 함수의 호출방법으로 호출함수의 전달인자가 피호출함수의 매개변수에 복사된다.
- 피호출함수는 리턴할 때 리턴값을 복사하여 호출함수로 전달한다.
#include <stdio.h> int add_ten(int); int main() { int a=10; a=add_ten(a); printf("a : %d\n", a); return 0; } int add_ten(int b) { b=b+10; return b; }
포인터를 사용한다(call by pointer)
- 호출함수에서 변수의 포인터를 전달인자로 주고
- 피호출함수에서는 이 포인터를 받아 호출함수의 변수를 참조하는 방식이다.
#include <stdio.h> void add_ten(int *); int main() { int a=10; a=add_ten(&a); printf("a : %d\n", a); return 0; } void add_ten(int *b) { *b = *b+10; }
연습문제
- 메인함수에서 숫자 두 개 입력받고 곱하여 출력하는 프로그램
포인터를 리턴하는 함수
- 피호출함수에서 포인터를 리턴하여 호출함수가 피호출함수의 기억공간을 참조할 수 있도록 할 수 있다.
- 포인터를 리턴하는 함수는 리턴값의 형태가 포인터형이 된다.
-> int형 변수의 포인터를 리턴하는 경우int *add_ten(int); //int형 기억공간의 포인터를 리턴
- 자동변수의 포인터를 리턴하여 호출함수에서 다시 참조하는 것은 위첨하다
-> 변수 lifetime#include <stdio.h> int *add_ten(int); int main() { int a=10; int *ap; ap=add_ten(a); printf("a : %d\n", *ap); //포인터 변수 ap로 add_ten함수의 변수를 참조 return 0; } int *add_ten(int b) { b+=10; return &b; //add_ten함수의 자동변수 b의 포인터를 리턴 } -> 오류발생
- 포인터를 리턴하는 경우는 함수가 리턴된 후에도 그 기억공간이 계속 유지되는 경우만 가능하다.
-> 호출함수로부터 포인터를 받아서 다시 리턴하는 경우(문자열 처리 함수들의 예)char *strcpy(char *A, char *B); //B의 문자열을 A에 복사하고 A를 리턴 char *strcat(char *A, char *B); //B의 문자열을 A에 붙인 후에 A를 리턴 char *gets(char *A); //A에 문자열을 입력하고 A를 리턴
- 포인터를 리턴하면 좀더 다양한 방식으로 프로그램을 작성할 수 있다.
-> 두 문자열을 붙인 후에 그 결과를 바로 확인하는 예#include <stdio.h> #include <string.h> int main() { char src[80]="빈대"; printf("결과 : %s\n", strcat(src, "떡")); return 0; }
static int sum; -> 정적 변수의 선언
#include <stdio.h>
void increase();
int main()
{
int i;
for(i=0; i<5; i++)
{
increase();
}
return 0;
}
void increase()
{
static int sum=0; //함수가 호출될 때 싱행하는 게 아니라 프로그램이 시작될 때 실행됨
sum++;
printf("sum : %d\n", sum);
}
-> 출력결과
sum : 1
sum : 2
sum : 3
sum : 4
sum : 5
정적변수는 포인터를 리턴할 수 있다.
- 정적변수는 함수가 리턴된 후에도 그 기억공간이 유지되기 때문에 그 포인터를 리턴하여 호출하는 함수에서 참조할 수 있다.
#include <stdio.h> int *increase(); int main() { int i; int *sp; for(i=0; i<5; i++) { sp=increase(); printf("sum : %d\n", *sp); } return 0; } int *increase() { static int sum=0; //함수가 호출될 때 싱행하는 게 아니라 프로그램이 시작될 때 실행됨 sum++; return ∑ } -> 출력결과 sum : 1 sum : 2 sum : 3 sum : 4 sum : 5
★자동변수는 메모리에서 변수가 사라지기 때문에 더이상 포인터를 가지고 접근할 수 없었는데 정적변수는 리턴 후에도 변수가 유지되기 때문에 포인터를 사용해서 접근할 수 있다.★
연습문제
- 함수가 호출될 때마다 배열요소의 값을 차례로 리턴하는 함수
-> 즉, 함수가 처음 호출되면 0.1을 리턴하고, 두번쨰 호출되면 0.3을 리턴한다.
줄8 -> 0.1이 시작주소값이 됨
줄20 -> 순서를 기억하도록 사용됨
#include <stdio.h>
void exchange();
int a, b; //변수를 함수 밖에 선언
int main()
{
printf("정수값 두 개를 입력 : ");
scanf("%d%d", &a, &b);
exchange(); //전달인자 없이 호출된다.
printf("a : %d, b: %d\n", a, b);
return 0;
}
void exchange()
{
int temp;
temp=a; //외부변수 a, b를
a=b; //자신의 변수인 것처럼
b=temp; //사용한다.
}
외부변수를 사용할 때 주의할 점
- 외부변수는 여러 함수가 공유하므로 데이터의 안전성을 보장 받을 수 없다. 따라서 외부변수의 값에 변화가 생긴다면 다른 함수에 미치는 영향도 살펴야 한다.
- 외부변수와 같은 이름의 변수를 함수 내에서 선언하는 경우는 함수내에서 선언된 변수를 우선적으로 참조한다.
연습문제
- 값을 처리하는 함수
get_next함수
현재 배열요소의 값을 리턴하고 다음 배열 요소로 이동
get_before함수
현재 배열요소의 값을 리턴하고 이전 배열 요소로 이동
-.
extern 지정자
- extern 지정자는 컴파일러에게 변수가 현재 범위가 아닌 다른 곳에서 선언되어 있음을 알리는 역할을 한다.
-> 지정된 변수는 반드시 다른 소스 파일에 정의되어 있어야 한다.- 주로 외부파일에서 정의된 전역 변수를 현재 파일에서 사용하려고 할 때 사용되는 경우가 많다.
register 지정자
- register 지정자는 변수를 레지스터 변수로 만든다.
- 레지스터 변수는 지역변수만 가능하다.
- 레지스터 변수는 메모리에 저장되는 것이 아니라 레지스터에 저장된다.
- 레지스터는 cpu내부에 위치하는 입출력 속도가 매우 빠른 임시 저장 장소라 할 수 있다.
//register1.c #include <stdio.h> int main() { register int i; int j; for(i=0; i<100; i++) j+=i; printf("%u %u\n", &i, &j); return 0; }
변수의 유효 범위