广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C语言中可变参数的原理是什么
  • 466
分享到

C语言中可变参数的原理是什么

2023-06-15 19:06:10 466人浏览 薄情痞子
摘要

C语言中可变参数的原理是什么,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。var_list可变参数介绍VA_LIST 是在C语言中解决变参问题的一组宏,原型:typedef&n

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)个字节           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;          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;     }           }   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) {          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);

看完上述内容,你们掌握C语言中可变参数的原理是什么的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注编程网其他教程频道,感谢各位的阅读!

--结束END--

本文标题: C语言中可变参数的原理是什么

本文链接: https://www.lsjlt.com/news/281377.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • C语言中可变参数的原理是什么
    C语言中可变参数的原理是什么,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。var_list可变参数介绍VA_LIST 是在C语言中解决变参问题的一组宏,原型:typedef&n...
    99+
    2023-06-15
  • C语言中的可变参数怎么使用
    今天小编给大家分享一下C语言中的可变参数怎么使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。一、什么是可变参数我们在C语言...
    99+
    2023-06-08
  • C语言可变参数使用与内存管理的方法是什么
    这篇“C语言可变参数使用与内存管理的方法是什么”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“C语言可变参数使用与内存管理的方...
    99+
    2023-07-04
  • C语言中可变参数如何使用
    C语言中可变参数如何使用,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。一、什么是可变参数我们在C语言编程中有时会遇到一些参数个数可变的函数,例如printf()...
    99+
    2023-06-17
  • C语言可变长的参数列表详解
    C语言可变长的参数列表 C语言可创建接收参数个数不确定的函数。如常用的标准库函数printf就是一个接收参数个数可变的函数。函数printf至少要接收一个字符串作为它的第一个实参。但...
    99+
    2022-11-12
  • C语言的可变参数函数实现详解
    目录1、简介2、简单的使用方式总结1、简介 今天看到一个有趣的东西C语言的可变参数函数 众所周知,C语言的函数不能重载,那么你printf和scanf是怎么可以输入多个参数的 例如查...
    99+
    2022-11-12
  • C语言可变参数与函数参数的内存对齐详解
    目录什么是可变参数?使用可变参数函数参数的内存对齐总结什么是可变参数? 有时,您可能会碰到这样的情况,您希望函数带有可变数量的参数,而不是预定义数量的参数。 C 语言为这种情况提供了...
    99+
    2022-11-13
  • C语言可变参数与内存管理超详细讲解
    目录概述动态分配内存重新调整内存的大小和释放内存概述 有时,您可能会碰到这样的情况,您希望函数带有可变数量的参数,而不是预定义数量的参数。C 语言为这种情况提供了一个解决方案,它允许...
    99+
    2023-01-02
    C语言可变参数 C语言内存管理
  • C语言函数的调用原理是什么
    C语言函数的调用原理是通过栈来实现的。当一个函数被调用时,系统会为该函数分配一块内存空间,这块空间被称为栈帧。栈帧包含了函数的参数、...
    99+
    2023-09-04
    C语言
  • C语言中变参函数传参的实现示例
    目录背景引入问题分析指针大小参数位置排布解决问题额外的测试总结参考资料背景引入 近期在看一本书,叫做《嵌入式C语言自我修养》,写的内容对我帮助很大,是一本好书。在第6章,GNU C编...
    99+
    2022-11-12
  • java中可变参数是什么
    这期内容当中小编将会给大家带来有关java中可变参数是什么,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。python是什么意思Python是一种跨平台的、具有解释性、编译性、互动性和面向对象的脚本语言,其...
    99+
    2023-06-14
  • C语言生成随机数的原理是什么
    本篇内容介绍了“C语言生成随机数的原理是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、引言C语言中生成随机数是一项非常重要的功能,因...
    99+
    2023-07-05
  • C语言可变参数列表的用法与深度剖析
    目录前言一、可变参数列表是什么?二、怎么用可变参数列表三、对于宏的深度剖析隐式类型转换对两个函数的重新认知总结前言 可变参数列表,使用起来像是数组,学习过函数栈帧的话可以发现实际上他...
    99+
    2022-11-13
  • C语言fft算法的原理是什么
    FFT(快速傅里叶变换)是一种计算离散傅里叶变换(DFT)的高效算法。傅里叶变换是一种将时域信号转换为频域信号的数学技术,它可以将信...
    99+
    2023-09-21
    C语言 fft算法
  • c语言mppt算法的原理是什么
    MPPT(最大功率点跟踪)算法的原理是通过调整光伏阵列的工作点,使得光伏阵列输出的功率达到最大。传统的光伏阵列输出功率与光照强度呈非...
    99+
    2023-09-21
    c语言 mppt算法
  • C语言scanf的工作原理是什么
    这篇文章主要介绍“C语言scanf的工作原理是什么”,在日常操作中,相信很多人在C语言scanf的工作原理是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C语言scanf的工作原理是什么”的疑惑有所帮助!...
    99+
    2023-06-22
  • C++可变参数模板的展开方式是什么
    这篇文章主要讲解了“C++可变参数模板的展开方式是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C++可变参数模板的展开方式是什么”吧!可变参数模板(variadic templates...
    99+
    2023-06-29
  • c语言解释器的实现原理是什么
    C语言解释器的实现原理是将C语言源代码转换为可执行的机器代码并执行。下面是C语言解释器的基本实现原理:1. 词法分析:将源代码分解为...
    99+
    2023-08-08
    c语言
  • c语言中变量的命名规则是什么
    C语言中变量的命名规则如下:1. 变量名只能由字母、数字和下划线组成。2. 变量名必须以字母或下划线开头,不能以数字开头。3. 变量...
    99+
    2023-08-18
    c语言
  • C语言函数调用底层实现原理是什么
    本文小编为大家详细介绍“C语言函数调用底层实现原理是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“C语言函数调用底层实现原理是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。前言C语言程序执行实质上的函数...
    99+
    2023-07-05
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作