함수 선언에 대해서

gkepsgds의 이미지

K&R C 형태로 함수를 선언/정의 할려구 합니다.
컴파일러는 CC를 사용하고 있습니다.

void log_save(fmt, ...)
char *fmt;
{
	va_list	argptr;
	char	msg[2000], dd[9], hms[7];	
	char	cbuf[2000];

	va_start( argptr, fmt );
		vsprintf( cbuf, fmt, argptr );
	va_end( argptr );	
                

}

문제는 이렇게 했을 경우 '...' 을 어떻게 선언해 줘야 하는지?
자꾸 저것때문에 에러가 나네요.
답변부탁드립니다.

전웅의 이미지

gkepsgds wrote:
K&R C 형태로 함수를 선언/정의 할려구 합니다.

왜 K&R C 형태로 함수를 선언/정의하려 하시는지 궁금하군요. C 언어의 첫
표준이 나온지도 15년이 다 되어가고 있습니다. 아무리 열악한 환경에서도
최소한 ANSI-C (C89) 는 지원합니다.

gkepsgds wrote:

void log_save(fmt, ...)
char *fmt;
{
	va_list	argptr;
	char	msg[2000], dd[9], hms[7];	
	char	cbuf[2000];

	va_start( argptr, fmt );
		vsprintf( cbuf, fmt, argptr );
	va_end( argptr );	
                

}

문제는 이렇게 했을 경우 '...' 을 어떻게 선언해 줘야 하는지?
자꾸 저것때문에 에러가 나네요.
답변부탁드립니다.

가변 인자를 받는 함수 선언을 위한 ... 는 표준에서 처음 도입된 것입니다.
따라서, K&R C 형태로 함수를 선언할 때는 ... 를 사용할 수 없습니다.

표준이 ... 를 도입한 이유는, 가변 인자를 받는 함수는 항상 ... 가 보이는
원형 선언 아래에서 호출되도록 요구함으로써 독특한 calling convention
을 사용하는 implementation 이 가변 인자 전달시 적절한 처리를 해줄 수 있
도록 배려하기 위해서 입니다 - 다시 말해, 가변 인자에 대한 걱정으로
레지스터등을 이용한 효과적인 calling convention 을 포기하지 않도록 해
주는 것입니다. ... 를 갖는 함수의 선언/정의가 반드시 원형이어야 하며,
호출 역시 원형으로 이루어져야 한다는 점을 생각하면, 함수 원형을 도입하
기 이전인 K&R C 로 ... 를 사용할 수는 없다는 사실은 당연한 것이 됩니다.
(IT 백두대간 C 언어, pp.522-523, pp.546-555, p.562)

따라서, K&R C style 로 가변 인자를 처리하는 일반적인 방법은, 표준에
의해 추가된 <stdarg.h> 가 아닌 그 이전부터 UNIX-like 환경의 이식성을
위해 존재하던 <varargs.h> 를 사용하는 것입니다 - <varargs.h> 는
<stdarg.h> 의 모델이기도 합니다.

    #include <varargs.h>

    log_save(va_alist)    // 전체를 모두 가변 인자로
    va_dcl
    {
        va_list ap;
        char *fmt;

        va_start(ap);
        fmt = va_arg(ap, char *);
        /* 나머지 가변 인자 처리 */

        va_end(ap);
    }

하지만, 사용하신 vsprintf() 는 K&R C style 이 아닌 표준의 가변 인자 방
식으로만 가능하기에, vsprintf() 가 fmt 까지 처리된 ap 를 받아 올바르게
작동한다면 위험을 감수하고 그대로 사용하거나, 직접 K&R C 의 가변 인자
방식에 대응하는 vsprintf() 를 구현해 사용하는 방법이 있을 수 있습니다.

보여주신 코드를 보니 여기저기서 표준 C 언어만의 특징이 보이는데 (예를
들면, void), 굳이 K&R C 를 고집하실 이유가 있을지 모르겠습니다.

그럼...

--
Jun, Woong (woong at gmail.com)
http://www.woong.org

익명 사용자의 이미지

4.4BSD에서 파생된 printf() 함수 본체입니다(저작권은 http://cvsweb.netbsd.org/bsdweb.cgi/src/lib/libc/stdio/printf.c?rev=1.1&content-type=text/x-cvsweb-markup 참조).

#include <stdio.h>
#if __STDC__
#include <stdarg.h>
#else
#include <varargs.h>
#endif

#if __STDC__
printf(char const *fmt, ...)
#else
printf(fmt, va_alist)
	char *fmt;
	va_dcl
#endif
{
	int ret;
	va_list ap;

#if __STDC__
	va_start(ap, fmt);
#else
	va_start(ap);
#endif
	ret = vfprintf(stdout, fmt, ap);
	va_end(ap);
	return (ret);
}

윗분 말씀과 달리 K&R 스타일에서도 char *fmt이 인수 명단에 들어있는 것을 볼 수 있습니다.
전웅의 이미지

방준영 wrote:
4.4BSD에서 파생된 printf() 함수 본체입니다(저작권은 http://cvsweb.netbsd.org/bsdweb.cgi/src/lib/libc/stdio/printf.c?rev=1.1&content-type=text/x-cvsweb-markup 참조).

윗분 말씀과 달리 K&R 스타일에서도 char *fmt이 인수 명단에 들어있는 것을 볼 수 있습니다.

표준에 의해 보장되는 <stdarg.h> 와는 달리, pre-standard 시절에 사용되던
<varargs.h> 에는 어차피 "full portability" 가 보장되지 않습니다. 님이
인용하신 코드가 가정한 implementaiton 에서는 이를 허락할지 모르지만,
제가 배운 K&R style 에서는 "그나마" 이식성을 위해 인자 모두를 가변 인
자로 처리하도록 추천하고 있습니다.

<To OP>
일부 환경에서 테스트 해본 결과, fmt 까지 처리한 ap 를 <stdio.h> 의
v*printf() 에 넘기는 것에 큰 문제가 없음을 확인하였습니다. 이식성은 보
장되지 않지만, 필요한 환경에서는 간단한 테스트 후에 사용하실 수 있으리
라 생각합니다
</To OP>

그럼...

--
Jun, Woong (woong at gmail.com)
http://www.woong.org

전웅의 이미지

참고로, The Single UNIX specification (version 2) 에서 인용합니다.

http://www.opengroup.org/onlinepubs/007908799/xsh/varargs.h.html

그럼...

--
Jun, Woong (woong at gmail.com)
http://www.woong.org

익명 사용자의 이미지

전웅 wrote:
참고로, The Single UNIX specification (version 2) 에서 인용합니다.

http://www.opengroup.org/onlinepubs/007908799/xsh/varargs.h.html

그럼...


그 문서를 비롯해서, 다른 어떤 문서에도 va_alist 앞에 여분의 인수가 더 있으면 안된다고 명시한 곳은 없습니다.

반대로 최초의 포터블 유닉스이자 데니스 리치와 그의 동료들이 직접 만든 유닉스 버전 7에서는 printf가 어떻게 구현되어 있는지 볼까요.

#include	<stdio.h>

printf(fmt, args)
char *fmt;
{
	_doprnt(fmt, &args, stdout);
	return(ferror(stdout)? EOF: 0);
}

이 코드와 아까 4.4BSD 버전간의 차이점은 printf의 두번째 인수로 된 args를 컴파일러가 인지할 수 있도록 매크로로 쳬계화한 정도뿐이지요.

K&R이 무엇이냐에 관한 논쟁은 이것으로 막을 내리길 바랍니다. 8)

전웅의 이미지

방준영 wrote:
그 문서를 비롯해서, 다른 어떤 문서에도 va_alist 앞에 여분의 인수가 더 있으면 안된다고 명시한 곳은 없습니다.

반대로 최초의 포터블 유닉스이자 데니스 리치와 그의 동료들이 직접 만든 유닉스 버전 7에서는 printf가 어떻게 구현되어 있는지 볼까요.

#include	<stdio.h>

printf(fmt, args)
char *fmt;
{
	_doprnt(fmt, &args, stdout);
	return(ferror(stdout)? EOF: 0);
}

이 코드와 아까 4.4BSD 버전간의 차이점은 printf의 두번째 인수로 된 args를 컴파일러가 인지할 수 있도록 매크로로 쳬계화한 정도뿐이지요.

K&R이 무엇이냐에 관한 논쟁은 이것으로 막을 내리길 바랍니다. 8)

va_alist 앞에 고정 인자가 있어서는 안 된다고 이야기한 적은 없습니다.
<varargs.h> 의 원 저작자의 의도는 매개변수 명칭 리스트 전체를 va_alist
로 대체하는 것임을 이야기하는 것입니다. 즉, 님께서 보여주신 예가
<varargs.h> 의 "잘못된" 사용임을 이야기하는 것이 아니라 가변 인자의
(그 당시로서는) 이식성 있는 지원을 위해 <varargs.h> 를 고안한 Andrew
Koenig 이 의도한 사용 방법이 그와는 다르다는 것을 이야기하는 것입니다
- 어차피 "표준" 이라는 것이 존재하지 않던 시대였습니다. 따라서,
"잘못된" 사용을 판단할 수 있는 기준이 결코 절대적이지 않았다는 사실에
유의하시기 바랍니다, 제 글 어디에서도 해당 방법을 "잘못된" 것이라 표현
한 부분은 없으며 단지 "이식성을 위해 ... 추천하고 있습니다" 라고 설명
드렸습니다.

이러한 관점에서, 반복해서 implementation detail 을 예로 보이고 계시지
만 개인적으로 바람직한 방법을 OP 께 보이고 계신 것이라 생각하기는 어렵
습니다 - 특히나 "이식성" 을 이야기하는 과정이라면 implementation magic
을 사용하는 implementation detail 을 예로 보이는 것은 바람직하지 않습
니다. 귀차니즘이 발동하지만, varargs 매크로에 대한 Andrew Koenig 의 글
을 인용합니다.

Quote:
Most C implementations achieve this through a set of macro definitions
collectively called varargs. The exact nature of these macros will
vary from one implementation to another, but a program that uses them
carefully will be able to use variable argument lists on a wide
variety of machines.

분명 Andrew 자신도 varargs 매크로가 full portability 를 갖는다고 이야
기하지는 않으며, 오히려 각 implementation 마다 다른 특성을 갖고 있다고
강조하고 있습니다. 그럼에도 "주의해서" 사용할 경우 많은 환경에서 원하
는 행동을 얻을 수 있다고 밝히고 있습니다. 그리고 이어서 다음과 같이
그 구체적인 방법을 설명합니다.

Quote:
A function that is called with a variable argument list must use the
va_alist and va_dcl macros to form the beginning of its definition, as
follows:

    #include <varargs.h>

    void error (va_alist) va_dcl

이제 어떤 것이 보다 더 바람직한 방법인지는 분명합니다.

그럼...

--
Jun, Woong (woong at gmail.com)
http://www.woong.org

익명 사용자의 이미지

근데 왜 K&R 을 추구하시는지요?

전웅의 이미지

minzkn wrote:
근데 왜 K&R 을 추구하시는지요?

저도 그게 궁금합니다. 표준 C 는 매우 제한적인 환경을 위해 freestanding
implementation 의 개념을 별도로 정의해 주고 있으며, 구체적인 구현 면에
서도 pre-standard C 보다 지나치게 많은 물리적 자원을 요구하는 것도 아
닙니다. 따라서, 오래 전에 사장되어 해당 환경에 맞는 표준 C
implementation 을 구하기 어려운 상황이 아닌 이상, 모든 환경에서 표준 C
혹은 준 표준 C 언어를 지원하는 implementation 을 사용할 수 있습니다.
C 언어를 너무 오래된 책으로 공부해 K&R C 가 익숙하다 말씀하시는 분을
본 적은 있지만, 멀쩡히 잘 작성되어 있는 프로그램을 굳이 K&R C 로 옮겨
야 하는 이유는 저 역시 궁금합니다.

--
Jun, Woong (woong at gmail.com)
http://www.woong.org

익명 사용자의 이미지

전웅 wrote:
va_alist 앞에 고정 인자가 있어서는 안 된다고 이야기한 적은 없습니다.
<varargs.h> 의 원 저작자의 의도는 매개변수 명칭 리스트 전체를 va_alist
로 대체하는 것임을 이야기하는 것입니다. 즉, 님께서 보여주신 예가
<varargs.h> 의 "잘못된" 사용임을 이야기하는 것이 아니라 가변 인자의
(그 당시로서는) 이식성 있는 지원을 위해 <varargs.h> 를 고안한 Andrew
Koenig 이 의도한 사용 방법이 그와는 다르다는 것을 이야기하는 것입니다
- 어차피 "표준" 이라는 것이 존재하지 않던 시대였습니다. 따라서,
"잘못된" 사용을 판단할 수 있는 기준이 결코 절대적이지 않았다는 사실에
유의하시기 바랍니다, 제 글 어디에서도 해당 방법을 "잘못된" 것이라 표현
한 부분은 없으며 단지 "이식성을 위해 ... 추천하고 있습니다" 라고 설명
드렸습니다.

원래 질문하신 분은 "K&R" 스타일로 가변 인수를 어떻게 구현하느냐고 질문하셨습니다. Andrew Koenig은 K&R이 아닙니다. 아래를 보시죠:

전웅 wrote:
표준에 의해 보장되는 <stdarg.h> 와는 달리, pre-standard 시절에 사용되던
<varargs.h> 에는 어차피 "full portability" 가 보장되지 않습니다. 님이
인용하신 코드가 가정한 implementaiton 에서는 이를 허락할지 모르지만,
제가 배운 K&R style 에서는 "그나마" 이식성을 위해 인자 모두를 가변 인
자로 처리하도록 추천하고 있습니다.

제가 지적하고자 한 것은 K&R 스타일 어디에서도 인수 모두를 가변 인수로 처리하도록 추천하고 있는 부분이 없다는 점입니다. 심지어 그 'R' 조차도 그렇게 쓰지 않았습니다. K&R이 직접 쓴 코드와 K&R이 아닌 사람이 쓴 코드중에 어느 것이 더 K&R에 가까운가라고 생각해 보면 간단한 답 아닌가요.

