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

查看:61
本文介绍了将单个参数列表传递给两个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\n", "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.

它应该类似于以下内容:

It should look something like the following:

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] = '\0';

  b = malloc(buf_len - a_len + 1);
  memcpy(b, buf + a_len, buf_len - a_len);
  b[buf_len - a_len] = '\0';
  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天全站免登陆