1. void event_warnx(const char *fmt, ...) EV_CHECK_FMT(1,2);
  2.  
  3. #define EV_CHECK_FMT(a,b) __attribute__((format(printf, a, b)))
  4. void
  5. event_debugx_(const char *fmt, ...)
  6. {
  7. va_list ap;
  8.  
  9. va_start(ap, fmt);
  10. event_logv_(EVENT_LOG_DEBUG, NULL, fmt, ap);
  11. va_end(ap);
  12. }
  13.  
  14. void
  15. event_logv_(int severity, const char *errstr, const char *fmt, va_list ap)
  16. {
  17. char buf[1024];
  18. size_t len;
  19.  
  20. if (severity == EVENT_LOG_DEBUG && !event_debug_get_logging_mask_())
  21. return;
  22.  
  23. if (fmt != NULL)
  24. evutil_vsnprintf(buf, sizeof(buf), fmt, ap);
  25. else
  26. buf[0] = '\0';
  27.  
  28. if (errstr) {
  29. len = strlen(buf);
  30. if (len < sizeof(buf) - 3) {
  31. evutil_snprintf(buf + len, sizeof(buf) - len, ": %s", errstr);
  32. }
  33. }
  34.  
  35. event_log(severity, buf);
  36. }
  1. event_log(int severity, const char *msg)
  2. {
  3. if (log_fn)
  4. log_fn(severity, msg);
  5. else {
  6. const char *severity_str;
  7. switch (severity) {
  8. case EVENT_LOG_DEBUG:
  9. severity_str = "debug";
  10. break;
  11. case EVENT_LOG_MSG:
  12. severity_str = "msg";
  13. break;
  14. case EVENT_LOG_WARN:
  15. severity_str = "warn";
  16. break;
  17. case EVENT_LOG_ERR:
  18. severity_str = "err";
  19. break;
  20. default:
  21. severity_str = "???";
  22. break;
  23. }
  24. (void)fprintf(stderr, "[%s] %s\n", severity_str, msg);
  25. }
  26. }

event_debugx("Connecting lime to coconut");的使用

eg:

1、__attribute__ format属性可以给被声明的函数加上类似printf或者scanf的特征,它可以使编译器检查函数声明和函数实际调用参数之间的格式化字符串是否匹配。format属性告诉编译器,按照printf, scanf等标准C函数参数格式规则对该函数的参数进行检查。在封装调试信息的接口时非常的有用

2、va_list va_start va_end 作用原理:

在函数中使用可变参数,要包含头文件<stdarg.h>。它包含以下几个宏:va_start;va_arg;va_end;va_copy。

    • VA_ARG宏,获取可变参数的当前参数,返回指定类型并将指针指向下一参数(t参数描述了当前参数的类型):
      #define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
    • VA_START宏,获取可变参数列表的第一个参数的地址(ap是类型为va_list的指针,v是可变参数最左边的参数):
      #define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
    • VSPRINT函数,vsprintf是sprintf的一个变形:
      #功能:送格式化输出到串中,返回值:正常情况下返回生成字串的长度(除去\0),错误情况返回负值
    • VA_END宏,清空va_list可变参数列表:
      #define va_end(ap) ( ap = (va_list)0 )

C语言中函数参数的内存布局。首先,函数参数是存储在栈中的,函数参数从右往左依次压入;

void test(char *para1,char *param2,char *param3, char *param4) { va_list list; ...... return; }

在linux中,栈由高地址往低地址生长,调用test函数时,其参数入栈情况如下

  1. void PrintString(char*, ...);
  2. int main(void)
  3. {
  4. PrintString("Msg","This","is","a","test!","");
  5. return 0;
  6. }
  7.  
  8. //ANSI标准形式的声明方式,括号内的省略号表示可选参数
  9. //利用va_start相关宏,获取参数列表中的所有参数,并打印
  10. void PrintString (char* prev_param, ...)
  11. {
  12. //定义保存函数参数的结构
  13. va_list pArgs;
  14. int argNo = 0;
  15. char* para; //获取参数用的临时变量
  16.  
  17. //对pArgs进行初始化,让它指向可变参数表里面的第一个参数
  18. //prev_param 是在变参表前面紧挨着的一个变量,即"..."之前的那个参数
  19. va_start(pArgs, prev_param);
  20.  
  21. while (1)
  22. {
  23. //获取参数
  24. para = va_arg(pArgs, char*);
  25. if ( strcmp( para, "") == 0 )
  26. break;
  27. printf("Parameter #%d is: %s\n", argNo, para);
  28. argNo++;
  29. }
  30.  
  31. //获取所有的参数之后,将pArgs指针关掉
  32. va_end(pArgs);
  33. }