Quote:
인용:
Most C implementations achieve this through a set of macro definitions
collectively called varargs. The exact nature of these macros will
vary from one implementation to another, but a program that uses them
carefully will be able to use variable argument lists on a wide
variety of machines.

분명 Andrew 자신도 varargs 매크로가 full portability 를 갖는다고 이야
기하지는 않으며, 오히려 각 implementation 마다 다른 특성을 갖고 있다고
강조하고 있습니다. 그럼에도 "주의해서" 사용할 경우 많은 환경에서 원하
는 행동을 얻을 수 있다고 밝히고 있습니다. 그리고 이어서 다음과 같이
그 구체적인 방법을 설명합니다.


원문을 잘못 해석하셨습니다. :( 해석하면,

"대부분의 C 구현들은 이 문제를 varargs라 부르는 일련의 매크로를 통해 해결하고 있다. 이들 매크로의 정확한 내부 구조는 구현된 것마다 각기 다르지만, varargs를 제대로 쓰는 프로그램은 다양한 기종에서 가변 인수 리스트를 사용 할 수 있게 된다."

이식성이 없다고 한 게 아니라 거꾸로 보장된다는 뜻입니다.

전웅의 이미지

방준영 wrote:
원래 질문하신 분은 "K&R" 스타일로 가변 인수를 어떻게 구현하느냐고 질문하셨습니다. Andrew Koenig은 K&R이 아닙니다.

K&R C Style 에 대해 오해가 있는 것 같습니다. 엄격히 구분해서
"K&R Style" 과 "K&R C" 는 다른 개념입니다 - 혼동의 여지가 있기는 하군
요. "K&R Style" 은 "kernel style" 의 다른 이름으로 프로그램의 가독성에
영향을 주는 순수한 프로그래밍 스타일에 대한 문제를 일컫는 것이며, 이는
분명 The C Programming Language (1st ed.) 에 사용된, 그리고 UNIX
kernel 작성에 사용된 coding style 을 지칭합니다.

http://jargon.watson-net.com/jargon.asp?w=indent+style

반면, "K&R C" 는 표준화 이전에 사용되었던 C 언어를 지칭하는데 사용됩니
다. <varargs.h> 는 pre-standard C (즉, K&R C) 언어로 가변 인자를 다루
기 위해 사용했던 방법으로, 표준이 <varargs.h> 를 모델로 한 다소 수정된
<stdarg.h> 를 도입하면서 대체된 것입니다. 따라서, 표준 C 로 작성된 프
로그램을 K&R C 로 옮기는 과정에는 반드시 다음과 같은 이야기가 들어갑니
다 - C FAQs 에서 인용합니다.

Quote:
9. Convert from the facilities of <stdarg.h> to <varargs.h>

방준영 wrote:
전웅 wrote:
표준에 의해 보장되는 <stdarg.h> 와는 달리, pre-standard 시절에 사용되던
<varargs.h> 에는 어차피 "full portability" 가 보장되지 않습니다. 님이
인용하신 코드가 가정한 implementaiton 에서는 이를 허락할지 모르지만,
제가 배운 K&R style 에서는 "그나마" 이식성을 위해 인자 모두를 가변 인
자로 처리하도록 추천하고 있습니다.

제가 지적하고자 한 것은 K&R 스타일 어디에서도 인수 모두를 가변 인수로 처리하도록 추천하고 있는 부분이 없다는 점입니다. 심지어 그 'R' 조차도 그렇게 쓰지 않았습니다. K&R이 직접 쓴 코드와 K&R이 아닌 사람이 쓴 코드중에 어느 것이 더 K&R에 가까운가라고 생각해 보면 간단한 답 아닌가요.

제가 "K&R Style" 이라는 표현을 잘못 사용해서 발생한 오해인가요? 저는
문맥이 분명하기에 "표준화 이전의 C 언어" 를 가리키는 개념과 "가독성과
관련된 coding style" 을 가리키는 개념이 혼동될 수 있으리라 생각하지는
못했습니다 - 정확히는, "K&R C" 나 "K&R C Style" 로 적었어야 했습니다.

말씀드린대로, "K&R C" 는 Kernighan 과 Ritchie 가 작성한 코드 만을 놓고
이야기하는 것이 아니라, 표준화 이전의 C 언어로 프로그램을 작성하는 것
에 대해 말하는 것이며, 여기에는 위에서 보여드린대로 Andrew Koenig 이
고안한 <varargs.h> 가 포함됩니다 - 못 믿으시겠다면, comp.std.c 나
comp.lang.c 같은 뉴스 그룹에서 "K&R C" 를 검색해 보시기 바랍니다.

Quote:
인용:
Most C implementations achieve this through a set of macro definitions
collectively called varargs. The exact nature of these macros will
vary from one implementation to another, but a program that uses them
carefully will be able to use variable argument lists on a wide
variety of machines.

방준영 wrote:
원문을 잘못 해석하셨습니다. :( 해석하면,

"대부분의 C 구현들은 이 문제를 varargs라 부르는 일련의 매크로를 통해 해결하고 있다. 이들 매크로의 정확한 내부 구조는 구현된 것마다 각기 다르지만, varargs를 제대로 쓰는 프로그램은 다양한 기종에서 가변 인수 리스트를 사용 할 수 있게 된다."

이식성이 없다고 한 게 아니라 거꾸로 보장된다는 뜻입니다.

제 해석과 무엇이 다른지요? (제 해석을 다시 인용합니다)

Quote:
분명 Andrew 자신도 varargs 매크로가 full portability 를 갖는다고 이야
기하지는 않으며, 오히려 각 implementation 마다 다른 특성을 갖고 있다고
강조하고 있습니다. 그럼에도 "주의해서" 사용할 경우 많은 환경에서 원하
는 행동을 얻을 수 있다고 밝히고 있습니다.

님의 해석: "이들 매크로의 내부 구조는 구현된 것마다 각기 다르지만"

제 해석: "Andrew 자신도 varargs 매크로가 full portability 를 갖는다고
이야기하지는 않으며, 오히려 각 implementation 마다 다른 특성을 갖고
있다고 강조하고 있습니다."

님의 해석: "varargs 를 제대로 쓰는 프로그램은 다양한 기종에서 가변 인
수 리스트를 사용할 수 있게 된다."

제 해석: "주의해서 사용할 경우 많은 환경에서 원하는 행동을 얻을 수 있
다고 밝히고 있습니다."

결론은, Andrew Koenig 은 서로 다른 implementation 이 서로 다른 방법을
사용해 지원하는 가변 인자를 이식성 있는 방법으로 대체하기 위한 노력으
로 <vararg.h> 를 고안했습니다 - 그럼에도 각 implementation 마다 그 구
현이나 사용 방법에 있어서 차이가 있었습니다. 그리고 그가 직접 고안한
방법의 가장 올바른 (이식성 있는) 사용 방법으로 제가 그 다음에 인용한

    #include <varargs.h> 

    void error (va_alist) va_dcl

형태를 이야기하고 있습니다. 제가 이야기했던 내용을 님께서 반복하신 것
에 불과합니다.

그리고, "The exact nature of these macros" 에 대해서 부연 설명하자면,
해당 매크로의 implementation detail 이 서로 다르다는 의미는 당연한 것
이며 (<varargs.h> 의 출현 배경을 생각해 보시기 바랍니다), 그 매크로를
사용하는 방법 (허락된 방법) 에도 차이가 있을 수 있음을 이야기하는 것입
니다 - 님이 앞서 보여주신 예도 그 경우 중 하나에 해당합니다. 예를 들어
이미 가변 인자 지원을 위해 다음과 같은 방법을 지원하고 있던
implementation 이 있었다면,

func(__vararg__)
{
    char *fmt = __pop_arg__(__func_id__, char *);
    /* ... */

아래와 같은 형태로 <varargs.h> 의 내용을 정의할 수 있으며,

#define va_alist       __vararg__
#define va_arg(ap, t)  (__pop_arg__((ap), t))
#define va_start(ap)   ((ap) = __func_id__)
typedef int va_list;
/* ... */

그와 같은 implementation 에서 다음과 같이 고정 인자를 도입하는 것은

func(fmt, __vararg__)
/* ... */

잘못된 것으로 취급될 수도 있습니다.

그럼...

--
Jun, Woong (woong at gmail.com)
http://www.woong.org

익명 사용자의 이미지

긴 논쟁 필요없이, printf(va_alist)는 허용하는데 printf(fmt, va_alist)는 허용하지 않는 컴파일러를 한가지라도 제시하신다면 님의 말씀이 맞다고 인정하겠습니다.

buelgsk8er의 이미지

에.. 끼어들어서 죄송합니다만.. 전웅님께서 그걸 증명하실 이유는 없는 것 같습니다. 방준영님의 말씀대로 설사 표준이 제정되기 전의 모든 C 컴파일러들이 printf(fmt, va_alist)를 지원하고 있다고 해도 그건 중요하지 않습니다. 그걸 지원해야 한다는 표준이 없는 이상 그건 우연일 뿐이니까요. 전웅님의 말씀이 맞다면 존재하는 건 그나마 좀 더 나은 포터빌리티를 추구하기 위해 도입된 vararg.h 있을 뿐인데 거기서는 printf(va_alist)의 형태를 추천한다고 되어있고 전웅님은 그걸 전한 것 뿐이니, 모든 컴파일러에서 printf(fmt, va_list)를 지원한다면 그냥 행운인거지 추천 자체가 잘못된 건 아닌 것 같습니다.

다만 이것은 애초에 질문하신 분이 말씀하신 "K&R C"라는 것이 일반적으로 말하는 표준제정전의 C"들"을 의미할 때 그렇다는 것이고, 만약 말그대로 데니스 리치가 만든 "바로 그 C"를 의미하신 거라면 방준영님의 항의가 옳겠습니다만.. 글쎄요 순전히 저의 추측입니다만 질문하신 분께서는 딱히 구별하고 쓰신 것 같지는 않은데요 :)

전웅의 이미지

방준영 wrote:
긴 논쟁 필요없이, printf(va_alist)는 허용하는데 printf(fmt, va_alist)는 허용하지 않는 컴파일러를 한가지라도 제시하신다면 님의 말씀이 맞다고 인정하겠습니다.

뉴스그룹에서도 그랬듯이, 어디에나 이처럼 위험한 태도를 가진 분이 계시
는군요.

int i, *p = &i;
printf("%p", p);

int a, b;
a = b = a = b = 0;

이 코드가 의도한대로 동작하는 않는 implementation 을 알고 계십니까?
없다면 위의 코드는 올바른 것인가요?

정말 중요한 문제는 varargs.h 의 원 자작자가 제가 말씀드린 방법만을 이
식성있는 방법으로 명시했기 때문에, 언제 어떠한 implementation 이라도
님이 보여주신 코드가 제대로 동작하지 않는 방법으로 varargs 매크로를 구
현할 수 있다는 것입니다. 그 외의 변형된 방법이 허락되는 것은 모두 의도
되지 않은 것이거나, 실수에서 시작되어 해당 implementation 의 backward
compatibility 를 위해서 지원되고 있는 것입니다 - 조사 결과, 그 실수의
근원은 AT&T 과 BSD 쪽이라는 의견이 유력합니다.

Google groups 에서 검색해 보시기 바랍니다. UNIX, C 와 역사를 함께한
Douglas A. Gwyn 이나 BSD 쪽에서 다양한 현장 경험을 가지고 있는 Chris
Torek 역시 님이 언급한 방법을 피하라고 지적하는군요.

http://groups.google.com/groups?selm=7163%40brl-smoke.ARPA
http://groups.google.com/groups?selm=23061%40mimsy.umd.edu
http://groups.google.com/groups?selm=1991Jun18.145823.2512%40cbnewsk.att.com

또한, 오래된 이야기로 들어가야 하지만 (사실, varargs 매크로 자체가 오
래된 이야기지요), 찾아본 결과 System V.2 역시 va_alist 만 있어야 한다
고 (추가적인 매개변수 명칭을 금지한다고) 밝히고 있으며, 과거 lint 의
경우 va_alist 외에 추가적인 매개변수 명칭이 있는 것을 잘못된 것으로 처
리했습니다. 또한, 레지스터를 통해 인자를 전달하는 MIPS 를 위해 Chris
Torek 이 구현한 varargs 매크로는 다음과 같습니다:

`va_alist' and `va_dcl' are often (always?) macros.  It is conceivable
that on a machine that passes the first two arguments in registers and
the third through nth on the stack, these might be

 #define va_alist  _p1, _p2, _p3
 #define va_dcl int _p1, _p2, _p3;

in which case va_start might be

 #define va_start(l) { \
  struct _va_info _va; \
  _va.reg = 2; _va.r[1] = _p1; _va._r[0] = _p2; \
  _va.addr = &_p3; \
  (l) = &_va;

and va_arg then

 #define va_arg(l, t) \
  (sizeof(t) == 8 ? \ /* size always 8 or 4 in this arch. */
   (ugly code to handle double float with spanning &c) : \
   (--(l)->reg >= 0 ? *(t *)&(l)->r[(l)->reg] : \
    ((l)->addr++, ((t *)((l)->addr))[-1])))

(and va_end would simply be defined as `}').

This would indeed preclude usage like

 f(fmt, va_alist)
  char *fmt;
  va_dcl
 {
  ...

분명, 님이 언급하신 방법 역시 "stack 만을 통해" 인자를 전달하는 대부분
의 implementation 에서 (위에서 말씀드린 이유 - 우연 혹은 호환성 - 로
인해) 문제 없이 작동합니다. 하지만, 그러한 사실이 과거 C community 에
의해 약속되어 de facto standard 로 자리잡은 것은 아니라는 사실이 제가
반복하여 강조하고 싶은 것입니다.

그럼...

--
Jun, Woong (woong at gmail.com)
http://www.woong.org

익명 사용자의 이미지

쩝, Chris Torek은 아까 전 4.4BSD printf 작성한 바로 그 사람입니다. 아군이라고 착각하시면 안됩니다. :(

끝에 예로 드신 Chris Torek의 MIPS 얘기는 밑부분을 싹둑 잘라 버림으로써 원래 얘기와 전혀 딴판으로 만들어 버리셨습니다. 뒷부분을 추가하면,

Quote:
The reason I mention this is that in fact, on the Pyramid, Sun,
and Vax (all implementations of which I am aware), usages such as
that above in fact work. I wonder, then, if this has been guaranteed
anywhere. Has it? (I know, I should dig out a copy of the X3J11
draft....)

>(C compilers can then recognize this directly to do any special
>processing needed, as for the Pyramid.)

Actually, Pyramid manages it without compiler hooks. I believe
MIPS uses compiler hooks, though.


Torek이 이것을 언급한 이유는 신기하게도 레지스터 기반 시스템에서조차 f(fmt, va_alist)이 제대로 동작했기 때문입니다. 그렇다면, 이와 같은 일이 어디선가 보장되었던 게 아닐까, 그게 궁금해서 X3J11 초안을 읽어봐야겠다고 한 겁니다. 그리고 MIPS 컴파일러는 훅을 써서 f(fmt, va_alist)을 처리하는 것 같다고 생각했죠.

이번에는 똑같은 MIPS 시스템에서 f(va_alist)가 제대로 동작하지 않은 예를 들어볼까요?
http://groups.google.com/groups?hl=ko&lr=&ie=UTF-8&oe=UTF-8&th=272676f11b596247&seekm=39254%40mips.mips.COM&frame=off
첫번째 인수가 double 타입이면 f(va_alist)을 쓰더라도 잘못된 코드가 생성되는 예입니다.

따라서 이 문제는 단순히 컴파일러 버그에 불과한 것임을 알 수 있습니다. f(va_alist)을 제대로 지원하는 컴파일러가 f(fmt, va_alist)를 지원하지 않거나 못할 이유가 전혀 없습니다.

그리고 중간에 예로 드신 뉴스그룹의 어떤 글에서도 f(fmt, va_alist)가 f(va_alist)에 비해 잘못된 코드를 더 많이 생성한다거나, 이식성이 더 떨어진다거나 하는 증거가 없습니다. 오히려 Chris Torek이 왜 f(fmt, va_alist)가 더 좋은지 설명까지 자세히 해놨는데요? 그 사람이 쓴 글들중 일부를 잠깐 보기로 하죠:

Quote:
Note that, to be Officially Correct, the `fmt = va_arg(ap, char *)' line
is necessary---you are Not Allowed to write

int foo(fmt, va_alist) char *fmt; va_dcl { <...code...> }

(Although this happens to work on every machine I have used, and it makes
a better analogy to the new stdarg code, and I like it better myself and
wish that it *were* Offically Correct, it is not; avoid this temptation.
Write the ugly extra assigments. Someday you will be glad you did.)


번역 wrote:
(비록 이 방법이 내가 지금까지 사용해본 모든 기계에서 우연히 동작했고, 새 stdarg 코드와 유사점이 더 많으며, 개인적으로는 이걸 더 좋아하고 이게 "공식적으로 옳은" 것이었으면 바라지만, 실제로는 옳지 않다고 하는군요. 그러므로 이 방법을 쓰고 싶은 유혹은 떨쳐 버리세요. 대신 보기 흉한 대입문들("fmt = va_arg(ap, char *);"같은 문장들을 지칭)을 쓰세요. 그럼 언젠가 여러분이 한 일을 기뻐하게 될 겁니다)

이 얘기는 그냥 "공식적으로 옳으니까" f(va_alist)이 옳다고 주장하는 사람들을 비꼬기 위해 쓴 겁니다. 다른 글에 보면 f(fmt, va_alist)처럼 쓰는 게 "far crearer"하다고도 했습니다. 이제 Torek이 누구편인지 아시겠습니까.

아무튼,

방준영 wrote:
그 문서를 비롯해서, 다른 어떤 문서에도 va_alist 앞에 여분의 인수가 더 있으면 안된다고 명시한 곳은 없습니다.

이 말은 분명히 틀렸음을 인정합니다. 그리고 f(va_alist)를 써야 했던 이유가 "그냥 그렇게 써있기 때문"이라는 점도 이번에 새롭게 알았음을 수확으로 생각합니다. 8)
익명 사용자의 이미지

음...

가변인자 처리에 대해서 방식이 2( 사실은 3가지) 가지가 있는걸로 알고 있습니다.

일단 어셈블리를 빌어서 0(%esp) 를 기준으로 첫번째 인자를 취하는

방법이 있으며

C는 어디까지나 C이므로 완전히 C만을 가지고 가변인자를 취하기 위한

첫번째 인자의 주소를 통한 가변인자를 취하는 방식이 있습니다.

즉, 원래 C만으로 가변인자를 처리하기위해서는 (매크로만으로...)

반드시 첫번째 인자가 주어져야 하는 방법이 되겠습니다.

즉 가변인자 첫번째를 취하기 위해서 매크로는 다음과 같은 예에서

다음과 같이 확장이 될겁니다.

void VArgumentTest( int s_FirstArgument, ...)
{
va_list(........

첫번째가변인자 = va_arg(....)

......va_end(......
}

void VArgumentTest( int s_FirstArgument, ...)
{

첫번째가변인자 = (cast type) (
(cast type *) (
(char *)(&s_FirstArgument)
+ (sizeof( int ) * (몇번째 인자 = 여기서는 1)
)
);

}

꼭 이렇게 확장된다기보다 처리가 위와 같은 절차로 C만을 이용한 매크로
확장이 가능하다는 점입니다.

하지만 첫번째 인자가 없다면
어셈블리를 통한 0(%esp)를 취해야만 처음 인자를 취할수 있을겁니다.

즉, 준영님이 말씀하신 첫번째 인자가 주어질수 있다는 것이
보다 C언어적으로 가까운 방법인듯 합니다.

물론 반드시 0(%esp)를 필요로 하는 방법만 있는것은 아닙니다.

함수내에서 변수를 처음(!)에 선언하고 그것의 주소로부터

뒤로 8바이트 뒤를 추적하면 그곳부터 0(%esp)가 될수도 있습니다.

하지만 이것은 스택프레임이 만들어진다면 12바이트 뒤가 된다는 점에서

좀 매크로 확장이 복잡할듯 합니다.

결론은 첫번째 인자가 반드시 없어야 정석이 되는것은 아닌듯 생각되며

또한 첫번째 인자가 반드시 있어야 하지도 않지만

있으면 좋은 그런 관계인거 같습니다.

즉, 준영님의 말씀하신대로 "첫번째 인자가 없어야만 하지는 않는다"라는 예기를

하고 싶습니다. 오히려 있는것이 가변인자처리가 수월할듯 합니다.

--------------------

여기까지가 제 생각의 결과를 마구잡이로 적어본건데

다시 읽어보니 머리가 아프군요. 정확하게 생각을 잘 전달한건지 모르겠네요.

제가 쓴글 재미없죠?

mach의 이미지

minzkn wrote:
음...

즉, 준영님의 말씀하신대로 "첫번째 인자가 없어야만 하지는 않는다"라는 예기를

........<중략>.........

제가 쓴글 재미없죠?

딴지거는거 아니고요, minzkn님 좋은 글 속에 티가 보여서, 옥의 티랄까?

예기 --> 얘기

가 맞습니다.

그리고, minzkn님이 쓰신 글은 재미있어서 전부 읽어보고 있습니다요. 애독자랄까요?

------------------ P.S. --------------
지식은 오픈해서 검증받아야 산지식이된다고 동네 아저씨가 그러더라.

lsj0713의 이미지

중간에 끼어들게 되어 죄송합니다.

방준영 wrote:

이번에는 똑같은 MIPS 시스템에서 f(va_alist)가 제대로 동작하지 않은 예를 들어볼까요?
http://groups.google.com/groups?hl=ko&lr=&ie=UTF-8&oe=UTF-8&th=272676f11b596247&seekm=39254%40mips.mips.COM&frame=off
첫번째 인수가 double 타입이면 f(va_alist)을 쓰더라도 잘못된 코드가 생성되는 예입니다.

따라서 이 문제는 단순히 컴파일러 버그에 불과한 것임을 알 수 있습니다. f(va_alist)을 제대로 지원하는 컴파일러가 f(fmt, va_alist)를 지원하지 않거나 못할 이유가 전혀 없습니다.

해당 쓰레드를 끝까지 읽어보십시오.

"MIPS version of 'varargs' does not work when the first
argument is a double. Since the MIPS compiler is migrating to ANSI,
stdarg should be used which fixes the problem. 'stdarg.h' use is
fully supported by the 2.10 compiler. The caller needs the
prototype to be visible (a requirement of ANSI for 'stdarg' calls)"

MIPS 컴파일러에선 varargs가 문제가 있음을 말하고 있으며, 새로운(글이 쓰여진 때가 1990이므로) ANSI C의 표준 방식인 stdarg.h를 사용하기를 권하고 있습니다. 제가 보기엔 MIPS컴파일러가 표준의 stdarg 방식을 제대로 지원하고 있기에, 굳이 기존의 코드와의 호환성을 고려할 필요가 없다고 생각하여 vararg.h 방식을 제대로 지원하지 않는 것으로 보입니다.

또한, 위의 글만 가지고서는 f(fmt, va_alist) 와 같은 코드가 제대로 동작하는지에 대해서도 알 수가 없습니다. f(va_alist) 가 문제가 있다면 마찬가지로 f(fmt, va_alist)도 똑같은 문제를 일으킬 가능성도 배제할 수 없습니다.

위의 사례가 f(va_alist)를 써야 된다는 주장을 약화시키거나 f(fmt, va_alist)와 같은 코드를 정당화하고 있다고 보기에는 어려울 것 같습니다.

방준영 wrote:

그리고 중간에 예로 드신 뉴스그룹의 어떤 글에서도 f(fmt, va_alist)가 f(va_alist)에 비해 잘못된 코드를 더 많이 생성한다거나, 이식성이 더 떨어진다거나 하는 증거가 없습니다.

오히려 Chris Torek이 왜 f(fmt, va_alist)가 더 좋은지 설명까지 자세히 해놨는데요? 그 사람이 쓴 글들중 일부를 잠깐 보기로 하죠:

Quote:
Note that, to be Officially Correct, the `fmt = va_arg(ap, char *)' line
is necessary---you are Not Allowed to write

int foo(fmt, va_alist) char *fmt; va_dcl { <...code...> }

(Although this happens to work on every machine I have used, and it makes
a better analogy to the new stdarg code, and I like it better myself and
wish that it *were* Offically Correct, it is not; avoid this temptation.
Write the ugly extra assigments. Someday you will be glad you did.)

번역 wrote:
(비록 이 방법이 내가 지금까지 사용해본 모든 기계에서 우연히 동작했고, 새 stdarg 코드와 유사점이 더 많으며, 개인적으로는 이걸 더 좋아하고 이게 "공식적으로 옳은" 것이었으면 바라지만, 실제로는 옳지 않다고 하는군요. 그러므로 이 방법을 쓰고 싶은 유혹은 떨쳐 버리세요. 대신 보기 흉한 대입문들("fmt = va_arg(ap, char *);"같은 문장들을 지칭)을 쓰세요. 그럼 언젠가 여러분이 한 일을 기뻐하게 될 겁니다)

이 얘기는 그냥 "공식적으로 옳으니까" f(va_alist)이 옳다고 주장하는 사람들을 비꼬기 위해 쓴 겁니다. 다른 글에 보면 f(fmt, va_alist)처럼 쓰는 게 "far crearer"하다고도 했습니다. 이제 Torek이 누구편인지 아시겠습니까.

번역하신 부분은 " int foo(fmt, va_alist) char *fmt; va_dcl { <...code...> } " 와 같은 코드가 그와 같은 장점이 있음에도, 옳지 않은 코드이므로 쓰지 말라고 만류하는 부분입니다. (쓰고 싶은 유혹을 떨쳐버리라고 하고 있지요) 절대 비꼬거나 위와 같은 코드를 옹호하는 말이 아닙니다. 번역하신 부분을 다시 한번 읽어보십시오.

제가 보기엔 방준영님께서 지나치게 흥분하고 계신 것이 아닌가 하는 생각이 듭니다. 머리를 차게 식히시고 다시한번 관련글들을 읽어보시는 것이 좋지 않을까 싶습니다. 흥분한 상태에서 쓴 글은 많은 허점이 드러나게 마련입니다.

전웅의 이미지

방준영 wrote:
쩝, Chris Torek은 아까 전 4.4BSD printf 작성한 바로 그 사람입니다. 아군이라고 착각하시면 안됩니다. :(

착각한 것이 아니라 Chris 가 어떤 사람인지를 알기에 그의 의견을 일부러
인용한 것입니다. 즉, 님이 언급하신 방법을 이식성을 위해 쓰지 말라고 이
야기하는 사람이 바로 Chris 라는 사실에 유의하셔야 합니다. 특정 프로그
램이 자신의 분명한 제어권을 갖는 특정 환경으로만 제한된다면, 이식성 없
는 방법에 의존하는 것을 두고 "잘못된" 것이라 지적할 수 있는 사람은 없
습니다. 하지만, 더 긴 프로그램의 수명과 더 많은 환경을 생각하는 경우에
는 이야기가 달라집니다.

그리고, "내편/니편", "아군/적군" 이라는 표현을 기술적인 논의에 사용하
는 모습은 결코 좋아보이지 않는군요.

방준영 wrote:
끝에 예로 드신 Chris Torek의 MIPS 얘기는 밑부분을 싹둑 잘라 버림으로써 원래 얘기와 전혀 딴판으로 만들어 버리셨습니다. 뒷부분을 추가하면,

Quote:
The reason I mention this is that in fact, on the Pyramid, Sun,
and Vax (all implementations of which I am aware), usages such as
that above in fact work. I wonder, then, if this has been guaranteed
anywhere. Has it? (I know, I should dig out a copy of the X3J11
draft....)

Torek이 이것을 언급한 이유는 신기하게도 레지스터 기반 시스템에서조차 f(fmt, va_alist)이 제대로 동작했기 때문입니다. 그렇다면, 이와 같은 일이 어디선가 보장되었던 게 아닐까, 그게 궁금해서 X3J11 초안을 읽어봐야겠다고 한 겁니다. 그리고 MIPS 컴파일러는 훅을 써서 f(fmt, va_alist)을 처리하는 것 같다고 생각했죠.

글의 시점을 잘 보시기 바랍니다. 해당 글을 작성할 때 Chris 는 경험상으
로는 문제를 겪지 못했는데, 그렇다면 va_alist 앞에 추가적인 고정 인자를
두는 것이 다른 권위있는 기준 (예를 들면, X3J11 - 이는 C89 를 준비하던
ANSI 위원회를 말합니다, POSIX 등) 에 의해 보장되는가를 궁금해 하는 것
입니다. 즉, 실제 Chris 가 사용했던 implementation 에서는 큰 문제를 일
으키지 않았기에 자신이 사용했던 방법이 보장받는 (혹은 앞으로 보장 받
을) 올바른 것인지 묻고 있는 것입니다. 해당 thread 를 계속 보시면, 다른
사람이 Chris 가 이야기한 변형된 사용 방법을 보장하는 권위있는 문서는
그 어디에서도 찾아볼 수 없으며, 오히려 다수의 문서에서 이를 금지한다고
답변하는 부분이 나옵니다 - 제가 모두 인용해 드려야만 하는지요? :(

그 이후 Chris 역시 다른 사람이 va_alist 앞에 고정 인자를 두는 방법을
사용하자 답변에서 "공식적으로" 올바른 방법을 사용하라고 이야기하기 시
작합니다. 즉, 위의 글을 쓰는 시점에서 Chris 는 무엇이 진정 바람직한 방
법인지를 아직 모르는 상태이며, 이를 확인하기 위해 묻고 있는 것입니다.

방준영 wrote:
이번에는 똑같은 MIPS 시스템에서 f(va_alist)가 제대로 동작하지 않은 예를 들어볼까요?
http://groups.google.com/groups?hl=ko&lr=&ie=UTF-8&oe=UTF-8&th=272676f11b596247&seekm=39254%40mips.mips.COM&frame=off
첫번째 인수가 double 타입이면 f(va_alist)을 쓰더라도 잘못된 코드가 생성되는 예입니다.

따라서 이 문제는 단순히 컴파일러 버그에 불과한 것임을 알 수 있습니다. f(va_alist)을 제대로 지원하는 컴파일러가 f(fmt, va_alist)를 지원하지 않거나 못할 이유가 전혀 없습니다.

인용하신 내용이 님의 주장을 뒷받침하는 근거라고 보기 어렵습니다. 오히
려 va_alist 의 특성이 implementation-dependent 하다는 Andrew Koenig 의
주장만을 뒷받침하는군요 - "주의해서 사용하면 다수의 implementation 에
서 원하는 바를 얻을 수 있다."

방준영 wrote:
그리고 중간에 예로 드신 뉴스그룹의 어떤 글에서도 f(fmt, va_alist)가 f(va_alist)에 비해 잘못된 코드를 더 많이 생성한다거나, 이식성이 더 떨어진다거나 하는 증거가 없습니다.

일부만 인용해 드린 것입니다. 직접 검색해서 읽어보시기 바랍니다. 앞에서
언급된 Chris 는 물론이고, 현재도 혹은 과거에 권위있던 다수의 개발자가
va_alist 를 "단독으로" 매개변수 명칭 리스트에 사용하라고 이야기하고 있
습니다. 저는 그와 같은 방법을 단지 "추천" 했지만, Douglas 같은 사람은
변형된 방법에 대해 아예 "금지" 라고 표현하며, Chris 역시 "잘못된" 이라
는 보다 강한 표현을 사용하는 것을 확인할 수 있습니다.

방준영 wrote:
오히려 Chris Torek이 왜 f(fmt, va_alist)가 더 좋은지 설명까지 자세히 해놨는데요? 그 사람이 쓴 글들중 일부를 잠깐 보기로 하죠:
Quote:
Note that, to be Officially Correct, the `fmt = va_arg(ap, char *)' line
is necessary---you are Not Allowed to write

int foo(fmt, va_alist) char *fmt; va_dcl { <...code...> }

(Although this happens to work on every machine I have used, and it makes
a better analogy to the new stdarg code, and I like it better myself and
wish that it *were* Offically Correct, it is not; avoid this temptation.
Write the ugly extra assigments. Someday you will be glad you did.)


번역 wrote:
(비록 이 방법이 내가 지금까지 사용해본 모든 기계에서 우연히 동작했고, 새 stdarg 코드와 유사점이 더 많으며, 개인적으로는 이걸 더 좋아하고 이게 "공식적으로 옳은" 것이었으면 바라지만, 실제로는 옳지 않다고 하는군요. 그러므로 이 방법을 쓰고 싶은 유혹은 떨쳐 버리세요. 대신 보기 흉한 대입문들("fmt = va_arg(ap, char *);"같은 문장들을 지칭)을 쓰세요. 그럼 언젠가 여러분이 한 일을 기뻐하게 될 겁니다)

이 얘기는 그냥 "공식적으로 옳으니까" f(va_alist)이 옳다고 주장하는 사람들을 비꼬기 위해 쓴 겁니다. 다른 글에 보면 f(fmt, va_alist)처럼 쓰는 게 "far crearer"하다고도 했습니다. 이제 Torek이 누구편인지 아시겠습니까.

자의적인 번역입니다. 위에 lsj0713 님께서 해당 문장의 의도를 올바르게
설명하고 계시는군요. Chris 는 그 외의 다양한 부분에서 varargs 의 그와
같은 사용이 자신이 보기에는 더 낫다고 이야기하고 있습니다. 하지만, 분
명 공식적으로는 잘못된 방법이라고 단정하고 있으며, 다른 사람들에게
va_alist 를 단독으로 쓰라고 강조까지 하고 있습니다. "fmt, va_alist"
같은 방법으로 좀 더 검색해 보시기 바랍니다 - "개인적으로" 선호하는 기
술과 그 기술의 올바른 사용 방법에는 차이가 있을 수 있습니다.

프로그래밍을 하면서 제가 생각하는 가장 위험한 태도는 함부로 특정 가정
에 의존하는 것입니다. "내가 지금까지 해봤더니 되더라" 라는 식의 경험에
만 의존한 프로그래밍은 그 프로그램의 생명을 단축시키는 결정적 계기가
될 수도 있습니다. 어떤 것이 더 바람직한 방법인지를 공식적인 문서나 다
른 경험 많은 개발자들이 분명히 하고 있으며, 그 바람직한 방법을 사용하
는데 그리 큰 불편이 따르지 않는다면, 아주 작은 관심만으로 자신의 프로
그램을 보다 견고하게 또 이식성을 갖추도록 만들 수 있습니다.

그럼...

--
Jun, Woong (woong at gmail.com)
http://www.woong.org

익명 사용자의 이미지

lsj0713 wrote:
"MIPS version of 'varargs' does not work when the first
argument is a double. Since the MIPS compiler is migrating to ANSI,
stdarg should be used which fixes the problem. 'stdarg.h' use is
fully supported by the 2.10 compiler. The caller needs the
prototype to be visible (a requirement of ANSI for 'stdarg' calls)"

MIPS 컴파일러에선 varargs가 문제가 있음을 말하고 있으며, 새로운(글이 쓰여진 때가 1990이므로) ANSI C의 표준 방식인 stdarg.h를 사용하기를 권하고 있습니다. 제가 보기엔 MIPS컴파일러가 표준의 stdarg 방식을 제대로 지원하고 있기에, 굳이 기존의 코드와의 호환성을 고려할 필요가 없다고 생각하여 vararg.h 방식을 제대로 지원하지 않는 것으로 보입니다.

또한, 위의 글만 가지고서는 f(fmt, va_alist) 와 같은 코드가 제대로 동작하는지에 대해서도 알 수가 없습니다. f(va_alist) 가 문제가 있다면 마찬가지로 f(fmt, va_alist)도 똑같은 문제를 일으킬 가능성도 배제할 수 없습니다.


님이나 위의 분이나 공통적으로 아주 이상한 가정을 하고 계십니다. "f(fmt, va_alist)를 잘못 작성한다면 잘못된 코드가 생성될 것이다" - 그렇게 주장하면서 실제 "예"는 도대체 어디에 있습니까? 어이 없게도 "f(fmt, va_alist)는 제대로 동작하지 않을 것 같았다. 그런데 실제로는 제대로 동작했다"는 글에서 중간을 싹둑 잘라서 "f(fmt, va_alist)는 제대로 동작하지 않을 것이다"라고 원문의 의도를 왜곡한 걸 증거로 삼다니 말이죠.

아무튼 f(fmt, va_alist)는 제대로 작성하지 못할 지도 모른다고 가정하면서 어떻게 f(va_alist)는 제대로 작성할 수 있을 거라고 확신을 갖는지 이해가 안갑니다. 위의 사례는 그 반박으로 쓸만하다고 생각합니다.

Quote:
번역하신 부분은 " int foo(fmt, va_alist) char *fmt; va_dcl { <...code...> } " 와 같은 코드가 그와 같은 장점이 있음에도, 옳지 않은 코드이므로 쓰지 말라고 만류하는 부분입니다. (쓰고 싶은 유혹을 떨쳐버리라고 하고 있지요) 절대 비꼬거나 위와 같은 코드를 옹호하는 말이 아닙니다. 번역하신 부분을 다시 한번 읽어보십시오.

허허... Chris Torek이 아까 4.4BSD printf 짠 사람이라니까요. 남들보고는 쓰지 말라고 하고("Officially Correct"하지 않다는 이유 하나 만으로) 자기 자신은 계속 쓰는 이유는 뭐라고 생각하십니까.

Quote:
제가 보기엔 방준영님께서 지나치게 흥분하고 계신 것이 아닌가 하는 생각이 듭니다. 머리를 차게 식히시고 다시한번 관련글들을 읽어보시는 것이 좋지 않을까 싶습니다. 흥분한 상태에서 쓴 글은 많은 허점이 드러나게 마련입니다.

저는 전혀 흥분한 것이 없는데 무슨 말씀을 하시는 건지...? 아무튼 왜 f(fmt, va_alist)가 문제가 되는지는 증거로 제시하지 못하면서 이상한 가정법으로 잘못된 코드를 생성할 거라고 암시하거나, "그냥 책에 그렇게 나와 있으니까" f(va_alist)를 써야 한다는 주장은 궁색하기 짝이 없습니다. 그렇게 얘길 하려면 그분은 그냥 처음부터 "책에 나온대로만 써라"라고 하지, 무슨 이식성이 그나마 낫고, 잘못된 코드가 생성되고, (중간에는) AT&T와 BSD의 실수가 어쩌고 하는 근거없는 주장들을 늘어놓습니까.
lsj0713의 이미지

Quote:

님이나 위의 분이나 공통적으로 아주 이상한 가정을 하고 계십니다. "f(fmt, va_alist)를 잘못 작성한다면 잘못된 코드가 생성될 것이다" - 그렇게 주장하면서 실제 "예"는 도대체 어디에 있습니까? 어이 없게도 "f(fmt, va_alist)는 제대로 동작하지 않을 것 같았다. 그런데 실제로는 제대로 동작했다"는 글에서 중간을 싹둑 잘라서 "f(fmt, va_alist)는 제대로 동작하지 않을 것이다"라고 원문의 의도를 왜곡한 걸 증거로 삼다니 말이죠.

저는 잘못된 코드가 생성된다고 하지 않았습니다. 이런 경우에는 보통 이식성이 떨어지는 코드라던가, 좋지 않은 코드라고 말을 합니다만(잘못된 코드라는 말과 좋지않은 코드라는 말은 다릅니다. 전자는 완벽히 오류라는 뜻이고, 후자는 추천되지 않는다는 뜻입니다), 이번에 제가 올린 글들에서는 직접적으로 '잘못된 코드다'라고 말한 적이 없습니다. (전웅님도 그렇게 하지 않는 것이 추천된다고 했을 뿐입니다.) 그러나 이식성이 떨어진다고 '절대적으로' 쓰지 말아야 되는 코드일까요? 그건 아닙니다. 다음에서 계속 얘기하지요.

Quote:

허허... Chris Torek이 아까 4.4BSD printf 짠 사람이라니까요. 남들보고는 쓰지 말라고 하고("Officially Correct"하지 않다는 이유 하나 만으로) 자기 자신은 계속 쓰는 이유는 뭐라고 생각하십니까.

'특정한 조건' 아래에서는 이식성이 없는 코드나 일반적으로 바람직하지 않다고 말해지는 코드를 사용할 수도 있습니다. 특정한 C 언어 구현체가 그러한 코드를 사용할 수밖에 없도록 강요하는 경우도 있고, 성능상의 문제로 그러는 경우도 있습니다.

언급하신 코드의 경우에도, __STDC__ 매크로 상수가 정의된 환경에서는 varargs.h 대신 stdarg.h를 사용하고 있습니다. 즉. 일반적으로 현존하는 거의 모든 컴파일러(ANSI C를 지원하는 컴파일러)에서는 예로 들고 계신 코드 - f(fmt, va_alist) 형식의 - 는 전처리 과정에서 없어져 버립니다. Chris torek이 어떤 환경을 대비하기 위해 그렇게 작성해 놓았는지는 모르겠지만, 그 환경에서는 ANSI C도 지원하지 않고, f(va_alist)보다는 f(fmt, va_alist)가 더 잘 작동되는 환경일 것입니다(아마도 무척 안좋은 환경인가 봅니다-_-; 임베디드나 오래된 컴퓨터 기종이겠지요). 그렇지 않다면 자기 자신이 추천하지 않는다는 코드를 굳이 쓸 필요가 없겠지요.

처음으로 돌아가서, 맨 처음에 질문을 올리신 분께서는 어떠한 환경에서라는 말을 하지 않았습니다. 그런고로 전웅님께서는 일반적으로 추천되는 코드를 권했을 뿐입니다. 그리고 그 다음에도 일반적으로 추천된다고만 했을뿐, 그 코드가 잘못되었다거나 써서는 안되는 코드라고 한적이 없습니다. 오히려 특정한 구현체 아래에서는 테스트 후에 사용할 수도 있다고 적고 있습니다.

Quote:

<To OP>
일부 환경에서 테스트 해본 결과, fmt 까지 처리한 ap 를 <stdio.h> 의
v*printf() 에 넘기는 것에 큰 문제가 없음을 확인하였습니다. 이식성은 보
장되지 않지만, 필요한 환경에서는 간단한 테스트 후에 사용하실 수 있으리
라 생각합니다
</To OP>

이제 나올 말은 거의 다 나왔고, 더 이상의 논쟁은 무의미하리라 봅니다. (사실 10년도 더 전에 표준에서 제외된 코드를 가지고 논쟁하는 것 자체가 소모적입니다만...) 남은것은 각자가 관련 글들을 읽어보고, 자신이 좋다고 생각되는 방향으로 나아가는 것 뿐입니다. 만약 저라면, comp.lang.c 의 여러 사람들이 추천하고 강력히 권하는 쪽을 선택하겠습니다만...

익명 사용자의 이미지

소모적인 논쟁은 지양하고, 왜 f(fmt, va_alist)가 아무 문제가 없는지 기술적으로 살펴보겠습니다.

사실 그 이유는 간단합니다. ANSI 표준 stdarg가 가변 인수 리스트를

Quote:
printf(...)

로 정의 안하고

Quote:
printf(fmt, ...)

로 정의했으니까요. 만약에 가변 인수 리스트 앞에 고정 인수가 존재하는 게 *본질적으로* 어떤 기종에서 말썸을 일으키거나, 조금이라도 일으킬 가능성이 있다면, 표준을 그렇게 정의하지 않았겠지요. 마찬가지로, printf(fmt, va_alist)는 사실 ANSI의 ...을 va_alist로 치환해 놓은 것에 불과합니다. 이것이 잠재적으로 문제를 일으키리라고 가정할 이유는 없습니다.

역사적으로 유추해 본다면, Andrew Koenig이란 사람이 varargs를 정의할 때는 foo(fmt, va_alist)같은 용법은 염두에 두지 않았습니다. 그런데 어떤 사람이 어느날 그렇게 짜봤더니 신기하게도 잘 동작하는 겁니다. 그래서 여기저기 게시판에 잘된다고 보고를 했겠죠. 그러나 기존의 옳은 방식을 신봉하는 사람들은 그것이 varargs의 오남용이라고 생각했고, 뭔가 문제를 일으킬 것이라고 의심을 했습니다. 그러나 오랜 시간이 지나면서 foo(fmt, va_alist)는 아무 문제가 없음이 밝혀졌고--우연인지 필연인지 몰라도--ANSI에 반영된 쪽은 foo(fmt, va_alist)와 비슷한 방법이었습니다.

위에 쓴 부분에서 문제가 있다면 지적해 주시기 바랍니다.

전웅의 이미지

minzkn wrote:
즉, 원래 C만으로 가변인자를 처리하기위해서는 (매크로만으로...)
반드시 첫번째 인자가 주어져야 하는 방법이 되겠습니다.
즉 가변인자 첫번째를 취하기 위해서 매크로는 다음과 같은 예에서
다음과 같이 확장이 될겁니다.

void VArgumentTest( int s_FirstArgument, ...)
{
va_list(........

첫번째가변인자 = va_arg(....)

......va_end(......
}

void VArgumentTest( int s_FirstArgument, ...)
{

첫번째가변인자 = (cast type) (
(cast type *) (
(char *)(&s_FirstArgument)
+ (sizeof( int ) * (몇번째 인자 = 여기서는 1)
)
);

}

표준 C 언어의 경우 구조체 전체를 전달할 수도 있습니다. 따라서, 보여주
신 매크로 확장의 예가 보다 더 실제에 가까우려면 해당 implementation 에
서 어떻게 전달되는 인자를 정렬하는지를 파악하여 해당 인자에 접근하려는
데이터형의 크기와 그 정렬을 모두 고려해 포인터 연산을 해야합니다. C 가
처음 구현된 PDP-11 에서는 사실상 모두 word 단위의 인자만이 전달되었기
때문에 외관상으로도 간단한 포인터 연산을 통해 가변 인자 접근이 용이했
지만, 다양한 implementation 에서 구현되면서 서로 다른 방법이 사용된 것
입니다. 이식성을 위해 이를 표준화하려는 노력이 Andrew 의 varargs 매크
로 였으며, 그 매크로의 내부 구현이 어떻든지 상관없이 원 제작자가 명시
한 방법을 그대로 따르는 것이 가장 의도에 부합하는 것임에는 틀림이 없습
니다.

minzkn wrote:
꼭 이렇게 확장된다기보다 처리가 위와 같은 절차로 C만을 이용한 매크로
확장이 가능하다는 점입니다.

이미 보여드린 예에서도 나와 있지만, 스택만을 거치는 것이 아닌 독특한
calling convention 을 사용하는 implementation 에서는 전혀 다른 방법으
로 구현될 수도 있습니다 - 이것이 바로 <varargs.h>, <stdarg.h> 의 존재
이유인 것입니다.

minzkn wrote:
즉, 준영님이 말씀하신 첫번째 인자가 주어질수 있다는 것이
보다 C언어적으로 가까운 방법인듯 합니다.

물론 반드시 0(%esp)를 필요로 하는 방법만 있는것은 아닙니다.
함수내에서 변수를 처음(!)에 선언하고 그것의 주소로부터
뒤로 8바이트 뒤를 추적하면 그곳부터 0(%esp)가 될수도 있습니다.
하지만 이것은 스택프레임이 만들어진다면 12바이트 뒤가 된다는 점에서
좀 매크로 확장이 복잡할듯 합니다.

결론은 첫번째 인자가 반드시 없어야 정석이 되는것은 아닌듯 생각되며
또한 첫번째 인자가 반드시 있어야 하지도 않지만
있으면 좋은 그런 관계인거 같습니다.

즉, 준영님의 말씀하신대로 "첫번째 인자가 없어야만 하지는 않는다"라는 예기를
하고 싶습니다. 오히려 있는것이 가변인자처리가 수월할듯 합니다.

<varargs.h> 의 존재 목적 자체를 오해하고 계신 것 같습니다. 실제
va_alist 는 다수의 implementation 에서 다음과 같이 구현될 수 있습니다.

#define va_alist __args

즉, 간단히 int 형으로 선언되는 매개변수 하나로 치환되는 것입니다. 이는
실제로 님이 위에서 보이신 것처럼 각 인자에 접근하기 위한 시작점을 제공
하기 위함입니다. 즉, 원 저작자의 <varargs.h> 의 va_alist 에 대한 의도
는 그 매크로를 통해 implementer 가 가변 인자 접근에 필요한 모든 방법을
매개변수 명칭 선언 문맥에서 사용할 수 있도록 온전히 varargs 매크로에게
맡기자는 것입니다. 한 사람이 이식성을 향상을 위해 만든 매크로는 도입하
기 어려울 경우 다른 implementer 에게 선택되지 않을 수도 있습니다. 즉,
지금의 <stdarg.h> 처럼 고정된 매개변수가 최소한 1개는 있어야 하며, 그
뒤에 va_alist 가 따른다고 명시할 경우, 이를 만족하기 위해 어려움을 겪
을 수 있는 implementation 은 간단히 <varargs.h> 를 지원하지 않거나 전
혀 다른 방법으로 지원할 수 있습니다. 결국, 이식성 증진이라는 목적은 물
거품이 되고, 또 다시 <varargs.h> 를 두고 이식성에 대한 문제가 생길 수
있는 것입니다 - 반면, 국제 표준의 권위는 이와는 전혀 다른 상황을 만들
게 되므로, 위원회의 동의를 얻은 기술은 어렵지 않게 도입될 수 있습니다.

--
Jun, Woong (woong at gmail.com)
http://www.woong.org

전웅의 이미지

방준영 wrote:
님이나 위의 분이나 공통적으로 아주 이상한 가정을 하고 계십니다. "f(fmt, va_alist)를 잘못 작성한다면 잘못된 코드가 생성될 것이다" - 그렇게 주장하면서 실제 "예"는 도대체 어디에 있습니까?

뭔가 단단히 오해하고 계시는군요. 이미 여러번 강조했듯이, 어차피
<varargs.h> 는 권위있는 표준 같은 것이 아닙니다 - varargs 매크로는 표
준이 필요하던 시기에 사용된 것일 뿐입니다. 따라서, 어떤 것이 "잘못되었
다", "그렇지 않다" 가 중요한 것이 아닙니다. 어떤 implementation 이
<varargs.h> 를 이름만 같고 전혀 다른 방법으로 지원한다고 해도 이는 단
지 해당 implementation 의 품질 문제일 뿐 잘잘못을 따질 수 있는 근거가
없다는 것입니다.

이러한 이유로 님이 보여주신 예제에 대해 제가 조심스럽게 "잘못된" 것이
라 말씀드리지 않은 것입니다. 논의를 다시 살펴보면 아시겠지만, 제가 시
종일관 말씀드리는 바는, <varargs.h> 를 고안해 발표한 Andrew Koenig 이
의도한 바람직한 사용 방법은 va_alist 를 하나의 매개변수로 사용하는 것
이며, 이렇게 쓰는 것이 그리 많은 귀찮음을 요구하는 것은 아니기에 (OP
의 문맥에서 단지 fmt 를 선언해 va_arg() 만 한번 사용해주면 되죠), 충분
히 바람직한 방법을 따를 가치가 있음을 알리려는 것이었습니다. 즉, 변형
된 사용 방법이 의도한 환경에서 잘 작동함에도 이를 "잘못된" 것이라 부른
적이 없으며, 사용해서는 안 된다는 "금지" 를 표현한 적은 없습니다 - 오
히려 Chris 나 Douglas 같은 개발자들이 저보다 단정적인 표현을 사용하고
있습니다.

방준영 wrote:
어이 없게도 "f(fmt, va_alist)는 제대로 동작하지 않을 것 같았다. 그런데 실제로는 제대로 동작했다"는 글에서 중간을 싹둑 잘라서 "f(fmt, va_alist)는 제대로 동작하지 않을 것이다"라고 원문의 의도를 왜곡한 걸 증거로 삼다니 말이죠.

님이야 말로 인용한 글을 곡해하고 계십니다. Chris 는 뉴스 그룹의 다수의
글에서 공식적으로는 이식성을 위해 "사용해서는 안 된다" 는 입장에 밝힌
사람입니다. "내가 사용한 환경에서는 되지만 공식적으로는 잘못된 것이다"
와 "문제없으니 사용해라" 는 분명히 다른 주장입니다. 제가 자른 부분을
님께서 보충해 반론을 제기하신다면, 저는 그 부분에서도 잘려진 부분을 보
충해 다시 반론을 제기할 수 있습니다 - Chris 가 C 표준화 위원회를 언급
하는 부분에 대한 답변을 계속 확인해 보시기 바랍니다.

방준영 wrote:
아무튼 f(fmt, va_alist)는 제대로 작성하지 못할 지도 모른다고 가정하면서 어떻게 f(va_alist)는 제대로 작성할 수 있을 거라고 확신을 갖는지 이해가 안갑니다. 위의 사례는 그 반박으로 쓸만하다고 생각합니다.

모든 implementation 에서 f(va_alist) 가 작동할 것이라 주장한 적도 없습
니다 - 계속해서 다른 이의 주장을 극단으로 인식하는 경향이 있군요. 오히
려 Andrew Koenig 의 글을 인용하면서, varargs 매크로의 특성이 지극히
implementation-dependent 함을 강조했습니다. 제가 보기에 지금 님은 주장
하는 것에 급급하여 논의가 이어지는 문맥을 제대로 바로 보기 어려운 상황
인 것 같습니다.

님께서 f(fmt, va_alist) 가 작동하지 않는 반례를 요구하신다면, 반대로
f(fmt, va_alist) 가 ANSI C 이전의 "모든" impelemtation 에서 작동함을
보여달라 요구할 수도 있습니다. 즉, 서로가 서로 다른 가정을 가지고 있는
한 그 가정에 대한 반례는 전혀 반대의 것이 될 수 있습니다.

방준영 wrote:
Quote:
번역하신 부분은 " int foo(fmt, va_alist) char *fmt; va_dcl { <...code...> } " 와 같은 코드가 그와 같은 장점이 있음에도, 옳지 않은 코드이므로 쓰지 말라고 만류하는 부분입니다. (쓰고 싶은 유혹을 떨쳐버리라고 하고 있지요) 절대 비꼬거나 위와 같은 코드를 옹호하는 말이 아닙니다. 번역하신 부분을 다시 한번 읽어보십시오.

허허... Chris Torek이 아까 4.4BSD printf 짠 사람이라니까요. 남들보고는 쓰지 말라고 하고("Officially Correct"하지 않다는 이유 하나 만으로) 자기 자신은 계속 쓰는 이유는 뭐라고 생각하십니까.

님이 주장하는 바 ("아무 문제 없으므로 사용해라") 에 대해 유일한 근거가
Chris 의 코드라면 직접 Chris 의 의견을 들을 필요도 있을 것 같군요. 준
비해 보겠습니다.

방준영 wrote:
Quote:
제가 보기엔 방준영님께서 지나치게 흥분하고 계신 것이 아닌가 하는 생각이 듭니다. 머리를 차게 식히시고 다시한번 관련글들을 읽어보시는 것이 좋지 않을까 싶습니다. 흥분한 상태에서 쓴 글은 많은 허점이 드러나게 마련입니다.

저는 전혀 흥분한 것이 없는데 무슨 말씀을 하시는 건지...? 아무튼 왜 f(fmt, va_alist)가 문제가 되는지는 증거로 제시하지 못하면서 이상한 가정법으로 잘못된 코드를 생성할 거라고 암시하거나, "그냥 책에 그렇게 나와 있으니까" f(va_alist)를 써야 한다는 주장은 궁색하기 짝이 없습니다.

"반대로 내가 써보니 잘 되더라, 너도 그렇게 써라" 라는 주장도 제가 보기
엔 궁색한 주장에 불과합니다.

방준영 wrote:
그렇게 얘길 하려면 그분은 그냥 처음부터 "책에 나온대로만 써라"라고 하지, 무슨 이식성이 그나마 낫고, 잘못된 코드가 생성되고, (중간에는) AT&T와 BSD의 실수가 어쩌고 하는 근거없는 주장들을 늘어놓습니까.

AT&T 와 BSD 의 실수에 대한 근거를 인용하겠습니다 - "근거없는 주장" 이
라는 말씀은 실수하신 것입니다. 자신과 반대되는 의견에 대한 근거를 원할
경우 바람직한 방법은 근거를 정중히 요청하는 것이지, "근거없는" 이라고
치부해 버리는 것이 아닙니다.

Bob Larson (USC) wrote:
va_alist MUST be the only argument to a varargs function. (Lint (sun
3.2) gets this horrably WRONG. If you don't get a ton of error
messages from lint you made a mistake. (This is another case where
something non-portable happens to work on 4.2bsd so people assume it
works everywhere.)

Guy Harris (SUN) wrote:
I guess AT&T made a mistake, too, because that code linted quite happily
on a 3B2 running S5R3.1. 4.2BSD has nothing to do with it....

기본적으로 토론하는 자세 조차 되어 있지 않은 분과 기술적인 이야기를
나누는 것이 어렵다는 사실을 새삼 재확인하게 되는군요.

그럼...

--
Jun, Woong (woong at gmail.com)
http://www.woong.org

전웅의 이미지

방준영 wrote:
소모적인 논쟁은 지양하고, 왜 f(fmt, va_alist)가 아무 문제가 없는지 기술적으로 살펴보겠습니다.

사실 그 이유는 간단합니다. ANSI 표준 stdarg가 가변 인수 리스트를

Quote:
printf(...)

로 정의 안하고

Quote:
printf(fmt, ...)

로 정의했으니까요.

... 를 사용하기 위해서는 표준에 의해 함수 원형 선언 (혹은 정의) 이 요
구됩니다 - translator 와 library 가 분리되어 제작되는 경우는 허다합니
다. 이는 기술적으로도 잘못된 예일 뿐더러, K&R C 로 프로그램을 작성하는
이유는 어떠한 이유에서든지 표준 C 가 지원되지 않는 상황을 위한 것입니
다. 표준 C 가 지원되지 않는 상황을 논하면서 "표준 C 가 ... 하기 때문에"
라는 근거는 적절하지 않습니다.

또한, 님의 예를 기술적으로 옳게 고쳐 표준 C 가 지원되는 상황에서 접근
하자면, 그와 같은 implementation 에서 varargs 를 지원하기 위해
va_alist 를

#define va_alist int __arg, ...

로 정의할 수 있습니다. 그러면,

f(va_alist) ...

extern int f(int __arg, ...) ...

와 동등한 의미로 확장될 것입니다. 물론,

f(char *fmt, va_alist) ...

도 문제가 없는 듯해 보이지만, 만약 사용자가 f() 의 원형 선언을 다음과
같이 둔다면,

int f(char *fmt, ...);

f(char *fmt, va_alist) ...

매개변수 개수가 맞지 않기에 번역이 거절될 수 있습니다 - va_alist 의 기
본적인 의미를 알고 계신다면, va_alist 를 함수 선언의 매개변수에 써야
한다고 주장하시지는 않으시겠지요?

방준영 wrote:
만약에 가변 인수 리스트 앞에 고정 인수가 존재하는 게 *본질적으로* 어떤 기종에서 말썸을 일으키거나, 조금이라도 일으킬 가능성이 있다면, 표준을 그렇게 정의하지 않았겠지요.

님의 추측에 불과합니다. ANSI C 의 라이브러리 표준화 장을 맡았던 PJ
Plauger 가 <stdarg.h> 의 역사에 대해 했던 말을 인용합니다:

PJ Plauger wrote:
The idea was to define the macros in such a way that that all known
implementations of C could conform without major changes. Some
implementation had to alter their translators to provide ...

이번에도 첫번째 문장을 읽고 "알려진 implementation 에서 문제가 없다는
데 무슨 소리냐?" 라고 주장하시지는 않으리라 믿습니다 - "major change"
라고 표현하고 있으며, 두번째 문장에서 "had to alter" 라는 표현을 사용
했습니다. 다른 글에서도 말씀드렸듯이 ISO 수준의 국제 표준은 한 개인이
만든 매크로와는 권위가 다릅니다. 때로는 기존 implementation 의
implementer 들이 표준을 따르기 위해 자신의 implementation 을 직접 수정
해야 하는 일이 발생합니다 - 한편에서는 이것이 많은 변화를 담은 C99 가
아직 대중화되지 못하는 이유라고 불평하기도 합니다.

방준영 wrote:
마찬가지로, printf(fmt, va_alist)는 사실 ANSI의 ...을 va_alist로 치환해 놓은 것에 불과합니다. 이것이 잠재적으로 문제를 일으키리라고 가정할 이유는 없습니다.

이는 표준에서 ... 를 도입한 이후에만 적용되는 이야기입니다. 만약, 님께
서 "표준 C 를 지원하는 환경에서, va_alist 를 ... 로 정의하고, f(...)
와 같은 형태로 더불어 지원한다면, f(fmt, va_alist) 가 작동하지 않을리
가 없다" 라고 주장하신다면 저는 100% 동의합니다. 하지만, 만약 표준 C
가 제대로 지원되는 환경이라면 왜 굳이 K&R C 를 사용하면서 <stdarg.h>
보다 이식성이 떨어지는 <varargs.h> 에 대해 고민하겠습니까? K&R C 의 문
맥이라는 것은 말 그대로 표준 C 가 철저히 배제된 상태를 말하는 것입니다.

방준영 wrote:
역사적으로 유추해 본다면, Andrew Koenig이란 사람이 varargs를 정의할 때는 foo(fmt, va_alist)같은 용법은 염두에 두지 않았습니다. 그런데 어떤 사람이 어느날 그렇게 짜봤더니 신기하게도 잘 동작하는 겁니다. 그래서 여기저기 게시판에 잘된다고 보고를 했겠죠. 그러나 기존의 옳은 방식을 신봉하는 사람들은 그것이 varargs의 오남용이라고 생각했고, 뭔가 문제를 일으킬 것이라고 의심을 했습니다. 그러나 오랜 시간이 지나면서 foo(fmt, va_alist)는 아무 문제가 없음이 밝혀졌고--우연인지 필연인지 몰라도--ANSI에 반영된 쪽은 foo(fmt, va_alist)와 비슷한 방법이었습니다.

제가 알고 있는 역사와 다르군요. 구체적인 근거를 부탁드립니다. 그 많은
근거를 보였음에도 추측과 가정만 이야기하며 근거를 보이지 않는다고 불평
했던 사람은 님이 아니던가요?

그럼...

--
Jun, Woong (woong at gmail.com)
http://www.woong.org

익명 사용자의 이미지

이런 식으로 토론에서 요리조리 빠져나가는 분의 공통적인 특징은, 절대 혹은 현실적으로 불가능한 가정을 툭 던져 놓고 반박을 당하면 "내가 언제 그렇다고 했느냐. 그럴지도 모른다고 했지"라고 되려 큰소리를 친다는 점입니다. 그리고 구구절절 쓸데없는 얘길 늘어놓으면서 변명을 하기에 급급하지요.

이런 분들의 또 한가지 특성은, 주장을 일단 먼저 하고 근거는 나중에 찾는다는 점입니다. 처음에 제가 printf(fmt, va_alist)처럼 써도 된다는 걸 보여주자, 그것보다 printf(va_alist)라고 쓰는 게 이식성이 "그나마" 낫다는 근거없는 주장을 펼친 뒤, 구글에서 검색해서 입맛에 맞게 주장을 뒷받침하는 글을 가려뽑는 것이죠. 그 글 어디서도 이식성에 문제가 있는 실제적인 증거라곤 없는데도 말이죠(지금까지 인용된 글에는 전부 문제가 있을지도 모른다, 실수일지도 모른다는 뜬구름 잡는 이유가 전부입니다). 그리고 처음에는 Chris Torek이 누군지도 몰랐으면서 BSD printf 짠 사람이라고 지적하자, 마치 알고 있었다는 듯이 아는 척을 하기도 하지요.

그리고 처음에는 "이런 위험한 태도를 가진 분이 ..." 어쩌고 하더니, 아군으로 착각하지 말라는 핀잔에 "아군은 기술적 토론에 적합한 말이 아니"라는 둥 맞받아 치면서도 정작 토론 상대방에게 "위험한 태도"를 가졌다고 비난한 자신이 앞뒤가 안맞는다는 건 깨닫지 못하기도 하지요.

거기다 자기 주장을 증명하기 위한 증거로 제출하는 것도 의도적으로 글을 가위질하거나, "우리편의 누가 내 말이 옳다고 말했으므로" 내 말이 맞다고까지 주장을 하지요. 그런데 그 "옳다"는 것이 정말로 옳다면, 사람의 말이 아니라 실제적인 증거는 왜 아직까지 못 꺼내고 있나요? 인터넷 검색이 덜 끝났나요?

그리고 무엇보다 이런 분들의 공통적인 특징은, 정작 프로그램은 짜본 적이 없으면서 책은 어떻게 열심히 읽었는지, 뭐가 어떻고 저떻고 지적을 잘하는 반면, 애매한 문제가 생겨서 대답하기 곤란한 지경이 되면 "그건 책에 안나오니까 잘못된 걸지도 모른다"식으로 권위로 밀어붙이려고 하지요. 물론 "잘못되었다"고 확언은 못합니다. 나중에 빠져나올 구멍을 만들어 놓기 위해서죠. 그럼 똑같은 논리로 "잘못이 아닐지도 모르지 않은가"라고 반박하면, "내가 말하는 것은 그뜻이 아니라 어느 회사의 누가 어쩌고 저쩌고..." 실컷 엉뚱한 얘기를 꺼내놓고 연막을 피웁니다.

그래서, 뭐가 어떻단 말이죠? printf(fmt, va_alist)가 문제가 있다는 겁니까, 없다는 겁니까? printf(fmt, va_alist)가 이식성이 "그나마" 떨어진다는 겁니까 아니란 겁니까? 잘못된 코드를 생성한단 얘깁니까, 아니란 얘깁니까? "이런 분과 토론 괜히 했네"가 결론입니까?

전웅의 이미지

방준영 wrote:
이런 식으로 토론에서 요리조리 빠져나가는 분의 공통적인 특징은, 절대 혹은 현실적으로 불가능한 가정을 툭 던져 놓고 반박을 당하면 "내가 언제 그렇다고 했느냐. 그럴지도 모른다고 했지"라고 되려 큰소리를 친다는 점입니다. 그리고 구구절절 쓸데없는 얘길 늘어놓으면서 변명을 하기에 급급하지요.

예상했지만, 제가 생각했던 것보다 토론 매너가 더 없군요. 이 주제와 관련
된 내용을 comp.lang.c 에 포스팅할 예정입니다. 그곳에서 올라오는 답변에
도 문제가 있다고 생각하신다면 똑같은 방법으로 반론을 제기해 보시기 바
랍니다.

방준영 wrote:
이런 분들의 또 한가지 특성은, 주장을 일단 먼저 하고 근거는 나중에 찾는다는 점입니다. 처음에 제가 printf(fmt, va_alist)처럼 써도 된다는 걸 보여주자, 그것보다 printf(va_alist)라고 쓰는 게 이식성이 "그나마" 낫다는 근거없는 주장을 펼친 뒤, 구글에서 검색해서 입맛에 맞게 주장을 뒷받침하는 글을 가려뽑는 것이죠. 그 글 어디서도 이식성에 문제가 있는 실제적인 증거라곤 없는데도 말이죠(지금까지 인용된 글에는 전부 문제가 있을지도 모른다, 실수일지도 모른다는 뜬구름 잡는 이유가 전부입니다).

제가 말씀드린 내용 중에서 단 하나라도 틀린 것이 있다면 지적해보시기 바
랍니다. 진심으로 드리는 말씀입니다. 단 하나라도 기술적인 내용이 틀렸다
면 틀렸음을 인정하고 해당 부분에 대해 사과드리겠습니다. 그렇지 못한다
면, 이토록 매너 없는 글을 올리신 님의 인격만 손상될 뿐입니다.

방준영 wrote:
그리고 처음에는 Chris Torek이 누군지도 몰랐으면서 BSD printf 짠 사람이라고 지적하자, 마치 알고 있었다는 듯이 아는 척을 하기도 하지요.

더 이상 할 말이 없군요. 자신이 잘못 알고 있는 부분을 지적당하자 격분하
여 이런 식의 글을 올리는 어처구니 없는 사람일줄은 몰랐습니다. 그동안
님의 여러 글을 보며 님에 대해 내심 많은 기대를 가지고 있었는데 이번 토
론으로 실망뿐이 남지 않는군요.

저는 수년전부터 comp.std.c, comp.lang.c 논의에 참여하고 있었으며 (제가
현재의 e-mail 주소로 포스팅했던 글들을 검색해 보시기 바랍니다 - 중간에
e-mail 주소를 바꿨으므로 최소한 그 이전부터 뉴스그룹 논의에 참여하고
있었습니다), 표준 C 언어를 학습하는 과정에서 Chris Torek 이 현재 다니
는 직장에 취직하기 전부터 알고 있었습니다. 님이 BSD 의 예를 보이기에
BSD 쪽에 기여하는 바가 큰 Chris Torek 의 글을 의도적으로 인용한 것이며,
제가 UNIX 문서를 인용했었기에 UNIX 의 역사와 함께한 Douglas 의 글을 의
도적으로 인용한 것입니다.

물론, 이렇게 아무리 떠들어봤자 이미 님은 이성을 잃은 상태일지 모르니
또 다른 번명에 불과하다고 말씀하실지도 모릅니다. 믿고 안 믿고의 문제는
제가 강제할 수 없는 부분이니 어쩔 수 없습니다만...

방준영 wrote:
그리고 처음에는 "이런 위험한 태도를 가진 분이 ..." 어쩌고 하더니, 아군으로 착각하지 말라는 핀잔에 "아군은 기술적 토론에 적합한 말이 아니"라는 둥 맞받아 치면서도 정작 토론 상대방에게 "위험한 태도"를 가졌다고 비난한 자신이 앞뒤가 안맞는다는 건 깨닫지 못하기도 하지요.

"위험한 태도" 라는 표현에 도대체 무슨 문제가 있는지 알기 어렵습니다.
실제 경험에서 비롯된 가정에 의존한 프로그래밍은 "위험" 합니다. 표준 C
언어로 작성한 프로그램이 표준을 따른다고 주장하는 implementation 에서
제대로 동작하지 않는다면 이는 implementation 을 고치라고 주장할 수 있
는 부분이지만, 아무도 보장해주지 않는 우연의 일치로 얻은 결론에 의존해
프로그램을 작성한 후 프로그램이 다른 환경에서 오작동한다면 이는 프로그
래머의 잘못입니다 - 실제로 과거 반환형이 int 인 함수에서 return; 을 해
주면 1 을 반환해주는 특정 컴파일러의 행동에 의존했다가 업그레이드된 동
일한 컴파일러에서 작동하지 않아 프로그램을 고치느라 고생했다는 글이 올
라온 적도 있습니다. 반환형이 void 가 아닌 경우의 return; 은 표준이 보
장해주는 행동이 아니기에 의존해서는 안 되는 행동이었던 것입니다.

방준영 wrote:
거기다 자기 주장을 증명하기 위한 증거로 제출하는 것도 의도적으로 글을 가위질하거나, "우리편의 누가 내 말이 옳다고 말했으므로" 내 말이 맞다고까지 주장을 하지요. 그런데 그 "옳다"는 것이 정말로 옳다면, 사람의 말이 아니라 실제적인 증거는 왜 아직까지 못 꺼내고 있나요? 인터넷 검색이 덜 끝났나요?

아직 답변 메일을 못 받았습니다. varargs 매크로 문제가 사실상 표준 C 관
련 뉴스 그룹에서는 OT 이기에 포스팅을 꺼렸습니다만, 이제는 포스팅을 해
서 저도 다양한 사람들의 의견을 듣고 varargs 에 대해 보다 자세히 알아보
고자 합니다.

방준영 wrote:
그리고 무엇보다 이런 분들의 공통적인 특징은, 정작 프로그램은 짜본 적이 없으면서 책은 어떻게 열심히 읽었는지, 뭐가 어떻고 저떻고 지적을 잘하는 반면,

인신공격이 극에 달하는군요. 제가 프로그램을 짜본 적이 있는지 없는지 어
떻게 아시죠? 실망의 실망을 거듭하는군요. 예, 저는 프로그램은 하나도 짜
본 적이 없으며, 책만 읽을 줄 알아서 프로그램을 진짜로 짜시는 님같은 분
들이 사용하는 C 언어의 표준화 과정에 참여해 아무것도 모르면서 C 표준을
수정하기도 했습니다. 이제 속이 좀 시원하신지요?

더 이상의 글에 답변을 다는 제 자신이 한심스러워 그만하겠습니다. 님이
맞습니다. f(fmt, va_alist) 로 쓰는 것에 아무 문제가 없나 봅니다.
<varargs.h> 매크로의 원 저작자인 Andrew Koenig, C 언어와 UNIX 의 역사
와 함께한 Douglas A. Gwyn 같은 사람들의 말만 믿고 f(va_alist) 를
("강요" 도 아닌) "추천"한 제가 잘못했습니다. 아마도 그 사람들이 거짓말
을 했나 봅니다. 진심으로 사과드리며, 이만 줄이겠습니다.

그럼...

--
Jun, Woong (woong at gmail.com)
http://www.woong.org

익명 사용자의 이미지

Quote:
printf(fmt, va_alist)가 문제가 있다는 겁니까, 없다는 겁니까? printf(fmt, va_alist)가 이식성이 "그나마" 떨어진다는 겁니까 아니란 겁니까? 잘못된 코드를 생성한단 얘깁니까, 아니란 얘깁니까?

하하하, 이 질문에 대답하는 게 그렇게 힘들어서 구구절절한 얘기로 논문을 한편 쓰시려나 봅니다. 결론은 제가 내어 드리죠: "printf(fmt, va_alist)는 문제가 있을지도 모른다. 이식성이 떨어질지도 모른다. 잘못된 코드를 생성할 지도 모른다. 따라서 써도 될지 안될지 모른다."
전웅의 이미지

방준영 wrote:
Quote:
printf(fmt, va_alist)가 문제가 있다는 겁니까, 없다는 겁니까? printf(fmt, va_alist)가 이식성이 "그나마" 떨어진다는 겁니까 아니란 겁니까? 잘못된 코드를 생성한단 얘깁니까, 아니란 얘깁니까?

하하하, 이 질문에 대답하는 게 그렇게 힘들어서 구구절절한 얘기로 논문을 한편 쓰시려나 봅니다.

대답하기가 힘들어서가 아니라 대답할 가치를 못 느껴서 입니다. 님이 토론
에 임하는 자세를 볼 때, 이 토론 자체가 무가치하게 느껴지기 때문입니다.
제가 얻은 정답은 "님이 잘못된 이야기를 뿌리고 다니든 말든 상관할 바가
아니었다" 입니다 - 그 전에도 님의 글을 읽으면서 말씀드리고 싶은 것들이
많았지만, 개인적인 시간이 부족하여 언급하지 않았었습니다. 이번에는 제
가 먼저 OP 께 제공한 답변에 님이 다른 내용을 언급하셔서 thread 가 이리
도 길어졌습니다만... 한가지 확실히 배운 건, 님의 인격이나 실력이 제가
생각했던 것보다 부족해서 많이 실망했다는 사실입니다. 아직 제가 사람을
제대로 보는 눈이 부족한 것 같습니다.

방준영 wrote:
결론은 제가 내어 드리죠: "printf(fmt, va_alist)는 문제가 있을지도 모른다. 이식성이 떨어질지도 모른다. 잘못된 코드를 생성할 지도 모른다. 따라서 써도 될지 안될지 모른다."

예, 제가 현재 이 문제에 대해서 가지고 있는 생각은 "모른다" 입니다. 분
명, 많은 사람들이 긴 시간동안 언급하신 구조를 "잘못된" 것이라 단정해왔
고, 그 이론적 및 논리적 뒷받침은 충분하지만, 단지 실제 그와 같은 구조
에 오작동하는 구체적인 implementation 을 제시하지 못했다는 이유만으로
그 많은 사람들의 소중한 의견과 제가 인용한 근거들이 무가치한 것으로 전
락했습니다. 이런 상황에서 님이 그토로 무례한 방법으로 글을 남기는데도
무리해서 논의를 이끌어간다는 것 자체가 우스꽝스럽다고 생각합니다.

님과의 논의와는 별도로, 저 역시 실제 단 하나의 구체적인 implementation
도 변형된 구조에 오작동 하지 않았다면, 왜 그토록 다수의 실력자들이 그
와 같은 구조를 강한 표현으로 막아왔는지 궁금합니다. 이 부분에 대해서는
뉴스그룹 논의나 메일을 통해 그 사람들과 직접 이야기해 알아보고자 합니
다.

그럼...

--
Jun, Woong (woong at gmail.com)
http://www.woong.org

seoleda의 이미지

역사적인 이야기는 잘 모르겠지만.. 어디까지나 그냥 제 생각입니다만

f(fmt, va_alist)는 적어도 하나의 전달인자가 있다는 것을 제외하곤 둘다 동일해 보이네요. 따라서

f(fmt, va_alist)라는 선언으로으로는 f(a0), f(a0, a1), f(a0, a1, a2), f(a0, a1, a2, a3), ... 등등이 가능하고,

f(va_alist)는 위의 선언에 f() 를 추가적으로 호출할 수 있으니깐 f(va_alist)가 잘 동작하는 컴파일러에서 f(fmt, va_alist)도 잘 동작할 것으로 생각되지만, 반대로 f(fmt, va_alist)가 동작하는 컴파일러 에서 f(va_alist)가 동작하리라곤 보장할수 없지 않나요?

개인적인 생각으론 f(fmt, va_alist)가 fmt를 특정한 용도로 사용하면 f함수 내부에서 전달인자를 몇개를 줘서 호출 했는지 알수 있으므로 더 구현하기 쉬울거 같습니다. ^^

전웅의 이미지

seoleda wrote:
역사적인 이야기는 잘 모르겠지만.. 어디까지나 그냥 제 생각입니다만

f(fmt, va_alist)는 적어도 하나의 전달인자가 있다는 것을 제외하곤 둘다 동일해 보이네요. 따라서

f(fmt, va_alist)라는 선언으로으로는 f(a0), f(a0, a1), f(a0, a1, a2), f(a0, a1, a2, a3), ... 등등이 가능하고,

f(va_alist)는 위의 선언에 f() 를 추가적으로 호출할 수 있으니깐 f(va_alist)가 잘 동작하는 컴파일러에서 f(fmt, va_alist)도 잘 동작할 것으로 생각되지만, 반대로 f(fmt, va_alist)가 동작하는 컴파일러 에서 f(va_alist)가 동작하리라곤 보장할수 없지 않나요?

개인적인 생각으론 f(fmt, va_alist)가 fmt를 특정한 용도로 사용하면 f함수 내부에서 전달인자를 몇개를 줘서 호출 했는지 알수 있으므로 더 구현하기 쉬울거 같습니다. ^^

위에서 제가 인용한 내용 중에 항상 고정된 개수의 인자를 레지스터를 통해
전달하는 implementation 에 대한 이야기가 나옵니다 - K&R C 의 경우 구조
체 대상체를 인자로 전달할 필요가 없기 때문에 이와 같은 상황이 발생하기
가 더욱 쉽습니다. 그와 같은 경우에는 va_alist 가 레지스터를 통해 전달
된 인자들이 va_arg() 를 통해 적절히 접근될 수 있도록 배려해줘야 하기
때문에 특이한 방법을 동원하게 되고, 이 경우 f(va_alist) 는 작동해도,
(implementation 이 따로 배려해 주지 않는 이상) f(fmt, va_alist) 는 작
동하지 않을 수 있습니다.

현재 논의에서 유일하게 문제가 되는 것은 바로 그와 같은 implementation
이 구체적으로 무엇이냐 입니다.

그럼...

--
Jun, Woong (woong at gmail.com)
http://www.woong.org

익명 사용자의 이미지

자, 이제 시원한 밤이 되었으니 감정을 가라앉히고 새로운 각도로 정리를 해봐야겠습니다. 8)

우선, 제가 자금까지 쓴 글을 찬찬히 읽어봤더니, 무수히 많은 오류와 "오버"가 쉽게 눈에 띄더군요. 이 자리에서 불필요하거나 잘못된 내용, 그리고 특히 무례한 말투를 사용한 것에 대해 깊이 사과드립니다. 굳이 변명을 드리자면, 그것들은 처음에는 전웅님을 개인적으로 비난하려고 한 의도가 아니었는데, 전웅님의 글에 대한 반박을 하려다 제 멋에 겨워 그만 선을 넘어 버리고 말았습니다(아뿔싸!). 그 점을 너그러이 이해해 주시면 감사하겠습니다. 더 굳이 변명을 하자면, 며칠전 "첫사랑 사수 궐기대회"란 영화를 보고 나서 부쩍 울분을 느끼는 경우가 많아진 것 같습니다(보신 분들은 무슨 얘긴지 잘 아실 겁니다). 8)

사실, 이 문제는 f(fmt, va_alist)가 왜 쓰면 안되는지 그점만 증명되면 쉽게 끝날 성질의 것입니다. 이전에 여러 개발자들이 "안된다"고 했으니 뭔가 이유는 있겠지요. 그렇지만 이상하게도 어디서도 그 이유는 아직 발견되지 않았고, 저는 그게 더욱 의구심이 일었던 것입니다. 표준이라고 해서 항상 허점이 없는 것은 아니므로, 더군다나 표준이 아니라 "공식적으로 옳은" varargs는 더욱 그런 문제를 가지고 있을 수 있겠지요(그래서 ANSI 표준에서는 varargs를 stdarg로 대체한 것이니까). 지금 와서 옛날 스타일에 관해 왈가왈부 해서 무슨 소용이냐는 분도 계시지만, 오히려 저는 이것이 재미있고 흥미로운 문제라고 생각합니다. 레지스터 기반 인수 전달 방법과 스택 기반 인수 전달 방법, 컴파일러별 차이, 기타 등등 해서 C에 관해 많은 지식을 얻을 수 있을 것입니다.

그럼 이만 저는 한발 물러서서 printf(fmt, va_alist)가 가진 문제점이 지적되는 것을 지켜보도록 하겠습니다. 다시 한번 전웅님께: 이번 일로 저에 대해 너무 나쁜 감정을 가지게 되셨을 것 같습니다. 그렇지만 직접 만나 보면 제가 그렇게 나쁜 사람은 아닙니다(오히려 그 반대라고 주장하고 싶으나 앞에 쓴글 때문에 그렇게 말할 용기는...). 그냥 제가 더위를 먹어서 소리 몇번 질렀다고 생각해 주세요. 8) 그럼... (꾸벅~)

전웅의 이미지

방준영 wrote:
자, 이제 시원한 밤이 되었으니 감정을 가라앉히고 새로운 각도로 정리를 해봐야겠습니다. 8)

이렇게 인용해가면서 답변하는 것이 어울리는 상황은 절대 아닙니다만, 의
도는 님의 잘못을 지적하려는 것이 분명 아니니 이해해 주시리라 믿습니다.

방준영 wrote:
우선, 제가 자금까지 쓴 글을 찬찬히 읽어봤더니, 무수히 많은 오류와 "오버"가 쉽게 눈에 띄더군요. 이 자리에서 불필요하거나 잘못된 내용, 그리고 특히 무례한 말투를 사용한 것에 대해 깊이 사과드립니다. 굳이 변명을 드리자면, 그것들은 처음에는 전웅님을 개인적으로 비난하려고 한 의도가 아니었는데, 전웅님의 글에 대한 반박을 하려다 제 멋에 겨워 그만 선을 넘어 버리고 말았습니다(아뿔싸!).

저 역시 그와 같은 실수를 빈번히 저지르기 때문에 충분히 이해할 수 있습
니다. 특히나 논의가 의도하는 방향으로 흘러가지 않거나 (이번의 경우에는
그 누구에게도 그렇지 않았지요), 너무 길어지는 경우 저 역시 쉽게 감정
섞인 글을 쓰는 경향이 있습니다 - 지금 보니 제 글에서도 그와 같은 분위
기가 풍기는군요. 다행히도 한 가지 주제의 논의가 수개월간 지속되는 일이
다반사인 comp 계열 뉴스 그룹에서 오랫동안 활동하다보니 많이 면역이 되
기는 했습니다만... 만약 면역되지 않았다면 제가 님보다 빨리 감정적이 되
었을지도 모릅니다. 원래는 상당히 참을성이 없는 사람이라서... ;-)

방준영 wrote:
그 점을 너그러이 이해해 주시면 감사하겠습니다. 더 굳이 변명을 하자면, 며칠전 "첫사랑 사수 궐기대회"란 영화를 보고 나서 부쩍 울분을 느끼는 경우가 많아진 것 같습니다(보신 분들은 무슨 얘긴지 잘 아실 겁니다). 8)

한 사람의 감정을 그토록 부정적으로 만들 수 있다니... 그 영화 매우 "쓰
레기 같은" (원래는 더 격한 표현을 써야 하는 걸로 압니다만) 영화라는 이
야기는 들었지만 제가 예상하는 것보다 더 심한가 봅니다.

방준영 wrote:
사실, 이 문제는 f(fmt, va_alist)가 왜 쓰면 안되는지 그점만 증명되면 쉽게 끝날 성질의 것입니다. 이전에 여러 개발자들이 "안된다"고 했으니 뭔가 이유는 있겠지요. 그렇지만 이상하게도 어디서도 그 이유는 아직 발견되지 않았고, 저는 그게 더욱 의구심이 일었던 것입니다. 표준이라고 해서 항상 허점이 없는 것은 아니므로, 더군다나 표준이 아니라 "공식적으로 옳은" varargs는 더욱 그런 문제를 가지고 있을 수 있겠지요(그래서 ANSI 표준에서는 varargs를 stdarg로 대체한 것이니까).

정확히 기억나지는 않습니다만, 제가 알고 있기론 X3J11 이 굳이
<varargs.h> 를 버리고 <stdarg.h> 를 도입한 이유에도 <varargs.h> 에 바
람직하지 않은 면이 있었기 때문입니다. 이와 관련된 글을 오래 전에 본 적
이 있는데 정확히 생각나지 않으니 다시 한번 찾아서 확인해봐야 겠습니다.

그리고 저는 C 언어에 대해서는 comp.std.c 나 comp.lang.c 에서 활동하는
몇몇 전문가 분들의 의견을 (때로는 구체적인 근거가 부족해도) 믿는 경향
이 있습니다. 저 역시 매우 회의적인 사람이기에 어떠한 주장에 근거가 부
족하다고 판단되면 계속 물고 늘어지는 편입니다 - 그래서 해당 그룹에서
"nit-picky guy" (나쁘게 이야기하면 "쪼잔한 놈" 정도가 되겠군요) 라는
별명을 얻기도 했습니다만. 하지만, 신기하게도 거의 매번 현실에서는 도저
히 일어나지 않을 것이라 생각했던 일이 실존하는 것을 확인하게 됩니다.
가장 흔한 예로,

    int func(char *, ...);
    /* 혹은 가변 인자를 쓰는 함수가 아니더라도 int func(); 로 선언되어 있는 경우 */

    func("first", "second", 0);

에서 (유사한 경우가 exec() 에도 적용되지요), 이 프로그램이 마지막 null
pointer 를 올바르게 func() 에 전달하지 못하는 구체적인 implementation
을 한번도 본 적이 없습니다. 그런데 이를 캐물어 가는 과정에서 알게 된
사실이지만 유사한 문맥에서 문제를 일으킬 수 있는 implementation 이 실
존하더군요 (비록 이름은 생소한 것들이지만). 그와 같은 경험이 수차례 반
복되다 보니, 경험 많은 (그곳에서 활동하는 분들 중 일부는 이곳에 계신
분들이 태어나기 전 혹은 태어날 쯤부터 컴퓨터라는 것을 사용하신 분들입
니다) 개발자들이 강조하는 이야기에는 분명한 근거가 존재한다는 믿음이
생긴 것 같습니다. 그리고 그 믿음을 따라 프로그래밍 했을 때 다소 귀찮을
수는 있지만 손해가 되는 경우는 한번도 없었다는 사실이 믿음을 더 확고히
하는 결과를 낳게 되었습니다.

하지만, 예전의 제가 그랬던 것처럼 이번에도 단순히 그들의 의견을 인용하
는데 그치지 말고 보다 일찍부터 논의를 확대해 그 구체적인 이유와 제가
알지 못하는 부분을 밝혀냈어야 한다고 생각합니다.

더구나 이 문제가 그야말로 관습과 de facto standard 와 관련된 문제이다
보니, 실제 권위있는 표준과 관련된 문제일 경우 근거가 매우 분명하거나
표준이 잘못되었다고 판단되는 경우 수정을 공식적으로 요청할 수 있지만,
근거와 권위가 모두 불분명한 것이 큰 문제 중 하나였다고 봅니다.

방준영 wrote:
지금 와서 옛날 스타일에 관해 왈가왈부 해서 무슨 소용이냐는 분도 계시지만, 오히려 저는 이것이 재미있고 흥미로운 문제라고 생각합니다. 레지스터 기반 인수 전달 방법과 스택 기반 인수 전달 방법, 컴파일러별 차이, 기타 등등 해서 C에 관해 많은 지식을 얻을 수 있을 것입니다.

예, 실용성이 없다고 해서 논의할 가치가 없는 것은 분명 아닙니다 - 이것
이 제가 이 논의를 뉴스그룹 쪽에서 반복하려고 하는 이유이기도 합니다.
오히려 실용성이 없다고 덮어둘 가능성이 큰 문제를 자세히 알아가는 과정
에서 말씀하신 것처럼 배울 것이 더 많다고 생각합니다.

방준영 wrote:
그럼 이만 저는 한발 물러서서 printf(fmt, va_alist)가 가진 문제점이 지적되는 것을 지켜보도록 하겠습니다. 다시 한번 전웅님께: 이번 일로 저에 대해 너무 나쁜 감정을 가지게 되셨을 것 같습니다. 그렇지만 직접 만나 보면 제가 그렇게 나쁜 사람은 아닙니다(오히려 그 반대라고 주장하고 싶으나 앞에 쓴글 때문에 그렇게 말할 용기는...). 그냥 제가 더위를 먹어서 소리 몇번 질렀다고 생각해 주세요. 8) 그럼... (꾸벅~)

저도 변명의 말씀을 한가지 드리자면, 저는 님이 생각하시는 것보다 프로그
래밍 경험이 많답니다 - 그야말로 책만 읽고 C 표준화 과정에 간섭하는 사
람은 아닙니다. ;-) 제가 관심있는 분야가 병렬 처리나 compiler
construction 이고 환경 독립적인 프로그래밍을 지향하다보니 (제가 C 표준
을 비롯한 국제 표준을 깊이 있게 공부하는 이유도 여기에 있습니다) 님처
럼 특정 환경의 깊숙한 부분까지 전문적으로 알지는 못합니다. 하지만, 수
치 해석용 클러스터링 머신을 위한 각종 프로그램을 주로 작성하는 틈틈이
장난감처럼 가지고 노는 portable C compiler (lex 나 yacc 을 쓰지않고 작
성한것이며, back-end 는 미완이지만 목적 코드로는 실행시 별도의
interpreter 를 필요로하는 U-code 등을 사용할 계획입니다) 나 상당한 이
식성을 자랑하는 (하지만, 동시에 상당히 좋지 않은 성능을 자랑하는 ;-)
portable standard C lib. 같은 것들은 여유 시간을 쪼개 몇달씩 걸려 작성
하고 수정하는 과정을 거친 것이랍니다 (그만큼 사랑스러운 것들이지요 :-)
이 놈들은 지금도 제가 표준에 대해 잘못 이해하고 있던 부분이 밝혀지면
수정하는 작업을 하고 있습니다 (현재 comp.lang.c 에서 진행 중에 있는
multibyte character 와 UCS 에 대한 논의가 한 가지 예입니다). 제가 비록
도덕 책에서나 읽을 법한 이야기를 자주 꺼내는 편이지만, 이는 이와 같은
제 관심사와 경험에서 비롯된 것임을 이해해 주시기 바랍니다.

제가 별로 사교적인 사람은 아니지만, 저 역시 언제 기회가 된다면 님과 술
이라도 한잔 하고 싶네요 (에... 성격이 드러워도 만취 상태에서 폭행하거
나 하지는 않으니 걱정 안하셔도 됩니다.. :-).

내용 없는 긴 글 읽어주셔서 감사드리며, 이만 줄이겠습니다.

--
Jun, Woong (woong at gmail.com)
http://www.woong.org

낙엽의 이미지

이번 토론의 끝은 서로 그 마무리를 잘 해주셔서 예전에 있었던 좀 보기 불쾌함으로 끝남이 아님을 너무나도 다행스럽게 생각합니다.
방준영님과 전웅님 두분 모습 보기 좋습니다.
어느정도 소신있는 개발자라면 누구나 다 자신이 알고있는 지식에 고집을 가지고 있는것으로 알고있습니다. 그것이 그다지 나쁘게 생각되지도 않구요.
다만 그 지식을 나눔에 있어 경계선이 가끔 흐트러지는 경우를 많이 보게되는데 이번 경우는 여러모로 배우게 되는군요.
이런저런 자료를 찾아보면서 저도 많은 부분을 다시 배우게 되었습니다.
정말 세상에는 고수가 많군요. :-)

