将单个参数列表传递给两个 v*printf() 调用 [英] Passing a single argument list to two v*printf() calls

查看:25
本文介绍了将单个参数列表传递给两个 v*printf() 调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑以下测试用例:

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

void test(char **outa, char **outb, const char* fstra, const char* fstrb, ...) {
    va_list ap;

    va_start(ap, fstrb);
    vasprintf(&outa, fstra, ap);
    vasprintf(&outb, fstrb, ap);
    va_end(ap);
}

int main(void) {
    char *a, *b;
    test(&a, &b, "%s", " %s
", "foo", "bar");
    /* ... */
}

这里的意图是 test() 函数接受两个格式字符串和一个参数列表.第一个格式字符串应该吃"尽可能多的参数,其余的应该用于第二个格式字符串.

The intent here is that the test() function takes two format strings and a list of parameters for both of them. The first format string is supposed to 'eat' as many arguments it needs, and the remaining ones are supposed to be used for the second format string.

所以,这里的预期结果是 foo &bar 这就是我用 glibc 得到的.但是 AFAICS 运行键盘的机器(猜猜是 *BSD),给出 foo &foo 我猜它在参数列表上使用了 va_copy().

So, the expected result here would be foo & bar and that's what I get with glibc. But AFAICS the machine running codepad (guess some *BSD it is), gives foo & foo and my guess is that it uses va_copy() on the argument list.

我想我在这里遇到了未定义(和丑陋)的行为;所以问题是:有没有办法实现双格式字符串 printf() 而无需从头开始重新实现它?是否有一种很好的方法可以使用 autoconf 来检查该行为而不使用 AC_RUN_IFELSE()?

I guess I'm hitting an undefined (and ugly) behavior here; so the question is: is there a way to achieve double-format-string printf() without reimplementing it from scratch? And is there a nice way to check that behavior using autoconf without using AC_RUN_IFELSE()?

我想一些快速扫描格式字符串以获取要使用的参数数量的方法也可以在这里工作(+va_copy()).

I guess some quick method of scanning format-string for the number of arguments to be consumed could work here as well (+va_copy()).

推荐答案

正如其他答案已经指出的那样,将 ap 传递给 av*() 函数会使 ap 留在一个未定状态.因此,解决方案是不依赖于这种状态.我建议另一种解决方法.

As the other answer already states, passing ap to a v*() function leaves ap in an undetermined state. So, the solution is to not depend on this state. I suggest an alternative workaround.

首先,正常初始化ap.然后使用 vsnprintf(NULL, 0, fstra, ap) 确定第一个格式化字符串的长度.连接格式字符串,重新初始化 ap,并使用第一个格式化字符串的预定长度拆分输出.

First, initialize ap as normal. Then determine the length of the first formatted string using vsnprintf(NULL, 0, fstra, ap). Concatenate the format strings, reinitialize ap, and split the output using the predetermined length of the first formatted string.

它应该如下所示:

void test(const char* fstra, const char* fstrb, ...) {
  char *format, *buf;
  char *a, *b;
  int a_len, buf_len;
  va_list ap;

  va_start(ap, fstrb);
  a_len = vsnprintf(NULL, 0, fstra, ap);
  va_end(ap);

  asprintf(&format, "%s%s", fstra, fstrb);

  va_start(ap, fstrb);
  buf_len = vasprintf(&buf, format, ap);
  va_end(ap);
  free(format);

  a = malloc(a_len + 1);
  memcpy(a, buf, a_len);
  a[a_len] = '';

  b = malloc(buf_len - a_len + 1);
  memcpy(b, buf + a_len, buf_len - a_len);
  b[buf_len - a_len] = '';
  free(buf);
}

正如在另一个答案中所讨论的那样,这种方法不会分离位置printf-样式占位符(%1$s.我再说一遍,%1$s.").所以接口的文档应该清楚地说明两个格式字符串共享相同的位置占位符命名空间——如果格式字符串之一使用位置占位符,那么两者都必须.

As also discussed in the other answer, this approach does not separate positional printf-style placeholders ("%1$s. I repeat, %1$s."). So the documentation for the interface should clearly state that both format strings share the same positional placeholder namespace—and that if one of the format strings uses positional placeholders then both must.

这篇关于将单个参数列表传递给两个 v*printf() 调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