简体   繁体   中英

How does va_arg actually work in variadic functions?

I read about variadic functions and how the datatype and macros work, i thought i understood how it worked but i found something is slightly different, heres is what I've been experimenting with, my understanding of how the different macros and data type work:

typedef char * va_list;
#define va_start( ap, v ) ap = (char *)&v + sizeof( v )
#define va_arg( ap, t ) ( (t *) ( ap += sizeof( t ) )[-1]
#define va_end( ap ) ap = NULL

so this is just a pointer that moves 1 byte at the time and moves the numbers of bytes the t data type uses, so i tried to replicate the behavior without using this macros or <stdarg.h> at all, just to better understand how it works, the code that the function that uses the header is this:

int _print_ints(int n, ...)
{
    va_list listArgs;

    va_start( listArgs, n );

    for(int i = 0; i < n; i++)
    {
        printf("%3d ", va_arg( listArgs, int ) );
    }

    va_end( listArgs );
}

it works as intended, the code i made is this:

int print_ints(int n, ...)
{
    char *arg = (char *)&n;

    for(int i = 0; i < n; i++)
    {
        int val = *( (int *) (arg += sizeof( int ) * 2 ) );
        printf("%3d ", val);
    }

    arg = NULL;
}

this one work as intended as well, but, you see i had to move twice the suppossed size I had to moved in the first place, instead of moving 4 bytes (int size in my system) per variable, i have to move 8 bytes which is a long long size, constant variables in the code are int so i dont really understand why in the stack in order to move from one variable to another i have to move twice its size, heres is full code:

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

int _print_ints(int n, ...)
{
    va_list listArgs;

    va_start( listArgs, n );

    for(int i = 0; i < n; i++)
    {
        printf("%3d ", va_arg( listArgs, int ) );
    }

    va_end( listArgs );
}

int print_ints(int n, ...)
{
    char *arg = (char *)&n;

    for(int i = 0; i < n; i++)
    {
        int val = *( (int *) (arg += sizeof( int ) * 2 ) );
        printf("%3d ", val);
    }

    arg = NULL;
}

int main( void )
{
    _print_ints(5, 1, 2, 3, 4, 5);
    printf("\n");
    print_ints(5, 6, 7, 8, 9, 10);

    return EXIT_SUCCESS;
}

it outputs:

1  2  3  4  5
6  7  8  9 10

to summarize, why do i have to move sizeof( int ) * 2 instead of sizeof( int ) ?

may be the book i got the information from is outdated

First of all, I'm not sure if you have obtained those macros right from your system's stdarg.h header or you just think that the compiler is using those, because it might very well not be. Compile your source with -E -P to only apply the preprocessor and take a look at what _print_ints() looks like after preprocessing. On modern compilers, va_* macros are simply compiler built-ins (you will see something like __builtin_va_arg(...) in the output) and therefore have no C equivalent, only the compiler knows how they are implemneted and the code needed for those.

Now, if those you show are really the macros being used on your system, then your re-implementations of va_start() and va_arg() are simply wrong:

char *arg = (char *)&n;             // wrong
char *arg = (char *)&n + sizeof(n); // correct

int val = *( (int *) (arg += sizeof( int ) * 2 ) ); // wrong
int val = *((int *)(arg += sizeof(int)) - 1);       // correct

However, since you are doing += sizeof(int) * 2 and strangely getting the right results, this could mean that your compiler is indeed implementing va_start() and va_arg() in a different way than what you think, most likely aligning your 4-byte integers to 8-byte boundaries on the stack (might make sense if you are on a 64-bit system).

In any case, you're most probably getting the right results out of pure luck since you are working with wild pointers and therefore what you're doing can easily end up being undefined behavior if you are not careful.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM