Analysis of C Variable Argument Functions (va_start, va_end, va_list…)

PS: Please indicate the source when reprinting, all rights reserved.PS: This is just based on my own understanding,if it conflicts with your principles and ideas, please forgive me, do not criticize.

Environment Description

  None

Introduction

  A few years ago, out of interest, I researched the issue of variable arguments in C. At that time, I was too lazy to record any materials, which led to having to start over when I actually needed it. Alas, it’s so annoying~~~~~~~

C/C++ Variable Arguments

  First, the header file that defines the macros for C variable arguments is in stdarg.h. In my system, it is located here: /usr/lib/gcc/x86_64-linux-gnu/5/include/stdarg.h. I won’t paste the source code, but I will briefly analyze a few important macros.

//va_list  //This macro depends on different platforms and operating systems. 
    //Common definitions: (specific to the compiler, but they are generally similar once understood)
    typedef char *  __builtin_va_list; // (Note that it may be defined this way)
    (or)
    typedef void *  __builtin_va_list; // (Note that it may be defined this way)
    typedef __builtin_va_list __gnuc_va_list;  
    typedef __gnuc_va_list va_list;


//_INTSIZEOF(n) //This macro is defined on Windows to consider compatibility with some memory address alignment systems. n should occupy how many bytes, for example: the ARM instruction set is 4-byte aligned, and the Thumb instruction set is 24-byte aligned. In Linux, the GCC compiler internally defines a similar macro to represent the aligned byte size. Its usage is to determine each parameter in the variable arguments based on a specific parameter.
    #define  _INTSIZEOF(n) \
        ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
    //Generally, in an operating system, the number of bytes occupied by the int type is the current aligned byte size, usually 4 or 8. For a detailed analysis of this macro, the following analysis comes from the internet, substituting sizeof(n) = 4,8 will clarify:
    #define  _INTSIZEOF(n)  ((sizeof(n) + x) & ~(x))
    x = sizeof(int) - 1 = 3 = 0000000000000011(b)
    ~x = 1111111111111100(b)

//va_start(v,l) //This macro retrieves the address of the first parameter in the variable arguments.
    #define __builtin_va_start(v,l) \
            ( v = (va_list)&l + _INTSIZEOF(l) ) // (Note that it may be defined this way), the address of l plus the space occupied by l.
    #define va_start(v,l)   __builtin_va_start(v,l)
    
//va_arg(v,l)//This macro assigns the address of the next variable argument to v, while returning the value pointed to by the initial address of v. Note: At this point, v has already pointed to the address of the next parameter, but the value returned by the expression is the initial value of v. 《《You just need to understand the priority of parentheses to comprehend this.》》
    #define __builtin_va_arg(v,l) \
            ( *(l *)((v += _INTSIZEOF(l)) - _INTSIZEOF(l)) ) // (Note that it may be defined this way)
    #define va_arg(v,l) __builtin_va_arg(v,l)
        
//va_end(v)//This macro processes a pointer declared by va_list to prevent wild pointers. There is a beginning and an end.
    #define __builtin_va_end(v)\
        ( v = (va_list)0 )
    #define va_end(v)   __builtin_va_end(v)
Example (t.c)
#include <stdarg.h>
#include <stdio.h>

void ttt(char * fuk,...);

int main(int argc, char * argv[]){
    ttt("fuk",2333,"this is 23333333333333333");
    return 0;
}
void ttt(char * fuk,...){

    va_list arg;
    int a;
    char *b;
    va_start(arg,fuk);
    a = va_arg(arg,int);
    b = va_arg(arg,char *);
    va_end(arg);

    printf("%d,%s\n",a,b);
}

Compilation

gcc t.c
./a.out

Postscript

Final notes:

  1. 1. Variable argument functions must have at least one specified parameter.
  2. 2. The type of variable arguments must be known in some way (e.g., format strings, “%d,%s”, etc.), otherwise it is fixed.
  3. 3. The number of variable arguments must also be known in some way (e.g., format strings, “%d,%s”, etc.), otherwise it is fixed.
  4. 4. The function parameters in C are pushed onto the stack from right to left, and the stack address generally goes from high to low.

References

  • • None

If you would like to support, subscribe, bookmark, or throw bananas and coins, please follow my public account.

PS: Please respect original work, if you dislike it, do not criticize.PS: Please indicate the source when reprinting, all rights reserved.PS: If you have questions, please leave a message, and I will reply as soon as I see it.

Leave a Comment