C语言可变参数的原理和应用

系统运维2025-11-05 07:04:172

本文转载自微信公众号「编程学习基地  」,语言可原理用作者deroy  。变参转载本文请联系编程学习基地  公众号。语言可原理用 

概述

C语言中没有函数重载,变参解决不定数目函数参数问题变得比较麻烦;

即使采用C++,如果参数个数不能确定,也很难采用函数重载.对这种情况,有些人采用指针参数来解决问题

var_list可变参数介绍

VA_LIST 是在C语言中解决变参问题的一组宏,原型:

typedef char* va_list; 

其实就是语言可原理用个char*类型变量

除了var_list ,我们还需要几个宏来实现可变参数

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

简单使用可变参数

#include <stdio.h> #include <stdarg.h> int AveInt(int, ...); void main() {     printf("%dt", AveInt(2, 2, 3));     printf("%dt", 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; } 

啊这..

可变参数原理

在进程中,堆栈地址是从高到低分配的.当执行一个函数的时候,将参数列表入栈,压入堆栈的高地址部分,然后入栈函数的返回地址,接着入栈函数的执行代码,这个入栈过程,堆栈地址不断递减,

「黑客就是亿华云在堆栈中修改函数返回地址,执行自己的代码来达到执行自己插入的代码段的目的」.

函数在堆栈中的分布情况是:地址从高到低,依次是:函数参数列表,函数返回地址,函数执行代码段.

说这么多直接上代码演示吧..

#include <stdio.h> #include <stdarg.h> int AveInt(int, ...); void main() {     printf("AveInt(2, 2, 4): %dn", 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 = %pn", &argc);            //打印参数i在堆栈中的地址     printf("arg_ptr = %pn", 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 = %pn", 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; } 

输出:

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

「这个是为了介绍简单化,所以举的语言可原理用例子」

这样有点不大方便只能获取两个参数的免费信息发布网,用可变参数改变一下

#include <stdio.h> #include <stdarg.h> int Arg_ave(int argc,变参 ...); void main() {     printf("Arg_ave(2, 2, 4): %dn", 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]=%dn", i + 1, value);         ReturnValue += value;     }     return ReturnValue/argc; } 

输出

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

当你理解之后你就会说就这?这么简单,指定第一个参数是语言可原理用后面参数的总数就可以了,这还不随随便玩

别着急,变参精彩的语言可原理用来了,「可变参数的变参应用」

可变参数应用:实现log打印

#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 : %sn","debug");     sendLog(callbackData, LIBVLC_NOTICE, NULL, "This should not be displayed : %sn", "notick");     sendLog(callbackData, LIBVLC_WARNING, NULL, "This message level is : %sn", "warning");     sendLog(callbackData, LIBVLC_ERROR, NULL, "Hello, %s ! You should see %ld message here : %sn", "World", 1, "warning message");     free(callbackData);     return 0; } 

输出                                                                                                                                                                                                                

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

这个使用示例精妙之处在于注册一个指定level的回调函数makeCallbackData(formattedLogCallback, "context", LIBVLC_WARNING);

然后在发送log的时候根据level判断是否执行回调函数,企商汇顺便格式化log信息

本文地址:http://www.bzve.cn/html/83a66299254.html
版权声明

本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。

全站热门

显示器错误导致的电脑故障(故障原因、解决方法及预防措施)

国外注册域名有哪些缺点?国外域名怎么转入国内?

程序员经典面试题,MySQL并发读写的时候,都是需要加锁的么?

手把手教你分析MySQL死锁问题

用电脑做系统XP教程光盘,轻松学习配置系统(自学成才,操作简单,教程详细易懂)

Python包管理工具之Poetry

Istio 可以代替 Spring Cloud 吗?

史上最全Oracle文件损坏处理办法 (附实验步骤)

友情链接

滇ICP备2023006006号-39