The Principle and Application of Variable Parameters in C Language

Source: WeChat Official Account "Programming Learning Base"

The second article in 2021, C language variable parameters

Overview

There is no function overloading in the C language, and it becomes more troublesome to solve the problem of an indefinite number of function parameters;

Even if C++ is used, if the number of parameters cannot be determined, it is difficult to use function overloading. In this case, some people use pointer parameters to solve the problem

Var_list variable parameter introduction

VA_LIST is a set of macros to solve the problem of variable parameters in C language, the prototype:

typedef char* va_list;

It's actually a char* type variable

In addition var_list, we also need several macros to implement variable parameters

va_start 、 va_arg 、 va_end

#define _INTSIZEOF(n)   ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )//第一个可选参数地址
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )//下一个参数地址
#define va_end(ap)    ( ap = (va_list)0 )                  // 将指针置为无效

Simple use of variable parameters

#include <stdio.h>
#include <stdarg.h>
int AveInt(int, ...);
void main()
{
    
    
    printf("%d\t", AveInt(2, 2, 3));
    printf("%d\t", AveInt(4, 2, 4, 6, 8));
    return;
}

int AveInt(int v, ...)
{
    
    
    int ReturnValue = 0;
    int i = v;
    va_list ap;
    va_start(ap, v);
    while (i > 0)
    {
    
    
        ReturnValue += va_arg(ap, int);
        i--;
    }
    va_end(ap);
    return ReturnValue /= v;
}

Ah this...

Variable parameter principle

In the process, the stack address is allocated from high to low. When executing a function, the parameter list is pushed into the stack, pushed into the high address part of the stack, and then pushed into the stack function's return address, and then pushed into the stack function's execution code , In this stacking process, the stack address continues to decrease,

The hacker modifies the function return address in the stack and executes his own code to achieve the purpose of executing the code segment he inserted .

The distribution of functions in the stack is: address from high to low, followed by: function parameter list, function return address, function execution code segment.

Say so much, go directly to the code demo...

#include <stdio.h>
#include <stdarg.h>
int AveInt(int, ...);
void main()
{
    
    
    printf("AveInt(2, 2, 4): %d\n", AveInt(2, 2, 4));
    return;
}

int AveInt(int argc, ...)
{
    
    
    int ReturnValue = 0;
    int next = 0;
    va_list arg_ptr;

    va_start(arg_ptr, argc);
    printf("&argc = %p\n", &argc);            //打印参数i在堆栈中的地址
    printf("arg_ptr = %p\n", arg_ptr);  //打印va_start之后arg_ptr地址,比参数i的地址高sizeof(int)个字节
    /*  这时arg_ptr指向下一个参数的地址 */

    next = *((int*)arg_ptr);
    ReturnValue += next;

    next = va_arg(arg_ptr, int);
    printf("arg_ptr = %p\n", arg_ptr);  //打印va_arg后arg_ptr的地址,比调用va_arg前高sizeof(int)个字节

    next = *((int*)arg_ptr);
    ReturnValue += next;
    /*  这时arg_ptr指向下一个参数的地址 */
    va_end(arg_ptr);
    return ReturnValue/argc;
}

Output:

&argc = 0088FDD4
arg_ptr = 0088FDD8
arg_ptr = 0088FDDC
AveInt(2, 2, 4): 3

This is to simplify the introduction, so the example

This is a bit inconvenient. You can only get two parameters. Change it with variable parameters.

#include <stdio.h>
#include <stdarg.h>
int Arg_ave(int argc, ...);
void main()
{
    
    
    printf("Arg_ave(2, 2, 4): %d\n", Arg_ave(2, 2, 4));
    return;
}
int Arg_ave(int argc, ...)
{
    
    
    int value = 0;
    int ReturnValue = 0;

    va_list arg_ptr;
    va_start(arg_ptr, argc);
    for (int i = 0; i < argc; i++)
    {
    
    
        value = va_arg(arg_ptr, int);
        printf("value[%d]=%d\n", i + 1, value);
        ReturnValue += value;
    }
    return ReturnValue/argc;
}

Output

value[1]=2
value[2]=4
Arg_ave(2, 2, 4): 3

When you understand, you will say this? It's as simple as that, just specify that the first parameter is the total number of the following parameters, this is not a casual game

