함수와 변수, 함수 인자 심화학습
✓ Comma Seperated Values
- 다양한 자료형과 연산 파트에서 CSV 형식의 문자열에서 값을 추출하여 연산을 수행하는 문제를 다루었는데 코드는 아래와 같았다.
- 근데 모두 음이 아닌 정수로 가정했다.
- CSV 문자열에서 값을 하나씩 추출하는 기능을 개별 함수로 분리해보자.
#include <stdio.h>
int main(void)
{
int i; // index
int num=0, sum=0; // number
char str[]="123,456,789";
for(i=0;str[i];i++) {
if (str[i] == ',') { // new number
sum += num;
num = 0;
} else { // a digit
num = num*10 + (str[i]-'0');
}
}
sum += num;
printf("Sum of all values in ");
printf(" CSV[%s] : %d", str,sum);
return 0;
}
- int csv_get_value(char str[]);
- 함수는 호출 될 때 마다 str으로 전달되는 csv 문자열에서 정수 값을 앞에서부터 순차적으로 하나씩추출하여 return한다.
- 종료 조건이 필요함 → 더 이상 읽어들일 값이 없을 경우 -1을 return 한다.
✓ 함수와 automatic variable
#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;
}
- CSV 문자열(배열)에 대한 index로 유효한 값은 0 이상
- 1을 0부터 +1씩 하며 값을 추출
- 문자열의 끝에 도달하면, 즉 index i로 접근하는 문자 값이 '\0'인 경우 i 값을 -1로 변경 (line 15)
- index i의 값이 -1인 경우 문자열의 끝에 도달하였기에 이후 호출에는 -1을 return (line 8)
- 그런데 이 함수를 호출하면 csv 문자열의 첫번째 값인 123이 반복적으로 추출되며 프로그램이 종료하지 않음
- 그 이유는 변수 i는 함수가 호출될 때 생성되어 메모리가 할당되고 함수가 종료하면 사라지는 자동변수(automatic variable)로서 기존 값을 유지하지 못한 채 함수가 호출 될 때마다 다시 0으로 초기화 되기 때문이다.
※automatic variable -> static variable
#include <stdio.h>
// int i=0; // global variable
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 변수가 되어야 한다.
- global variable는 static 변수로 프로그램 수행 전체 시간 동안 유지되므로 line 2에서와 같이 i를 전역 변수로 설정하는 것이 해결책의 하나일 수 있다. (그러나, 그렇게 되면 함수 간 의존성이 커져 논리적 오류 가능성이 높아져서 꼭 필요할 때만 쓰는 것 추천.)
- 전역 변수를 사용하지 않는 해결책은 line 5와 같이 기존 변수 i선언에 static을 덧붙이는 것이다. 이렇게 하면 변수 i는 지역 변수임에도 static 특성을 가진다. 결과적으로 변수가 기존의 값을 계속 유지한다.
- static 변수 초기화는 1번만 이루어진다. 즉, line 5의 초기화가 매 함수 호출 시마다 이뤄지는 것은 아니다.
✓ 변수의 속성 정리
- 변수: 값/데이터 저장하는 데 사용되는 기억 장소/메모리 공간
- 변수의 속성
- Identifier → 식별자(Identifier)를 가진다.
- 코드에서 변수에 접근하기 위해서는 identifier를 알고 사용할 수 있어야 한다
- Value → 값을 가진다.
- 코드에서 변수의 Identifier를 이용하여 값을 읽어오거나 변화시킬 수 있다
- Type → 자료형을 가진다.
- 메모리에 저장된 이진 데이터를 어떻게 해석할지를 결정하는 자료형을 가진다
- Memory size → 값을 저장하기 위해 특정 크기의 메로리 공간을 가진다.
- 자료형에 따라 필요한 메모리 공간의 크기가 다르다
- sizeof()연산자로 변수가 가지는 메모리 공간의 크기 값을 알아올 수 있다
- Scope → 코드 내에서 변수의 이름이 유효한 범위가 있다.
- 지역 변수 (Local Variable) - 변수의 유효 범위가 변수가 선언된 함수/블록 내로 제한되는 변수
- 전역 변수 (Global Variable) - 변수의 유효 범위가 변수가 선언된 파일 또는 프로그램 전체인 변수
- Lifetime/extent → 변수가 유지되는 시간이 있다
- 정적 변수 (Static Variable) - 프로그램 시작 시 변수가 생성되어 (메모리가 할당되어) 프로그램 종료 때까지 유지되는 변수, 모든 전역 변수는 정적 변수이다. static 지시자를 덧붙인 변수도 정적 변수기 된다
- 자동 변수 (Automatic Variable) - 함수가 호출될 때 생성되어 메모리가 할당되고 종료하면 사라지는 변수이다. static이 덧붙여지지 않을 경우 지역 번수는 자동 변수이다
- Address → 메모리 공간에 대한 주소를 가진다.
- CPU는 메모리에 접근하기 위해서는 주소가 필요하다 → 변수는 메모리 주소를 가진다.
- Identifier → 식별자(Identifier)를 가진다.
함수 호출 시 배열 크기 전달 방법 ①
#include <stdio.h>
int sum_w_size(int nums[], int size)
{
int i, sum=0;
for (i=0;i<size;i++)
sum += nums[i];
return sum;
}
int main(void)
{
int array1[3] = {1,2,3};
int array2[5] = {3,5,7,1,9};
int array3[] = {1,9,2,8,3,7};
int s1, s2, s3;
s1 = sum_w_size(array1,3);
s2 = sum_w_size(array2,5);
s3 = sum_w_size(array3,
sizeof(array3)/sizeof(int));
printf("Sum of Arrary1:%d\n", s1);
printf("Sum of Arrary2:%d\n", s2);
printf("Sum of Arrary3:%d\n", s3);
return 0;
}
배열 관련 함수 작성 시 고려 사항
- 배열을 대상으로 하는 함수의 경우 배열의 크기, 또는 값이 유효한 마지막 배열 요소의 index 값을 알아야 하는 경우가 많다
- 이를 위해 함수 호출 시 배열의 크기를 인자로서 명시적으로 알려주는 접근이 있을 수 있다
- 또 다르 방법은 묵시적 방법인데 배열의 끝, 또는 유효한 마지막 원소 다음에 끝을 알릴 수 있는 특별한 값을 가지는 원소를 추가함으로써 배열의 크기를 알릴 수 있다. 마지막에 NULL문자 ('\0')을 덧붙이는 문자열은 이러한 접근을 택한 대표적 예이다.
integer 배열의 합을 구하는 함수
- 작성한 함수는 다양한 크기의 integer 배열의 합을 계산하고자 한다
- 배열의 원소 값은 모두 음이 아닌 정수라고 가정한다
- sum_w_size()함수는 배열의 크기를 인자 size를 통해 명시적으로 전달하는 함수이다.
integer 함수 호출 시 배열 크기 정보 전달 방법 ②
include <stdio.h>
#define END_MARK -1
int sum_w_endmark(int nums[])
{
int i=0, sum=0;
while (nums[i] != END_MARK)
sum += nums[i++];
return sum;
}
int main(void)
{
int array1[4] = {1,2,3,END_MARK};
int array2[6] = {3,5,7,1,9,END_MARK};
int array3[] = {1,9,2,8,3,7,END_MARK};
int s1, s2, s3;
s1 = sum_w_endmark(array1);
s2 = sum_w_endmark(array2);
s3 = sum_w_endmark(array3);
printf("Sum of Arrary1:%d\n", s1);
printf("Sum of Arrary2:%d\n", s2);
printf("Sum of Arrary3:%d\n", s3);
return 0;
}
- sum_w_endmark()는 유효한 마지막 원소 다음에 끝을 알리는 특별한 값을 가지는 원소를 추가하여 배열의 크기를 알리는 접근법을 택한 함수다.
- 배열의 크기를 알 필요가 없고 별도의 크기 정보 전달을 위한 인자가 필요없다는 것이 장점이다.
- 유효한 값은 음이 아닌 정수이며 END_MARK 값으로는 -1을 사용한다. (line 2)
- line 12, 13, 14에 보이듯 함수 호출에 사용할 배열 마지막에 END_MARK원소를 추가하였다. 이에 따라 배열의 크기도 원소 하나만큼 커진다.
C 함수 특성 이해: Call by Value Revisited
C의 매개 변수 전달 방법
- 함수 호출 시 ( )안의 변수 또는 expression 값이 매개변수로 복사 됨
- 변수 자체가 함수로 전달되는 것이 아님
- 이런 함수 호출 방식을 "Call by Value"라고 하며 C언어의 중요한 특징임
- 함수 내에서 매개변수의 값이 변하더라도 함수 호출 시 사용한 변수에는 변화가 없음 !!!
- 함수 호출 시 사용한 변수의 예기치 않은 변화를 방지하여 보호함
Call by Value의 한계
- 구현하려는 함수가 함수 호출을 통해 호출 시 사용한 변수의 변화를 필요로 한 경우
- 예를 들어 a와 b의 값을 서로 교환(swap)하는 swap()함수 구현을 시도해보자!
C 함수 특성 이해: 전달하려는 결과가 여럿인 경우
C함수에서는 return 값을 통해 하나의 값만을 결과로 전달할 수 있다
함수를 통해 획득한 하나 이상의 결과를 전달하고자 할 때는 어떻게 하는가? (가령, 정수 a,b를 입력받고 합과 곱을 결과로 돌려주려면 return 만으로는 부족하다.
=> 이것은 Call by Value 방식의 매개 변수 전달 방식으로 구현할 수 없다. -> 포인터가 해결책이다!
C함수 특성 이해: 배열이 인자인 함수
앞서 C함수는 Call by Value 특성으로 함수 호출에 쓰인 변수가 함수 호출에 의해 값이 변하지 않는 것이 특징이라고 설명하였다.
sum_w_endmark2()함수는 앞서 소개한 sum_w_endmark()를 일부 수정한 것으로 line 8 에서 num[i]의 값을 END_MARK로 변경한다.
Call by Value 특성을 감안하면 line 21의 함수 호출에서 쓰인 변수 array1[]의 값은 함수 호출로 인해 값이 바뀌지 않아야 한다.
실행 결과는 array1[]의 값은 바뀐다는 것을 알 수 있다. -> 이것도 포인터를 공부해야함.
요약
'Language Study > C' 카테고리의 다른 글
[C/C++] 07 - 03 함수와 Modular Programming (0) | 2022.11.02 |
---|---|
[C/C++] 07 - 02 STANDARD LIBRARY 함수의사용 (0) | 2022.11.02 |
[C/C++] 04 - 함수 기초 - 함수 선언과 변수 유효 범위 (1) | 2022.10.03 |
[C/C++] 04 - 함수 기초 - 함수의 개념과 호출 구조, 재귀 호출 (0) | 2022.10.03 |
[C/C++] 03 - 제어 구조 기초 - 반복문과 배열 기초 (0) | 2022.10.03 |