이런 토론문화가 다른 게시판에도 제발 번져야 할텐데요..

군가산점 문제라든지 문희준의 극성팬들의 그런 억지스러운 쌍방간의 더러운 입심말고 말이죠.. ㅋㅋㅋ

lsj0713의 이미지

만약에 f(fmt, va_alist)가 문제를 일으키는 구현체라던가 많은 사람들이 f(va_alist)를 추천했던 이유를 찾아내신다면 han.comp.lang.c 뉴스그룹이나 여기 쓰레드에 꼭 좀 글을 올려 주시길 부탁드리는 바입니다. 무척 궁금해서 살수가 없군요. (전 궁금한거 있으면 잠도 못잡니다^^;;;)

그리고, hclc와 kldp에서의 활동에 대해서 이 기회를 빌어 무한한 감사의 뜻을 전하는 바입니다. 덕분에 정말 많은 것을 배웠습니다.

fairycat의 이미지

C언어 펀더멘탈 잘 읽고 있습니다. 실은 레퍼런스로만 사용합니다. ^^
제가 초보라 넘 어려워여..ㅠㅠ

가이: 리여.. 확실히 너는 네지와는 다르다
록리: 위로라면 집어치세요..
가이: 위로같은게 아니다 ! 너는 네지와는 다르게 천재도 아니고 재능도 없다 하지만 너는 노력의 천재다..