_stdcall与_cdecl区别:

实际上声明一个函数时应该告诉函数的调用方式,如下:

typedef void (WINAPI *LPFUN)(void);

typedef void (__stdcall *LPFUN)(void);

typedef void    (FAR PASCAL *LPFUN)    (void);
_cdecl是C和C++程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码,
所以产生的可执行文件大小会比调用_stdcall函数的大。函数采用从右到左的压栈方式;

  cdecl调用约定的参数压栈顺序是和stdcall是一样的,参数首先由右向左压入堆栈。所不同的是,函数本身不清理堆栈,调用者负责清理堆栈。由于这种变化,所以C调用约定允许函数的参数的个数是不固定的;在被调用函数 (Callee) 返回后,由调用方 (Caller) 调整堆栈。

1. 调用方的函数调用

2. 被调用函数的执行

3. 被调用函数的结果返回

4. 调用方清除调整堆栈

因为每个调用的地方都需要生成一段调整堆栈的代码,所以最后生成的文件较大。

函数的参数个数可变(就像 printf 函数一样),因为只有调用者才知道它传给被调用函数几个参数,才能在调用结束时适当地调整堆栈。

stdcall在被调用函数 (Callee) 返回前,由被调用函数 (Callee) 调整堆栈

1. 调用方的函数调用

2. 被调用函数的执行

3. 被调用函数清除调整堆栈

4. 被调用函数的结果返回

因为调整堆栈的代码只存在在一个地方(被调用函数的代码内),所以最后生成的文件较小。

注意:

2.Variadic Macros: ... and _ _VA_ARGS_ _

Some functions, such as printf(), accept a variable number of arguments. The stdvar.h header file,provides tools for creating user-defined functions with a variable number of arguments. And C99 does the same thing for macros.Although not used in the standard, the word variadic has come into currency to label this facility. (However, the process that has added stringizing and variadic to the C vocabulary has not yet led to labeling functions or macros with a fixed number of arguments as fixadic functions and normadic macros.)

The idea is that the final argument in an argument list for a macro definition can be ellipses (that is, three periods)(省略号). If so, the predefined macro _ _VA_ARGS_ _ can be used in the substitution part to indicate what will be substituted for the ellipses. For example, consider this definition:

#define PR(...) printf(_ _VA_ARGS_ _)

Suppose you later invoke the macro like this:

PR("Howdy");

PR("weight = %d, shipping = $%.2f\n", wt, sp);

For the first invocation, _ _VA_ARGS_ _ expands to one argument:

"Howdy"

For the second invocation, it expands to three arguments:

"weight = %d, shipping = $%.2f\n", wt, sp

Thus, the resulting code is this:

printf("Howdy");