Don't worry, the wonderful thing is coming, the application of variable parameters

Variable parameter application: realize log printing

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
/*定义一个回调函数指针*/
typedef void (*libvlcFormattedLogCallback)(void* data, int level, const void* ctx, const char* message);
enum libvlc_log_level {
    
     
    LIBVLC_DEBUG = 0,       //调试
    LIBVLC_NOTICE = 2,      //普通
    LIBVLC_WARNING = 3,     //警告
    LIBVLC_ERROR = 4 }      //错误
;
/*定义一个回调函数结构体*/
typedef struct CallbackData {
    
    
    void* managedData;
    libvlcFormattedLogCallback managedCallback;
    int minLogLevel;        //log 级别
} CallbackData;

/*构造回调函数结构体*/
void* makeCallbackData(libvlcFormattedLogCallback callback, void* data, int minLevel)
{
    
    
    CallbackData* result = (CallbackData *)malloc(sizeof(CallbackData));
    result->managedCallback = callback;
    result->managedData = data;
    result->minLogLevel = minLevel;
    return result;
}

/*回调函数*/
void formattedLogCallback(void* data, int level, const void* ctx, const char* message)
{
    
    
    printf("level:%d", level);
    if (level == LIBVLC_ERROR)
    {
    
    
        printf("LIBVLC_ERROR:%s", message);
        return;
    }
    if (level >= LIBVLC_WARNING) {
    
    
        printf("LIBVLC_WARNING:%s", message);
        return;
    }
    if (level >= LIBVLC_NOTICE)
    {
    
    
        printf("LIBVLC_ERROR:%s", message);
        return;
    }
    if (level >= LIBVLC_DEBUG) {
    
    
        printf("LIBVLC_WARNING:%s", message);
        return;
    }
    
    
}

/*和石化log信息并执行回调函数*/
void InteropCallback(void* data, int level, const void* ctx, const char* fmt, va_list args)
{
    
    
    CallbackData* callbackData = (CallbackData*)data;
    if (level >= callbackData->minLogLevel)
    {
    
    
        va_list argsCopy;
        int length = 0;

        va_copy(argsCopy, args);
        length = vsnprintf(NULL, 0, fmt, argsCopy);
        va_end(argsCopy);

        char* str = malloc(length + 1);
        if (str != NULL)
        {
    
    
            va_copy(argsCopy, args);
            vsprintf(str, fmt, argsCopy);
            va_end(argsCopy);
        }
        else
        {
    
    
            // Failed to allocate log message, drop it.
            return;
        }
        callbackData->managedCallback(callbackData->managedData, level, ctx, str);
        free(str);
    }
}
void sendLog(void* data, int level, const void* ctx, const char* fmt, ...)
{
    
    
    va_list va;
    va_start(va, fmt);
    InteropCallback(data, level, ctx, fmt, va);
    va_end(va);
}
int main(int argc, char** argv)
{
    
    
    /*注册一个回调函数结构体,level等级为LIBVLC_WARNING 只要发送的log等级大于等于LIBVLC_WARNING次啊会触发回调函数*/
    void* callbackData = makeCallbackData(formattedLogCallback, "context", LIBVLC_WARNING);
    /*发送四个等级的消息*/
    sendLog(callbackData, LIBVLC_DEBUG, NULL, "This should not be displayed : %s\n","debug");
    sendLog(callbackData, LIBVLC_NOTICE, NULL, "This should not be displayed : %s\n", "notick");
    sendLog(callbackData, LIBVLC_WARNING, NULL, "This message level is : %s\n", "warning");
    sendLog(callbackData, LIBVLC_ERROR, NULL, "Hello, %s ! You should see %ld message here : %s\n", "World", 1, "warning message");

    free(callbackData);
    return 0;
}

Output

level:3LIBVLC_WARNING:This message level is : warning
level:4LIBVLC_ERROR:Hello, World ! You should see 1 message here : warning message

The subtlety of this usage example is to register a specified levelcallback functionmakeCallbackData(formattedLogCallback, "context", LIBVLC_WARNING);

Then when sending log, according to leveljudge whether to execute the callback function, format the loginformation by the way

Guess you like

Origin blog.csdn.net/qq_44519484/article/details/112211361