[C/C++] variable parameters

In c/c++, functions support variable parameters, the most typical one is printf()the function , in order to support variable parameters, the stack order of function parameters is from right to left by default , that is, the last parameter is located at a high address, and the first parameters at low addresses.

1. Related macros

1. va_list type variable

The variable type is a macro definition, which is essentially a char*variable of pointer type, and this pointer points to the address of the next parameter .

typedef char* va_list;

2. va_start() macro

 void va_start(va_list ap, last_arg)

effect

Initialize the parameter list ap to determine the position of the first immutable parameter (that is, the address of the second variable parameter)

parameter

  • ap : list of parameters.
  • last_arg : Pass in the last parameter pushed onto the stack (that is, the first parameter).

3、va_arg() 宏

 type va_arg(va_list ap, type)

effect

Retrieves the next argument of type type in the function's argument list. It cannot tell if the retrieved argument was the last argument passed to the function.

parameter

  • ap : list of parameters.
  • type : the type of the next parameter

4. va_end() macro

 void va_end(va_list ap) 

effect

Before taking all arguments and returning from the function, va_end() must be called, thus ensuring proper restoration of the stack . If va_end() is not used correctly, the program may crash.

parameter

  • ap : list of parameters.

5. How to get the number/type of variable parameters

Because va_arg()the macro cannot know whether the next parameter obtained is the last one in the variable parameters, so if you want to get it, you can get it from the passed in parameters.

  • Agreed in advance (too rigid, almost become an ordinary function, not recommended)
  • For variable parameters that are not of the same type : you can set the first parameter to char*, and agree on the number and type of subsequent variables. For example my_printf(“cdfs”, 'a', 10, 9.5, "hello world");, cmeans that the second parameter is of char type, dthat the third parameter is of int type, fthat the fourth parameter is of float type, and sthat the fifth parameter is of char* type.
  • For variable parameters of the same type : one is to pass in the number of subsequent parameters in the first parameter. For example add(3, 5, 8, -2);, the first parameter 3 means that there are 3 variable parameters behind. The other is to pass in a cutoff element in the last parameter. For example combine("hello", "world", NULL);, when the element read by va_arg()the macro is NULL, it knows that this element is the last one, and there is no need to continue reading.

6. Examples

#include <stdio.h>
#include <stdarg.h>

int adds(char *a, ...)
{
    
    
    va_list args;

    va_start(args, a);

    printf("第 1 个参数 a 的值:%s\n", a);
    printf("第 1 个参数 a 的地址:%p\n", &a);
    printf("此时 args 的地址:%p\n", args);
    printf("args 所指向的内容:%d\n", *((int*)args));
    printf("=======================================\n");
    for (int i = 0; i < 2; i++)
    {
    
    
        int j = va_arg(args, int);
        printf("第 %d 个参数:%d\n", i + 2, j);
        printf("此时 args 的地址:%p\n", args);
        printf("args 所指向的内容:%d\n", *((int*)args));
        printf("=======================================\n");
    }

    va_end(args);
}

int main()
{
    
    
	adds("hello world", 25, 32);
}

Output result:

第 1 个参数 a 的值:hello world
第 1 个参数 a 的地址:0000005911D1F9E0
此时 args 的地址:0000005911D1F9E8
args 所指向的内容:25
=======================================
第 2 个参数:25
此时 args 的地址:0000005911D1F9F0
args 所指向的内容:32
=======================================
第 3 个参数:32
此时 args 的地址:0000005911D1F9F8
args 所指向的内容:298973088
=======================================

2. Existing variable parameter underlying functions

1. vprintf() function

It is the underlying function of printf().

/*
*描述:将可变参数列表的格式化数据打印到 stdout
*
*参数:
*  format  	包含格式字符串的 C 字符串,其格式字符串与 printf 中的格式相同。
*
*  arg  	标识使用 va_start 初始化的变量参数列表的值。
*
*返回值:
*   成功后,返回写入的字符总数。
*   如果发生写入错误,则会设置错误指示符(ferror)并返回负数。
*   如果在编写宽字符时发生多字节字符编码错误,则将 errno 设置为 EILSEQ,并返回负数;
*/
int vprintf(const char * format, va_list arg);

The vprintf() function can realize the encapsulation of the printf() function.

#include <stdio.h>
#include <stdarg.h>

void my_printf(char *format, ...)
{
    
    
    va_list args;
    va_start(args, format);
    vprintf(format, args);
    va_end(args);
}

2. vsprintf() function

is the int sprintf(char *str, const char *format, ...)underlying function.

/*
*描述:向一个字符串缓冲区打印格式化字符串
*
*参数:
*	str		字符串数组指针,该数组用于存储格式化后的 C 字符串。
*	format 	格式化模式参数。
*	arg		可变参数列表对象。
*	
*返回值:
*	如果成功,则返回写入的字符总数,不包括末尾追加的\0,否则返回一个负值。 
*/
int vsprintf(char *str, const char *format, va_list arg);
#include <stdio.h>
#include <stdarg.h>

char buffer[80];
int vspfunc(char *format, ...)
{
    
    
   va_list aptr;
   int ret;

   va_start(aptr, format);
   ret = vsprintf(buffer, format, aptr);
   va_end(aptr);

   return(ret);
}

3. vsnprintf() function

is the int snprintf(char *str, size_t size, const char *format, ...)underlying function.

/*
*描述:向一个字符串缓冲区打印格式化字符串
*
*参数:
*	str		用于缓存格式化字符串结果的字符数组。
*	n 		限定最多打印到 str 缓冲区的字符的个数为 n-1 个,因为 vsnprintf() 
*			还要在结果的末尾追加 '\0'。如果格式化字符串长度大于 n-1,则多出的部分被丢弃。
*			一般这里传递的值就是 str 缓冲区的长度。
*	format	格式化模式。
*   arg		可变参数列表对象。
*	
*返回值:
*	如果成功,则返回成功保存到缓冲区中的字符的个数,不包括末尾追加的\0,否则返回一个负值。 
*/
int vsnprintf(char *str, size_t n, const char *format, va_list arg);
#include <stdio.h>
#include <stdarg.h>

#define MAX_SIZE 	256

char buf[MAX_SIZE];

// 该函数也可以用于封装 Arduino 的 Serial.printf() 函数,只需将里面的 printf() 进行替换即可
void my_printf(const char *format, ... )
{
    
    
	va_list args;
	
	va_start(args, format);
	vsnprintf(buf, SBUF_SIZE, format, args);
	va_end(args);
  
	printf("%s", buf);
}

4、__VA_ARGS__

This macro can also realize the encapsulation of printf()functions .
Regarding the use of #, ## symbols in macros, you can read another article of mine: [C/C++] usage of define (advanced usage)

#include <stdio.h>

#define SHOWLIST(...) 		printf(#__VA_ARGS__)
#define PRINT(format, ...) 	printf(format, ##__VA_ARGS__)

int main(void)
{
    
    
	SHOWLIST(Hello, 520, 3.14\n);
	PRINT("num = %d\n", 520);
	PRINT("Hello World!\n");

	return 0;
}

Guess you like

Origin blog.csdn.net/weixin_48896613/article/details/129768027