El principio y la aplicación de parámetros variables en lenguaje C

Fuente: "Base de aprendizaje de programación" de la cuenta oficial de WeChat

El segundo artículo en 2021, parámetros de variables en lenguaje C

Visión general

No hay sobrecarga de funciones en el lenguaje C, y se vuelve más problemático resolver el problema de un número indefinido de parámetros de función;

Incluso si se usa C ++, si no se puede determinar el número de parámetros, es difícil usar la sobrecarga de funciones. En este caso, algunas personas usan parámetros de puntero para resolver el problema

Introducción al parámetro de variable var_list

VA_LIST es un conjunto de macros para resolver el problema de los parámetros variables en lenguaje C, el prototipo:

typedef char* va_list;

En realidad, es una variable de tipo char *

Además var_list, también necesitamos varias macros para implementar parámetros variables.

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 )                  // 将指针置为无效

Uso simple de parámetros variables

#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 esto ...

Principio de parámetro variable

En el proceso, la dirección de la pila se asigna de mayor a menor. Cuando se ejecuta una función, la lista de parámetros se inserta en la pila, se inserta en la parte de dirección alta de la pila y luego se inserta en la dirección de retorno de la función de pila, y luego empujado en el código de ejecución de la función de la pila, en este proceso de apilamiento, la dirección de la pila continúa disminuyendo,

El pirata informático modifica la dirección de retorno de la función en la pila y ejecuta su propio código para lograr el propósito de ejecutar el segmento de código que insertó .

La distribución de funciones en la pila es: dirección de mayor a menor, seguida de: lista de parámetros de función, dirección de retorno de función, segmento de código de ejecución de función.

Di tanto, ve directamente a la demostración del código ...

#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;
}

Producción:

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

Esto es para simplificar la introducción, por lo que el ejemplo

Esto es un poco inconveniente. Solo puede obtener dos parámetros. Utilice parámetros variables para cambiarlo.

#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;
}

Producción

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

Cuando entiendas, dirás esto? Es tan simple como eso, solo especifique que el primer parámetro es el número total de los siguientes parámetros, este no es un juego casual

No te preocupes, viene lo maravilloso, la aplicación de parámetros variables

Aplicación de parámetros variables: realizar la impresión de registros

#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;
}

Producción

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

La sutileza de este ejemplo de uso es registrar una levelfunción de devolución de llamada específicamakeCallbackData(formattedLogCallback, "context", LIBVLC_WARNING);

Luego, al enviar log, según leveljuzgar si ejecutar la función de devolución de llamada, formatee la loginformación por cierto

Supongo que te gusta

Origin blog.csdn.net/qq_44519484/article/details/112211361
Recomendado
Clasificación