printf("weight = %d, shipping = $%.2f\n", wt, sp);

  1. #define VLOG_FATAL(...) vlog_fatal(THIS_MODULE, __FILE__, __LINE__, __func__, __VA_ARGS__)
  2.  
  3. void
  4. vlog_fatal(const struct vlog_module *module, const char *file, int lineno,
  5. const char *func, const char *message, ...)
  6. {
  7. va_list args;
  8.  
  9. va_start(args, message);
  10. vlog_fatal_valist(module, file, lineno, func, message, args);
  11. va_end(args);
  12. }
  13. /* Writes 'message' to the log at the given 'level' and as coming from the
  14. * given 'module'.
  15. *
  16. * Guaranteed to preserve errno. */
  17. extern int get_telnet_level(void);
  18. void
  19. vlog_valist(const struct vlog_module *module, enum vlog_level level,
  20. const char *file, int lineno, const char *func,
  21. const char *message, va_list args)
  22. {
  23. bool log_to_console = module->levels[VLF_CONSOLE] >= level;
  24. bool log_to_syslog = module->levels[VLF_SYSLOG] >= level && syslog_fd >= 0;
  25. bool log_to_file = module->levels[VLF_FILE] >= level && log_fd >= 0;
  26. int xxx_log_level = get_telnet_level();
  27.  
  28. if (s_log_enable) {
  29. if (level <= VLL_WARN) {
  30. xxxx_alarm_log(func, lineno, message, args);
  31. }
  32. if (s_log_enable) {
  33. switch(level)
  34. {
  35. case VLL_OFF:
  36. case VLL_EMER:
  37. case VLL_ALARM:
  38. case VLL_ERR:
  39. case VLL_WARN:
  40. case VLL_INFO:
  41. case VLL_DBG:
  42. if (get_telnet_level()) {
  43. xxx_vlog_output(TO_TELNET, ET_DEBUG, 0, basename(file), lineno, func, 0, 0, message, args);
  44. }
  45. break;
  46. case VLL_KEYEVENT:
  47. xxx_vlog_output(TO_LOGS, ET_WARN, 0, basename(file), lineno, func, 0, 0, message, args);
  48. break;
  49. default:
  50. // unknown level, do nothing
  51. break;
  52. }
  53. }
  54. }
  55.  
  56. if ((log_to_console && !s_log_enable) || log_to_syslog || log_to_file) {
  57. int save_errno = errno;
  58. struct ds s;
  59.  
  60. vlog_init();
  61.  
  62. ds_init(&s);
  63. ds_reserve(&s, 1024);
  64. ++*msg_num_get();
  65.  
  66. ovs_rwlock_rdlock(&pattern_rwlock);
  67. if (log_to_console) {
  68. format_log_message(module, level,
  69. destinations[VLF_CONSOLE].pattern, message,
  70. args, &s);
  71. ds_put_char(&s, '\n');
  72. fprintf(stderr, "[%s %s:%d]", func, basename(file), lineno);
  73. fputs(ds_cstr(&s), stderr);
  74. }
  75.  
  76. if (log_to_syslog) {
  77. int syslog_level = syslog_levels[level];
  78. char *save_ptr = NULL;
  79. char *line;
  80. int facility;
  81.  
  82. format_log_message(module, level, destinations[VLF_SYSLOG].pattern,
  83. message, args, &s);
  84. for (line = strtok_r(s.string, "\n", &save_ptr); line;
  85. line = strtok_r(NULL, "\n", &save_ptr)) {
  86. atomic_read_explicit(&log_facility, &facility,
  87. memory_order_relaxed);
  88. syslogger->class->syslog(syslogger, syslog_level|facility, line);
  89. }
  90.  
  91. if (syslog_fd >= 0) {
  92. format_log_message(module, level,
  93. "<%B>1 %D{%Y-%m-%dT%H:%M:%S.###Z} "
  94. "%E %A %P %c - \xef\xbb\xbf%m",
  95. message, args, &s);
  96. send_to_syslog_fd(ds_cstr(&s), s.length);
  97. }
  98. }
  99.  
  100. if (log_to_file) {
  101. format_log_message(module, level, destinations[VLF_FILE].pattern,
  102. message, args, &s);
  103. ds_put_char(&s, '\n');
  104.  
  105. ovs_mutex_lock(&log_file_mutex);
  106. if (log_fd >= 0) {
  107. if (log_writer) {
  108. async_append_write(log_writer, s.string, s.length);
  109. if (level == VLL_EMER) {
  110. async_append_flush(log_writer);
  111. }
  112. } else {
  113. ignore(write(log_fd, s.string, s.length));
  114. }
  115. }
  116. ovs_mutex_unlock(&log_file_mutex);
  117. }
  118. ovs_rwlock_unlock(&pattern_rwlock);
  119.  
  120. ds_destroy(&s);
  121. errno = save_errno;
  122. }
  123. }

重要:

1) va_start和va_end须配对使用;
2) va_copy和va_end须配对使用;

##__VA_ARGS__ 宏前面加上##的作用在于,当可变参数的个数为0时,这里的##起到把前面多余的","去掉的作用,否则会编译出错

一般这个用在调试信息上多一点

例如:

  1. #define test_print1(fmt,...) printf(fmt,__VA_ARGS__)
  2.  
  3. test_print2("iiiiiii\n") 编译失败打印,因为扩展出来只有一个参数,至少要两个及以上参数
  4.  
  5. 如果是#define test_print2(fmt,...) printf(fmt,##__VA_ARGS__)