- 나루토 <키시모토마사시>

전웅의 이미지

lsj0713 님께:

lsj0713 wrote:
만약에 f(fmt, va_alist)가 문제를 일으키는 구현체라던가 많은 사람들이 f(va_alist)를 추천했던 이유를 찾아내신다면 han.comp.lang.c 뉴스그룹이나 여기 쓰레드에 꼭 좀 글을 올려 주시길 부탁드리는 바입니다. 무척 궁금해서 살수가 없군요. (전 궁금한거 있으면 잠도 못잡니다^^;;;)

예, 약속 드리겠습니다. 얼마전 c.l.c 에 단도직입적인 질문을 포스팅했으
나 걱정했던대로 OT 라 그런지 (아니면 주말이라 그랬는지 :-) 아무런 답을
얻지 못하고 있습니다. 현재 메일을 통해 이런 저런 이야기를 나누면서 여
유 시간이 날 때마다 (<varargs.h> 와 다르게 정의된) <stdarg.h> 의 역사
를 중심으로 여러가지 이야기를 찾아보고 있습니다. 결정적인 답을 얻거나
(답은 얻지 못하더라도) 어느 정도 이야기가 정리되면 이곳이나 뉴스그룹을
통해 포스팅하겠습니다.

lsj0713 wrote:
그리고, hclc와 kldp에서의 활동에 대해서 이 기회를 빌어 무한한 감사의 뜻을 전하는 바입니다. 덕분에 정말 많은 것을 배웠습니다.

제가 특별히 도움드린 것이 있는 것도 아닌데... *^^*
저 역시 감사드립니다.

그리고 w12ard 님께:

w12ard wrote:
C언어 펀더멘탈 잘 읽고 있습니다.

감사합니다. ^^ "잘 팔린다" 는 소리보다는 독자 한분 한분의 작은 격려와
칭찬이 정말 큰 힘이 됩니다.

그럼...

--
Jun, Woong (woong at gmail.com)
http://www.woong.org

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.