-목차-
1. 자동변수와 정적변수
2. 변수의 속성
3. 함수 호출 시 배열 크기를 전달하는 방법
4. C 함수 특성의 이해
1. 자동변수와 정적변수
- CSV 문제 다시보기
#include <stdio.h>
int main(void) {
int i;
int num = 0, sum = 0;
char str[] = "123,456,789";
for (i=0;str[i];i++){
if (str[i] == ','){
sum += num;
num = 0;
} else {
num = num*10 + (str[i]-'0');
}
}
sum += num;
printf("Sum of all values in ");
printf(" CSV[%s] : %d",str,num);
return 0;
}
위의 코드는 5장에서 작성했던 CSV 코드이다. ex) "123,456,789" -> 123 + 456 + 789 (단, csv 문자열 내의 수는 모두 음이 아닌 정수라고 가정한다.)
허나 CSV 프로그래밍 부분을 다른 함수를 사용하여 결과값을 반환하는 형식으로 짜보면 어떨까?
① 자동변수 ★★★
#include <stdio.h>
int csv_get_value(char str[]){
int i=0; // index for str, 자동변수
int num = 0;
if (i<0) return -1;
while(str[i]>='0' && str[i]<='9'){
num = num*10 + (str[i]-'0');
i++;
}
if (!str[i]) i = -1; // str[i] == '\0'
else i++; // str[i] == ','
return num;
}
int main(void)
{
int num;
char str[] = "123,456,789";
while ((num=csv_get_value(str))>=0){
printf("%d\n",num);
}
return 0;
}
위와 같이 int csv_get_value(char str[]); 이라는 함수를 만들어주고 호출될 때마다 str으로 전달되는 CSV 문자열에서 정수 값을 앞에서부터 순차적으로 하나씩 추출하여 return하도록 하자.
그러나 이 코드에는 큰 문제점이 있다..!!!!
함수가 호출될 때마다 자동변수 i값은 기존 값을 유지하지 못한 채 0으로 초기화되기에 위의 코드를 실행하면 123 만 무수히 많이 출력되는 것을 볼 수 있다.
② 정적변수 ★★★
자동변수로 인한 문제점은 다음의 방법들로 해결가능하다.
(1) 전역변수로 해결하기
#include <stdio.h>
int i = 0; // index for str, 전역변수
int csv_get_value(char str[]){
int num = 0;
if (i<0) return -1;
while(str[i]>='0' && str[i]<='9'){
num = num*10 + (str[i]-'0');
i++;
}
if (!str[i]) i = -1; // str[i] == '\0'
else i++; // str[i] == ','
return num;
}
int main(void)
{
int num;
char str[] = "123,456,789";
while ((num=csv_get_value(str))>=0){
printf("%d\n",num);
}
return 0;
}
위의 코드와 같이 전역변수를 설정함으로써 함수 호출이 끝나더라도 i값을 유지할 수 있다.
(그러나 전역 변수는 함수 간 의존성이 커서 논리적 오류 가능성이 크기에 꼭 필요한 경우가 아니라면 사용하지 않는 것이 좋다.)
(2) 정적변수로 해결하기
#include <stdio.h>
int csv_get_value(char str[]){
static int i=0; // index for str, 정적변수
int num = 0;
if (i<0) return -1;
while(str[i]>='0' && str[i]<='9'){
num = num*10 + (str[i]-'0');
i++;
}
if (!str[i]) i = -1; // str[i] == '\0'
else i++; // str[i] == ','
return num;
}
int main(void)
{
int num;
char str[] = "123,456,789";
while ((num=csv_get_value(str))>=0){
printf("%d\n",num);
}
return 0;
}
위의 코드와 같이 static이라는 말을 붙여줌으로써 정적변수를 만든다.
static 변수 초기화는 1번만 이루어지기에(첫번째 함수호출에서 초기화가 이루어짐) 두번째 호출부터는 초기화가 이루어지지 않고 첫번째 호출이 종료될 때의 값을 계속 유지함.
static 변수의 값은 소스코드의 실행이 종료될 때까지 해당 변수값을 계속 유지한다.
전역변수도 static 변수다.
+) 전역변수와 정적변수의 차이
- 전역변수와 정적변수의 lifetime은 동일하다. 해당 프로그램이 끝날 때까지 한번 할당된 변수 메모리와 값은 유지된다.
- 전역변수와 정적변수의 scope는 서로 다르다. 전역변수는 해당 프로그램의 어느 함수에서도 접근이 가능한 반면 정적변수는 변수가 선언된 함수 내에서만 접근이 가능하다.
2. 변수의 속성
※ 변수 : 값/데이터 저장하는 데 사용되는 기억 장소/메모리 공간
※ 변수의 속성 ★★
● Identifier : 식별자를 가진다.
- 변수에 접근하기 위해서는 당연히 식별자를 알고 있어야한다.
● Value : 값을 가진다.
- 코드에서 변수의 식별자를 이용하여 값을 읽어 오거나 변화시킬 수 있다.
● Type : 자료형을 가진다.
- 메모리에 변수가 어떤 식으로 저장될지, 저장된 이진 데이터가 어떻게 해석될지를 결정하는 자료형을 가진다.
● Memory Size : 값을 저장하기 위해 특정 크기의 메모리 공간을 가진다.
- 자료형에 따라 필요한 메모리 공간의 크기가 다르다. (sizeof( ) 연산자로 변수가 가지는 메모리 공간의 크기 값을 알 수 있다.)
● Scope : 코드 내에서 변수의 유효범위가 존재한다.
- 지역 변수 : 변수의 유효 범위가 변수가 선언된 함수/블록 내로 제한되는 변수
- 전역 변수 : 변수의 유효 범위가 변수가 선언된 파일 또는 프로그램 전체인 변수
● Lifetime/extent : 변수가 유지되는 시간이 있다.
- 정적 변수 : 프로그램 시작 시 변수가 생성되어 프로그램이 종료될 때까지 유지되는 변수. (참고로 모든 전역변수는 정적변수이다.)
- 자동 변수 : 함수가 호출될 때 생성되어 함수가 종료되면 사라지는 변수. (static이 덧붙여지지 않은 지역변수는 자동 변수이다.)
● Address : 메모리 공간에 대한 주소를 가진다. (포인터 파트에서 더 자세히 다룰 예정)
3. 함수 호출 시 배열 크기를 전달하는 방법
ex) integer 배열의 합을 구하는 함수를 작성해보자. (단, 배열의 원소 값은 모두 음이 아닌 정수이다.)
(1) 배열의 크기를 명시적으로 적어주기
# include <stdio.h>
int array_sum(int array[], int size){
int i, sum = 0;
for (i=0; i<size;i++){
sum += array[i];
}
return sum;
}
int main(void){
int array1[3] = {1,2,3};
int array2[5] = {2,4,6,8,10};
int array3[] = {3,5,7};
int sum1 = array_sum(array1,3);
int sum2 = array_sum(array2,5);
int sum3 = array_sum(array3,sizeof(array3)/sizeof(int));
printf("Sum of array1 : %d\n",sum1);
printf("Sum of array2 : %d\n",sum2);
printf("Sum of array3 : %d\n",sum3);
}
- 함수를 호출할 때 명시적으로 알려줌으로써 배열의 길이를 전달할 수 있다.
(2) 배열의 크기를 묵시적으로 알아내기
# include <stdio.h>
int array_sum(int array[]){
int i, sum = 0;
i = 0;
while(array[i] != '\0'){
sum += array[i];
i += 1;
}
return sum;
}
int main(void){
int array1[3] = {1,2,3};
int array2[5] = {2,4,6,8,10};
int array3[] = {3,5,7};
int sum1 = array_sum(array1);
int sum2 = array_sum(array2);
int sum3 = array_sum(array3);
printf("Sum of array1 : %d\n",sum1);
printf("Sum of array2 : %d\n",sum2);
printf("Sum of array3 : %d\n",sum3);
}
- 배열의 끝에는 항상 '\0'(NULL)이 들어가기에 while문을 통해 조건을 검으로써 배열의 길이를 알리는 인자가 필요하지 않다.
4. C 함수 특성의 이해
※ Call by Value ★★★
- 함수 호출 시 전달하는 변수 또는 ecpression 값은 그 값 자체가 전달되는 것이 아니라 매개변수로 복사된다. (이런 함수 호출 방식을 "Call by Value"라고 하며 C 언어의 중요한 특징이다.)
- 함수 내에서 매개변수의 값이 변하더라도 함수 호출 시에 사용된 변수의 값은 변화가 없다.
ex)
# include <stdio.h>
void swap(int a, int b){
int t = a;
a = b;
b = t;
}
int main(void){
int a = 3, b = 7, t;
printf("(a,b) = (%d,%d)\n",a,b);
t = a;
a = b;
b = t;
printf("(a,b) = (%d,%d)\n",a,b);
swap(a,b);
printf("(a,b) = (%d,%d)\n",a,b);
return 0;
}
위 코드의 결과는 다음과 같다.
(a,b) = (3,7)
(a,b) = (7,3)
(a,b) = (7,3)
이를 통해 함수 호출을 통해 사용한 변수의 값에는 변화가 없음을 알 수 있다.
※ 반환하려는 값이 여럿인 경우
- C 함수에서는 return을 통해 하나의 값만을 결과로 전달할 수 있기에 여러 값을 동시에 반환하지 못한다. (다음 장에서 배울 pointer을 통해 해결할 수 있다.)
※ 배열이 인자인 함수 ★
- 단순 변수 인자는 매개변수로 복사되지만 배열은 함수 호출시 주소를 전달하기 때문에 배열 안의 값은 바뀔 수 있다.
ex)
# include <stdio.h>
int swap(int array[]){
int i = 0;
while(array[i] != '\0'){
array[i] = -1;
i+=1;
}
return 0;
}
int main(void){
int array[4] = {1,2,3,4};
printf("array[%d,%d,%d,%d]\n",
array[0],array[1],array[2],array[3]);
swap(array);
printf("array[%d,%d,%d,%d]\n",
array[0],array[1],array[2],array[3]);
}
위 코드의 결과는 다음과 같다.
array[1,2,3,4]
array[-1,-1,-1,-1]
- 자세한 설명은 8장에서 다루겠다.
'CS > C, C++' 카테고리의 다른 글
[C/C++] 07 - 3 함수와 모듈러 프로그래밍 (0) | 2021.11.02 |
---|---|
[C/C++] 07 - 2 표준 라이브러리 함수의 사용 (0) | 2021.10.29 |
[C/C++] 03 - 2 반복문과 배열 기초 (0) | 2021.10.17 |
[C/C++] 03 - 1 제어 구조와 조건문 기초 (0) | 2021.10.17 |
[C/C++] 02 - 2 자료형과 연산 기초 (0) | 2021.10.17 |