可变参数以及stdcall的更多相关文章

  1. C函数和宏中的可变参数

    一:调用惯例 函数的调用方和被调用方对函数如何调用应该有统一的理解,否则函数就无法正确调用.比如foo(int n, int m),调用方如果认为压栈顺序是m,n,而foo认为压栈顺序是n, m,那么 ...

  2. C可变参数的函数

    我们实现一个简单的printf函数(可变参数) #include <stdio.h> #include <stdarg.h> void myprintf(const char ...

  3. c#编程基础之函数可变参数

    可变参数:int sum (params int[] values)int sum (string name,params int[] values) 注意:params参数必须是形参表中的最后一个参 ...

  4. C语言的可变参数在Linux(Ubuntu)与Windows下注意点

    基本上C语言的可变参数原理在不同平台和不同编译器下基本类似(通过函数入栈,从右向左,从高位到低位地址),不过部分实现会有所不同:在使用中需要注意的是: va_list 为char 类型指针,部分调用如 ...

  5. 可变参数列表与printf()函数的实现

    问题 当我们刚开始学习C语言的时候,就接触到printf()函数,可是当时"道行"不深或许不够细心留意,又或者我们理所当然地认为库函数规定这样就是这样,没有发现这个函数与普通的函数 ...

  6. C#与Java对比学习:数据类型、集合类、栈与队列、迭达、可变参数、枚举

    数据类型: C#:String与StringBuilder Java:String与StringBuffer 第一个不习惯是string的第一个字母必须大写了. 第二个不习惯是int得写成Intege ...

  7. params可变参数

    class Program { // params可变参数 //将实参列表中跟可变参数数组类型一致的元素都当做数组的元素去处理. //params可变参数必须是形参列表中的最后一个元素. static ...

  8. java高新技术-可变参数与OverLoad相关面试题分析

    可变参数 可变参数的特点: 只能出现在参数列表的最后: ...位于变量类型和变量名之间,前后有无空格都可以: 调用可变参数的方法时,编译器为该可变参数隐含创建一个数组,在方法中以数组的形式访问可变参数 ...

  9. C和指针 第七章 可变参数

    可变参数列表是通过stdarg.h内的宏来实现的: 类型 va_list 三个宏: va_start va_arg va_end 我们可以声明一个va_list变量,与这三个宏配合使用. 可变参数必须 ...

随机推荐

  1. CSS语法规范与代码风格

    CSS语法规范与代码风格 1. 语法规范 CSS规则又两个主要的部分构成:选择器+一条或多条声明. 选择器:用于指定CSS样式的HTML标签,花括号内的是设置的具体样式 属性与属性值以键值对的形式出现 ...

  2. async-await和Promise的关系

    关于异步处理,ES5的回调使我们陷入地狱,ES6的Promise使我们脱离魔障,终于.ES7的async-await带我们走向光明.今天就来学习一下 async-await. 经常会看到有了 asyn ...

  3. 【最短路】HDU 1688 Sightseeing

    题目大意 给出一个有向图(可能存在重边),求从\(S\)到\(F\)最短路的条数,如果次短路的长度仅比最短路的长度多1,那么再加上次短路的条数. 输入格式 第一行是数据组数\(T\). 对于魅族数据, ...

  4. rabbitmq 交换机模式 -主题模式 topic

    建立一个交换机 tpc 并且绑定了各自的路由到 Q1 Q2 <?php require_once "./vendor/autoload.php"; use PhpAmqpLi ...

  5. PHP转Go系列:数组与切片 转

    数组的定义# 用过PHP的同学应该很清楚,无论多么复杂的数据格式都可以用数组来表达,什么类型的数据都可以往里塞,它是工作必备的一部分,使用很简单,易用程度简直变态. Copy $array = [1, ...

  6. History和Screen的对象属性

    History 对象是 window 对象的一部分,可通过 window.history 属性对其进行访问. 属性 说明 length 返回历史列表中的网址数 History 对象方法 方法 说明 b ...

  7. Ubuntu20.4安装

    官网下载镜像 https://releases.ubuntu.com/20.04/ubuntu-20.04-live-server-amd64.iso 挂载开装 选语言 选键盘 网络设置DHCP到地址 ...

  8. poj1655 Balancing Act (dp? dfs?)

    Balancing Act Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 14247   Accepted: 6026 De ...

  9. 基于SSM框架的JavaWeb通用权限管理系统

    - - ->关注博主公众号[C you again],获取更多IT资源(IT技术文章,毕业设计.课程设计系统源码,经典游戏源码,HTML网页模板,PPT.简历模板,!!还可以投稿赚钱!!,点击查 ...

  10. Python&&Pip

    Pip简易使用 使用pip list命令就可以发现自己电脑里所安装库的名字.如图展示的出来的有package.Version.Location三列,package是下载的python库名,Versio ...