当前位置:网站首页>Variable parameter principle of C language function: VA_ start、va_ Arg and VA_ end

Variable parameter principle of C language function: VA_ start、va_ Arg and VA_ end

2022-07-06 11:58:00 Weiyuan escort agency

Speaking of C Variable parameters of language functions , Our first thought may be printf、scanf、printk 了 . stay Linux-2.6.24.7 Kernel source code ,printk The function prototype is as follows :

asmlinkage int printk(const char *fmt, ...)

 
    asmlinkage Means passing parameters through the stack .gcc The compiler calls c There are two ways to pass parameters in language functions : One is through the stack , The other is through registers . Registers are used by default , If you want to call in your assembly process c Language functions , And want to pass parameters through the stack , You define c Add macros before functions asmlinkage.
     from printk The function prototype knows ,printk In addition to receiving a fixed parameter fmt Outside , The following parameters use ... Express . stay C/C++ In language ,... Indicates that a certain number of parameters can be received (0 or 0 Above parameters ). that printk How to support variable parameters ?
     Let's first look at some macros :va_list、va_start、va_arg And va_end(va It should mean variable), stay Linux-2.6.24.7 Kernel source code , Its definition ( Definitions in the kernel and C The definition of language library is similar ) as follows
 

/*
  * Use local definitions of C library macros and functions
  * NOTE: The function implementations may not be as efficient
  * as an inline or assembly code implementation provided by a
  * native C library.
  */


#ifndef va_arg

#ifndef _VALIST
#define _VALIST
typedef char *va_list;
#endif                /* _VALIST */

/*
 * Storage alignment properties
 */

#define _AUPBND (sizeof (acpi_native_int) - 1)
#define _ADNBND (sizeof (acpi_native_int) - 1)

/*
 * Variable argument list macro definitions
 */

#define _bnd(X, bnd) (((sizeof (X)) + (bnd)) & (~(bnd)))
#define va_arg(ap, T) (*(*)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))
#define va_end(ap) (void) 0
#define va_start(ap, A) (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))

#endif                /* va_arg */

1、va_list
    va_list Represents the variable parameter list type , It's actually a char The pointer
2、va_start
    va_start Used to get the first pointer of the variable parameter in the function parameter list ( Get the function variable parameter list )
  * Output parameters ap( The type is va_list):  Used to save the first pointer of variable parameters in the function parameter list ( namely , Variable parameter list )
  * Input parameters A:  Is the last fixed parameter in the function parameter list
3、va_arg
    va_arg Used to get the current ap Refers to the variable parameter and will ap The pointer moves to the next variable parameter
  * Input parameters ap( The type is va_list):  Variable parameter list , Point to the variable parameter currently being processed
  * Input parameters T:  The type of variable parameter being processed
  * Return value : The value of the current variable parameter
 
     stay C/C++ in , Default call method _cdecl The caller manages the parameter stack operation , And the order of stacking is from right to left , The stacking direction is from high address to low address . therefore , The first 1 One to the first n Parameters are placed on the address incrementing stack . therefore , The address of the last fixed parameter in the function parameter list plus the offset of the first variable parameter is the variable parameter list of the function (va_start The implementation of the ); The address of the current variable parameter plus the offset of the next variable parameter is the address of the next variable parameter (va_arg The implementation of the ). The offset mentioned here is not necessarily equal to the number of bytes occupied by the parameter , Instead, the number of bytes occupied by the parameter is expanded to the machine word length (acpi_native_int) Number of bytes after multiple ( Because the stack operation is for a machine word ), That's why _bnd Then the reason for the definition .
 
4、va_end
    va_end Used to end the processing of variable parameters . actually ,va_end Is defined as null . It is only for the realization of va_start pairing ( Implement code symmetry and " Code self annotation " function )
 
     The processing process of variable parameter list is generally :
1、 use va_list Define a variable parameter list
2、 use va_start Get the function variable parameter list
3、 use va_arg Loop through the variable parameters in the variable parameter list
4、 use va_end End the processing of variable parameter list
 
Here is an example :

#include <stdio.h>
#include <stdarg.h>   /*  Use va_list、va_start And other header files that must be included */
#include <string.h>

/* linux C No, itoa function , So write it yourself */
char *itoa(int i, char *str)
{
    int mod, div = fabs(i), index = 0;
    char *start, *end, temp;

    do
    {
        mod = div % 10;
        str[index++] = '0' + mod;
        div = div / 10;
    }while(div != 0);

    if (< 0)
        str[index++] = '-';

    str[index] = '\0';

    for (start = str, end = str + strlen(str) - 1;
        start < end; start++, end--)
    {
        temp    = *start;
        *start    = *end;
        *end    = temp;
    }
    
    return str;
}

void print(const char *fmt, ...)
{
    char str[100];
    unsigned int len, i, index;
    int iTemp;
    char *strTemp;
    va_list args;

    va_start(args, fmt);
    len = strlen(fmt);
    for (i=0, index=0; i<len; i++)
    {
        if (fmt[i] != '%')    /* Unformatted parameters */
        {
            str[index++] = fmt[i];
        }
        else                /* Format parameters */
        {
            switch(fmt[i+1])
            {
            case 'd':        /* integer */
            case 'D':
                iTemp = va_arg(args, int);
                strTemp = itoa(iTemp, str+index);
                index += strlen(strTemp);
                i++;
                break;
            case 's':        /* character string */
            case 'S':
                strTemp = va_arg(args, char*);
                strcpy(str + index, strTemp);
                index += strlen(strTemp);
                i++;
                break;
            default:
                str[index++] = fmt[i];
            }
        }
    }
    str[index] = '\0';
    va_end(args);

    printf(str);        
}

int main()
{
    print("Version: %d; Modifier: %s\n", -958, "lingd");
    return 0;
}

     Another practical example :printk The console can only be initialized in the kernel (console_init) Only after that can we use . therefore , stay Linux Before the kernel initializes the console , Only other functions can be used to print kernel messages . These functions are :

void printascii(const char *);
void printhex(unsigned long value, int nbytes);
void printhex2(unsigned char value);
void printhex4(unsigned short value);
void printhex8(unsigned long value);

    These functions are implemented by assembly , And operate directly from the lower level s3c2410 Serial port , And display information . So there is no need to wait console_init Then you can display the information .
    In order to use these functions , Requires kernel option support for features :make menuconfig
      Kernel hack ->
       [*]Kernel low-level debugging functions
       [*]Kernel low-level debugging messages via S3C UART
       (0)S3C UART to use for low-level debug

    however , These functions do not printk It's so powerful , Support for variable parameters , Support format string . In order to be in Linux Before the kernel initializes the console , Can use a similar printk Function of , We will printascii Function encapsulated into new function debug in :

extern void printascii(const char *);

static void debug(const char *fmt, ...)
{
    va_list va;                
    char buff[256];

    va_start(va, fmt);
    
    /* function vsprintf: Used to output formatted string to buffer
     * Output parameters buff: Buffer for saving results
     * Input parameters fmt:  Formatted string
     * Input parameters va:   Variable parameter list
     * Return value :       The number of characters actually output to the buffer
     */

    vsprintf(buff, fmt, va);
    va_end(va);

    printascii(buff);
}

原网站

版权声明
本文为[Weiyuan escort agency]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/187/202207060913225789.html