티스토리 뷰
다른 언어에서 본 가변인자 함수
- 프로그래밍을 처음 배울때 c언어로 배웠고 그 후에는 java나 js같은 것들을 배웠는데, 조금 돌이켜보면 은근히 개념적으로 비슷한 것들이 참 많다.
- js 배울때는 js에서 함수는 일급함수(고차함수) 라고 배우면서 의미는 변수에 함수를 대입할 수 있다는 것이었다.
- 이 일급함수 개념을 배우면서 c언어 먼저 배운 사람이라면, 함수포인터가 생각났던 건 나만 그런건 아닐 것이다.
- 다만 c언어를 한창 쓸때는 내 실력이 지금보다 낮은 것도 있고 언어 자체가 절차지향이 기본인 언어라서 그런것도 있어서 함수포인터는 거의 사용하지 않았다. 물론 지금도 c로 만든다면 잘 사용 안할거 같긴하다.
- 여튼 js에서 함수의 인자는 갯수나 타입에 관계없이 거의 마음대로(물론 책임도 프로그래머가 지겠지만) 사용할 수 있는데 위에서 말한 가변인자 함수가 약간 비슷한 개념 같아서 주저리주저리 얘기해봤다. 다시 본론으로 돌아가자
c/c++에서의 가변인자 함수
- printf 와 scanf를 보면 일반적인 프로그래머가 만드는 함수와는 조금 다르다.
- 둘다 당연히 함수지만, 인자를 1개 또는 0개 아니면 10개도 줄 수 있기 때문이다.
- 이유는 두 함수가 가변인자 함수이기 때문이다.
가변인자 함수를 직접 만들어보자.
#include <stdio.h>
int Sum(int n, ...);
int main(int argc, char* argv[]){
Sum(2, 1, 2);
Sum(3, 1, 2, 3);
Sum(4, 1, 2, 3, 4);
return 0;
}
int Sum(int n, ...){
printf("n=%d \n", n);
return 0;
}
//출력
//n=2
//n=3
//n=4
- 기본적인 사용방법은 이렇지만 c언어는 이렇게 친절할리가 없다는 생각이 들었는데 역시나였다.
- 이렇게 사용하면 가변인자로 전달된 인자들을 사용할 수가 없다고 한다. 그래서 추가적으로 가변인자를 추출하는 과정을 거쳐야 한다.
stdarg.h 를 이용한 전달된 인자들을 추출해서 사용하기.
- 가변인자를 사용하기 위해서는 다음 과정들을 거쳐야 한다.
- 가변인자를 가리킬 수 있는 참조자를 선언한다.
- 참조자가 가변인자를 실제로 참조할 수 있도록 한다.
- 참조자를 통해 전달된 정보를 추출한다.
- 참조자가 더 이상 가변인자를 가리키지 않도록 해제한다.
- 여기서 참조자란 전달된 인자들을 변수의 선언처럼 이름을 붙이는 것이라고 생각하면 이해가 더 쉬울 것 같다.
- 위의 4가지 순서들을 하기 위해서는 헤더파일에 <stdarg.h>를 포함해 주어야 한다.(c++에서는 <cstdarg>인데 사용방법은 동일하다.)
- 그 후에 아래 처럼 사용할 수 있다.
- va_list
- va_start
- va_arg
- va_end
- 아래 예제는 실제 사용 방법이다.
#include <stdio.h>
#include <stdarg.h>
int Sum(int n, ...);
int main(int argc, char* argv[]){
printf("1+2=%d \n", Sum(2, 1, 2));
printf("1+2+3=%d \n", Sum(3, 1, 2, 3));
printf("1+2+3+4=%d \n", Sum(4, 1, 2, 3, 4));
return 0;
}
int Sum(int n, ...){
int sum = 0;
int i;
va_list vlist; //1단계 가변인자의 참조자 선언
va_start(vlist, n); //2단계: 참조 대상과 범위 지정
for(i = 0; i < n; i++){
sum += va_arg(vlist, int); //3단계: 값의 추출
}
va_end(vlist); //4단계: 해제
return sum;
}
//출력
//1+2+3
//1+2+3=6
//1+2+3+4=10
- 7,8,9 행의 함수 호출로 가변인자들이 메모리에 저장된다. 다만 이름이 존재하지 않아서 참조자가 필요하다. 그래서 va_list형 변수가 참조자의 역할을 한다.
- 17행에서 va_list형 vlist를 선언하고 19행에서 va_start 함수를 통해서 호출된다. 첫번째 인자로 가변인자를 참조하기 위한 va_list가 두번째 인자로 참조할 인자의 갯수 정보가 전달되어야 한다.
- 21행의 va_arg 함수는 값의 추출을 위해 제공되는 함수다. 첫번째 인자로 참조자의 이름, 두번째 인자로 참조 대상의 자료형이 와야한다. 특히! 두번째 인자인 참조 대상의 자료형이 잘못 되지 않도록 주의해야한다.
- 23행의 va_end 함수로 vlist가 더 이상 가변인자를 참조하지 않도록 해제해줘야 한다(메모리의 안전한 사용을 위해서)
몇가지 의문점
- 가변인자라서 va_list형으로 선언하고 va_start로 정의하고 va_arg로 사용하는 것까지는 일반적인 보통의 변수 사용법이랑 유사해서 이해가 가는데 va_end를 해주는 이유는 무엇일까?
- 조금 찾아봤는데 stackoverflow에도 영어로 비슷한 질문이 있어서, 원하는 답이 있나 봤는데 영어라서 잘 못알아 들어서 그런건지도 모르겠지만 그냥 표준이 그렇고 스택이나 레지스터에 저장된다고만 나와있다. 나중에 영어 더 배워서 알아들어보자..
- 그리고 이건 이미 풀린 궁금증이지만, printf나 scanf는 우리가 구현한 가변인자 함수처럼 int n의 가변인자 갯수를 받지 않는데 어떻게 구현하는 건지 궁금해서 apple에서 공개한 printf 함수 구현 코드를 봤는데 그냥 인자로 전달한 format(""부분) 을 '\0'을 만날때까지(문자열의 끝) 읽어서 %d나 %s 같은 부분들을 하나하나 읽어서 갯수를 파악하는 거였다. 뭐랄까 굉장이 단순하다고할까 아니면 무식하다고 할까.. 거기에 mutex lock으로 thread에 대비한 상호배제 처리도 되어있었다. 역시 구현이 쉽지 않아.. 아래는 내가 본 printf 함수 원형이다.
printf의 함수 구현 원형
https://opensource.apple.com/source/xnu/xnu-201/osfmk/kern/printf.c.auto.html
마지막 의문점
- 맨 처음 말했던 js는 가변인자가 기본적으로 지원되는 언어이고 또 가변인자의 형식이 다 달라도 된다.
- 물론 그 이유는 js의 모든 변수는 기본적으로 c언어의 auto변수라고 보면 되기 때문에 어떤 자료형이든지 담을 수 있기 때문이라는 것은 알고 있다.
- 여튼 말하고 싶은 것은 위의 예제에서 21행 설명에서 참조 대상의 자료형이 잘못되지 않도록 주의해야하고 직접적으로 자료형을 명시하는 것을 보니까 1가지의 자료형으로만 가변인자가 가능한 것 같다.
- 아마 템플릿을 활용하면 내가 생각한 여러가지 자료형으로도 받는 게 가능하지 않을까? 라고 생각했지만, 결국 템플릿도 여러가지 자료형을 사용하는 것은 가능하지만 한번에 하나의 자료형만 가능한 것은 동일하므로 안될거 같다.
'C\\C++' 카테고리의 다른 글
[C/C++] linux 멀티플렉싱. select 함수 개념 정리. (0) | 2023.01.15 |
---|---|
[C/C++] 10, 2, 16, 8 진수 변환 규칙 (0) | 2023.01.07 |
[C/C++] 조건부 컴파일(#if #ifdef #ifndef #endif···) (0) | 2023.01.01 |
[C/C++] #define 매개변수의 결합과 문자열화 (0) | 2022.12.28 |
[C/C++] 선행처리기(전처리)와 매크로 (0) | 2022.12.26 |
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- ##연산자
- MySql 날짜 차이 구하기
- linux select
- 이중 콜론 연산자
- Java8 #java stream
- c 가변인자
- static의 장점 단점
- 메소드 참조 연산자
- Builder #SuperBuilder
- 나만의 강점
- 표준입출력 함수
- JPA #SPRING #ENTITY #DATABASE
- JPA #cascade
- javascript 문자열 뒤집기
- ajax 403에러
- 전처리기
- java
- #define
- 매크로
- Double.compareTo(Double)
- JavaScript
- 네트워크 오더링
- 영속성전파
- c++ 가변인자
- C++
- c 매크로
- Java Double형 비교방법
- C언어
- JPQL 사용하기
- Til
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
글 보관함