가변 목록 포인터에 대해 질문이 있습니다.
[variable_argument_print_numbers.c]의 line 13의 내용
printf("%d ", num);
printf("%d %p", num, ap);로 바꾸어서 실행해 보았습니다.
이 경우,
ap의 값은 int num = va_arg(ap, int); 가 실행된 이후임에도 불구하고
변하지 않았습니다.
컴파일(gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0) 후 실행 내용:
/*
(10 0x7ffc5958c9c0)
(10 0x7ffc5958c9c0) (20 0x7ffc5958c9c0)
(10 0x7ffc5958c9c0) (20 0x7ffc5958c9c0) (30 0x7ffc5958c9c0)
(10 0x7ffc5958c9c0) (20 0x7ffc5958c9c0) (30 0x7ffc5958c9c0) (40 0x7ffc5958c9c0)
*/
교재 내용에서
"int num = va_arg(ap, int);를 실행하면 현재 ap에서 4바이트(int 크기)만큼 역참조하여 값을 가져온 뒤 ap를 4바이트만큼 순방향으로 이동시킵니다." 라고 하셨는데,
ap의 값은 변하지 않았기에 어떤 형태로 순방향 이동된건지 잘 모르겠습니다.
감사합니다.
Visual Studio 기준으로는 ap의 위치가 변화하면서 동작합니다.
10 0076FBA820 20 0076FBAC30 30 0076FBB040 40 0076FBB4
ap_list의 구현은 구현체마다 다르며, 표준에 명시되어 있지 않습니다.
책은 Visual Studio 기준으로 설명되어 있고, GCC, Clang에서는 구현 방식이 다른 것으로 보입니다.
The va_arg()
function retrieves a value
of the given var_type from the location
given by arg_ptr, and increases arg_ptr to
point to the next argument in the list. The va_arg()
function
can retrieve arguments from the list any number of times within the
function. The var_type argument must be
one of int, long, decimal, double, struct, union, or pointer, or a
typedef of one of these types.
보통의 표준 함수 설명서에도 va_arg 함수는 값을 가져오고, arg_ptr의 값을 증가시켜서 리스트에서 다음 인수를 가리키게 합니다.
C 언어 표준 함수에 명시되어 있지 않은 부분은 컴파일러 구현체의 동작 방식에 따라 다릅니다. 해당 컴파일러 매뉴얼이나 소스 코드를 찾아봐야 할 것으로 보입니다.
va_arg는 매크로로 정의되어 있고, 기본 코드는 다음과 같습니다.
/* * Increment ap to the next argument in the list while returing a * pointer to what ap pointed to first, which is of type t. * * We cast to void* and then to t* because this avoids a warning about * increasing the alignment requirement. */ #define va_arg(ap, t) \ (((ap) = (ap) + __va_argsiz(t)), \ *((t*) (void*) ((ap) - __va_argsiz(t))))ap를 정확하게 다음 원소만큼의 크기로 이동시키고 있음을 알 수 있습니다.
ap의 값이 왜 고정으로 나오는지는 해당 컴파일러 매뉴얼이나 소스 코드를 찾아봐야 할 것 같습니다.
답변 감사합니다.
표준 헤더가 아니면 컴파일러마다 다를 수도 있다라고 받아들이면 될까요?
그리고 한가지 더 질문이 있습니다.
visual studio 2019를 사용해서
variable_argument_print_numbers.c에서 원래 질문과 마찬가지로,
printf("%d %p ", num, ap);
로 변경해서 입력해보았습니다.
그 결과
(vprintf로 바꾼다고해도 에러가 발생합니다.)
이것도 표준 헤더파일이 아니기에, 비주얼 스튜디오가 버전이 업데이트되면서 구현 방식이 바뀌었다 정도로 이해하면 될까요?
%p 서식 지정자의 동작은 구현체마다 다릅니다.
undefined behavior로 정의되어 있습니다. 간단히 UB라고 씁니다.
Visual Studio 2019에서 경고를 띄운다면 (void*)로 캐스팅해서 출력하면 됩니다. 정석 대로는 (void*)로 캐스팅해야 합니다. 캐스팅 없이 출력하는 것은 구현체에 따라 경고가 나올 수도 있고, 아닐 수도 있습니다.
Visual Studio 2017에서는 경고가 없고, 2019에서는 경고가 있는 것으로 보입니다. 내부 구현체 동작이 바뀌었을 겁니다.