变长参数按照格式拼接string (vsnprintf\vasprintf)

1.字符串拼接

std::string stringf(const char *fmt, ...)
{
	std::string string;
	va_list ap;

	va_start(ap, fmt);
	string = vstringf(fmt, ap);
	va_end(ap);

	return string;
}

std::string vstringf(const char *fmt, va_list ap)
{
	std::string string;
	char *str = NULL;

#if defined(_WIN32 )|| defined(__CYGWIN__)
	int sz = 64, rc;
	while (1) {
		va_list apc;
		va_copy(apc, ap);
		str = (char*)realloc(str, sz);
		rc = vsnprintf(str, sz, fmt, apc);
		va_end(apc);
		if (rc >= 0 && rc < sz)
			break;
		sz *= 2;
	}
#else
	if (vasprintf(&str, fmt, ap) < 0)
		str = NULL;
#endif

	if (str != NULL) {
		string = str;
		free(str);
	}

	return string;
}

2.include 头文件

#ifndef _VA_LIST
typedef __builtin_va_list va_list;
#define _VA_LIST
#endif
#define va_start(ap, param) __builtin_va_start(ap, param)
#define va_end(ap)          __builtin_va_end(ap)
#define va_arg(ap, type)    __builtin_va_arg(ap, type)

3.va_list使用例程 例程来源

void foo(char *fmt, ...)
{
    va_list ap;
    int d;
    char c, *s;

    va_start(ap, fmt);
    while(*fmt){
        switch(*fmt++){
            case 's':
                s = va_arg(ap, char *); 
                printf("string %s\n", s); 
                break;
            case 'd':
                d = va_arg(ap, int);
                printf("int %d\n", d); 
                break;
            case 'c':
                /* need a cast here since va_arg only
                   takes fully promoted types */
                c = (char) va_arg(ap, int);
                printf("char %c\n", c); 
                break;
            default :
                break;
        }
    }
    va_end(ap);
}

4、实现原理 来源

其实际就是利用函数参数入栈的顺序来计算参数地址,从而获知参数值。
函数参数是以数据结构:栈的形式存取,从右至左入栈。
  首先是参数的内存存放格式:参数存放在内存的堆栈段中,在执行函数的时候,从最后一个开始入栈。因此栈底高地址,栈顶低地址,举个例子如下:
void func(int x, float y, char z);
  那么,调用函数的时候,实参 char z 先进栈,然后是 float y,最后是 int x,因此在内存中变量的存放次序是 x->y->z,因此,从理论上说,我们只要探测到任意一个变量的地址,并且知道其他变量的类型,通过指针移位运算,则总可以顺藤摸瓜找到其他的输入变量。

va_list 是一个字符指针,可以理解为指向当前参数的一个指针,取参必须通过这个指针进行。

<Step 1> 在调用参数表之前,定义一个 va_list 类型的变量,(假设va_list 类型变量被定义为ap);
<Step 2> 然后应该对ap 进行初始化,让它指向可变参数表里面的第一个参数,这是通过 va_start 来实现的,第一个参数是 ap 本身,第二个参数是在变参表前面紧挨着的一个变量,即“…”之前的那个参数;
<Step 3> 然后是获取参数,调用va_arg,它的第一个参数是ap,第二个参数是要获取的参数的指定类型,然后返回这个指定类型的值,并且把 ap 的位置指向变参表的下一个变量位置;
<Step 4> 获取所有的参数之后,我们有必要将这个 ap 指针关掉,以免发生危险,方法是调用 va_end,他是输入的参数 ap 置为 NULL,应该养成获取完参数表之后关闭指针的习惯。说白了,就是让我们的程序具有健壮性。通常va_start和va_end是成对出现。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 C++ 中,可以使用 `+` 运算符对字符串进行拼接。如果两个字符串都是 `std::string` 类型的,可以直接使用 `+` 运算符进行拼接。例如: ```C++ #include <iostream> #include <string> int main() { std::string str1 = "Hello, "; std::string str2 = "world!"; std::string result = str1 + str2; // 字符串拼接 std::cout << result << std::endl; // 输出结果 return 0; } ``` 上面的代码中,我们定义了两个字符串 `str1` 和 `str2`,分别初始化为 `"Hello, "` 和 `"world!"`。然后我们使用 `+` 运算符将它们拼接起来,得到一个新的字符串 `result`,最后使用 `std::cout` 输出结果。 如果一个字符串是 `char*` 或 `const char*` 类型的,可以将它们转换成 `std::string` 类型,再使用 `+` 运算符进行拼接。例如: ```C++ #include <iostream> #include <string> int main() { const char* str1 = "Hello, "; const char* str2 = "world!"; std::string result = std::string(str1) + std::string(str2); // 字符串拼接 std::cout << result << std::endl; // 输出结果 return 0; } ``` 上面的代码中,我们定义了两个 `const char*` 类型的字符串 `str1` 和 `str2`,分别初始化为 `"Hello, "` 和 `"world!"`。然后我们将它们转换成 `std::string` 类型,使用 `+` 运算符将它们拼接起来,得到一个新的字符串 `result`,最后使用 `std::cout` 输出结果。需要注意的是,这里使用了 `std::string` 的构造函数将 `const char*` 类型的字符串转换成 `std::string` 类型。 除了 `+` 运算符,也可以使用 `append` 函数对字符串进行拼接。例如: ```C++ #include <iostream> #include <string> int main() { std::string str1 = "Hello, "; std::string str2 = "world!"; str1.append(str2); // 字符串拼接 std::cout << str1 << std::endl; // 输出结果 return 0; } ``` 上面的代码中,我们定义了两个字符串 `str1` 和 `str2`,分别初始化为 `"Hello, "` 和 `"world!"`。然后我们使用 `append` 函数将 `str2` 拼接到 `str1` 的后面,得到一个新的字符串 `str1`,最后使用 `std::cout` 输出结果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值