您现在的位置是:亿华云 > 人工智能
C语言可变参数的原理和应用
亿华云2025-10-09 12:49:23【人工智能】0人已围观
简介本文转载自微信公众号「编程学习基地 」,作者deroy 。转载本文请联系编程学习基地 公众号。概述C语言中没有函数重载,解决不定数目函数参数问题变得比较麻烦;即使采用C++,如果参数个数不能确定,也很
本文转载自微信公众号「编程学习基地 」,语言可原理用作者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("%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; }啊这..
可变参数原理
在进程中,堆栈地址是从高到低分配的.当执行一个函数的时候,将参数列表入栈,压入堆栈的高地址部分,然后入栈函数的返回地址,接着入栈函数的执行代码,这个入栈过程,堆栈地址不断递减,
「黑客就是源码库在堆栈中修改函数返回地址,执行自己的代码来达到执行自己插入的代码段的目的」.
函数在堆栈中的分布情况是:地址从高到低,依次是:函数参数列表,函数返回地址,函数执行代码段.
说这么多直接上代码演示吧..
#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; }输出:
&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): %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; }输出
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 : %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; }输出
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信息
很赞哦!(597)
上一篇: 4、注册门槛低
下一篇: 小白注册网站域名该怎么办?有什么步骤?
相关文章
- 顶级域名可以增加企业品牌的价值。随着经济的快速发展,域名已不再是企业在网络中的独立地位。顶级域名的服务范围、企业产品、综合形象体现等,对于企业单位来说,顶级域名的重要性不言而喻。
- 什么样的数字域名是精品数字域名?
- HarmonyOS流转之跨端迁移
- 企业怎么申请老域名?申请老域名的时候要注意什么?
- 以上的就是为大家介绍的关于域名的详解
- 面试必知的Spark SQL几种Join实现
- 开发必掌握!Json数据交互和ResTful开发
- MySQL DBA必备:MySQL 5.7升级8.0过程(全)
- 4、待所有域名查询结束后可在右侧点击导出结果,即可以excel的文件方式将查询到的结果导出。
- 单字母.company域名高价成交!你认识.company域名吗?