可变参数函数

背景:

  在我们开发C/C++项目中,经常会使用到系统提供的可变参数函数,例如printf,scanf等。根据需求我们也可能使用到可变参数函数,虽然真正使用的机会不是很多,但是我还是比较好奇如何去实现可变参数函数。这篇博文主要是我记录学习可变参数的一个demo,具体功能是模仿scanf,目前只实现简单的功能,对可变参数函数的一种简单理解。其中使用到这篇博文使用宏实现日志信息以及异常处理。除此之外,我还搜索了几遍博文感觉讲的不错,大家也可以仔细读一读

Demo:

  1 /** WARNING: this code is fresh and potentially isn't correct yet. */
  2 #include <stdio.h>
  3 #include <stdlib.h>
  4 #include <stdarg.h>
  5 #include "dbg.h"
  6 
  7 #define MAX_DATA 100
  8 
  9 int read_string(char **out_string, int max_buffer)
 10 {
 11     *out_string = calloc(1, max_buffer + 1);
 12 
 13     check_mem(*out_string);
 14     char *result = fgets(*out_string, max_buffer, stdin);
 15     check(result != NULL, "Input error.");
 16     return 0;
 17 error:
 18     if(*out_string) free(*out_string);
 19     *out_string = NULL;
 20     return -1;
 21 }
 22 
 23 int read_int(int *out_int)
 24 {
 25     char *input = NULL;
 26     int rc = read_string(&input, MAX_DATA);
 27     check(rc == 0, "Failed to read number.");
 28 
 29     *out_int = atoi(input);
 30     free(input);
 31     return 0;
 32 error:
 33     if(input) free(input);
 34     return -1;
 35 }
 36 
 37 int read_scan(const char *fmt, ...)
 38 {
 39     int i = 0;
 40     int rc = 0;
 41     int *out_int = NULL;
 42     char *out_char = NULL;
 43     char *out_string = NULL;
 44     int max_buffer = 0;
 45     va_list argp;
 46     va_start(argp, fmt);
 47 
 48     for (i = 0; fmt[i] != '\0'; i++) {
 49         if (fmt[i] == '%') {
 50             i++;
 51             switch(fmt[i])
 52             {
 53                 case '\0':
 54                     sentinel("Invalid format, you ended with %%.");
 55                     break;
 56                 case 'd':
 57                     out_int = va_arg(argp, int *);
 58                     rc = read_int(out_int);
 59                     check(rc == 0, "Failed to read int.");
 60                     break;
 61                 case 'c':
 62                     out_char = va_arg(argp, char *);
 63                     *out_char = fgetc(stdin);
 64                     break;
 65                 case 's':
 66                     max_buffer = va_arg(argp, int);
 67                     out_string = va_arg(argp, char **);
 68                     rc = read_string(out_string, max_buffer);
 69                     check(rc == 0, "Failed to read string.");
 70                     break;
 71                 default:
 72                     sentinel("Invalid format.");
 73             }
 74         } else {
 75             fgetc(stdin);
 76         }
 77         check(!feof(stdin) && ! ferror(stdin), "Input error.");
 78     }
 79     va_end(argp);
 80     return 0;
 81 error:
 82     va_end(argp);
 83     return -1;
 84 }
 85 
 86 int main(int argc, char* argv[])
 87 {
 88     char *first_name = NULL;
 89     char initial = ' ';
 90     char *last_name = NULL;
 91     int age = 0;
 92     int age1 = 0;
 93     int age2 = 0;
 94     printf("What's your first name?");
 95     int rc = read_scan("%s", MAX_DATA, &first_name);
 96     check(rc == 0, "Failed first name.");
 97 
 98     printf("What's your initial?");
 99     rc = read_scan("%c\n", &initial);
100     check(rc == 0, "Failed initial.");
101 
102     printf("What's your last name?");
103     rc = read_scan("%s", MAX_DATA, &last_name);
104     check(rc == 0, "Failed last name.");
105 
106     printf("How old are you?");
107     rc = read_scan("%d", &age);
108     check(rc == 0, "Failed age.");
109 
110     rc = read_scan("%d%s", &age1, MAX_DATA, &first_name);
111     check(rc == 0, "Failed more age.");
112 
113     printf("-------- RESULT -------\n");
114     printf("First name: %s", first_name);
115     printf("Initial: %c\n", initial);
116     printf("Last name : %s", last_name);
117     printf("Age: %d\n", age1);
118 
119     free(first_name);
120     free(last_name);
121     return 0;
122 error:
123     return -1;
124 }

  这个demo主要是简单模拟scanf函数功能。

看看结果:

 1 zhaoscmatoMacBook-Pro:c zhaosc$ ./ex25
 2 What's your first name?LI
 3 What's your initial?T
 4 What's your last name?Shuchao
 5 How old are you?26
 6 23
 7 Zhaosc
 8 -------- RESULT -------
 9 First name: Zhaosc
10 Initial: T
11 Last name : Shuchao
12 Age: 23

简单说说:

  首先必须引入头文件stdarg.h,这样才能使用结构体va_list,和宏va_start,va_arg,va_start,va_end,具体每个宏在“读一读”中有详细的说明。这个demo只支持%s,%c和%d,他们可以单独使用也可以组合使用。其中主要处理在read_scan函数中。

原文地址:https://www.cnblogs.com/zhaosc/p/3079088.html