如何替换va_list中的值? [英] How to replace values in va_list?

查看:121
本文介绍了如何替换va_list中的值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想做一些关于va_list的练习.这是我的代码.

I want to do some exercise about va_list. This is my code.

int myscanf( char* fmt, ... ) {
  va_list ap;
  va_start ( ap, fmt );
  vfscanf ( stdin, fmt, ap );
  va_end ( ap );
}

int main() {
  int a, b;
  myscanf( "%d %d", &a, &b );
}

如上所示,我已经编写了scanf(),并且可以正常工作.

As I shown above, I have written a scanf() and it is work.

现在,我想重定向myscanf()中参数的值.

Now I want to redirect the value of arguments in myscanf().

例如,我可以将fmt重定向到myscanf()中分配的空间

For example, I can redirect fmt to the space which is allocated in myscanf()

int myscanf( char* fmt, ... ) {
  char newFmt[10] = "%d %d";
  va_list ap;
  va_start ( ap, fmt );
  vfscanf ( stdin, newFmt, ap );
  va_end ( ap );
}

但是,当我尝试更改其他参数的值时,我感到困惑.

However, I feel confused when I try to change the value of others arguments.

我可以通过va_arg()获取这些变量参数,但由于va_arg()是宏,所以我无法修改它们.

I can fetch these variable argument by va_arg(), but I can't modify them because va_arg() is a macro.

int myscanf( char* fmt, ... ) {
  va_list ap;
  va_start ( ap, fmt );

  int* arg1 = (int)va_arg(ap, int*); // get the value of &a in main()
  int newA; // I want to read a value by scanf() and store it to &newA
  // ??? = &newA // <- how to do?

  vfscanf ( stdin, fmt, ap );
  va_end ( ap );
}

有什么建议吗?

-----------编辑-----------

-----------edit-----------

感谢您的答复,

但是应该澄清一些事情.

But something should be clarified.

在这种情况下,值"是地址".因此,我的目的是更改目标地址,以便vfscanf()可以将值读写到另一个地址空间.

The "value" in this case is "address". Therefore, my purpose is changing the target address so that the vfscanf() will read and write the value to the another address space.

例如,

int gA, gB, gC, gD;

int myscanf( char* fmt, ... ) {
  va_list ap;
  va_start ( ap, fmt );

  // do something for making the following vfscanf() to write value into gC and gD directly
  vfscanf ( stdin, fmt, ap );

  // don't assign *gA to *gC and *gB to *gD after performing vfscanf()
  va_end ( ap );
}

int main() {
  myscanf( "%d %d", &gA, &gB );
}

当我将fmt更改为newFmt时,我们想直接更改va_list中的值(在这种情况下为地址).

As I change fmt to newFmt, we want to change the value (in this case is address) in va_list directly.

解析问题得以解决,因为当我从格式字符串中解析%..."时,我可以动态分配空间.如果上述问题得到解决,则这些地址的空格将反复替换输入.

And the parsing problem is solved because that I can allocate a space dynamically while I parse a "%..." from format string. These addresses of spaces will replace inputs repeatedly if the question above is solved.

推荐答案

可变函数

scanf的参数将始终是指针,而不是示例中的值.获取scanf参数的正确方法是int *arg1 = va_arg(ap, int*);-无需强制转换.

Variadic Functions

The arguments to scanf will always be pointers, not values as in your example. The correct way of getting an argument of scanf would be int *arg1 = va_arg(ap, int*); - and you don't need to cast.

如果要操纵scanf的行为方式,则必须首先了解可变函数有效(您可以通过阅读任何va_*函数系列的手册来获得它).在大多数体系结构中,变量ap是指向函数的堆栈框架的指针.在这种情况下,它指向fmt之后的下一个变量.

If you want to manipulate the way scanf behaves, you have to know first how variadic functions work (you can get it by reading the manual of any of the va_* family of functions). The variable ap in most architectures is a pointer to the function's stack frame. It points to the next variable after fmt in this case.


在您的示例中,在scanf的情况下,它将指向指针列表(因为scanf all 参数必须是指针).因此,您应该像这样将其放入指针:

In the case of scanf in your example, it will point to a list of pointers (because all arguments to scanf must be pointers). So you should put that into your pointers like this:

int *a = va_arg(ap, int*);

/* Then you can modify it like this: */
*a = 666;

这有一些问题.

处理完参数后,必须将fmtap传递给vfscanf,然后将解析fmt并期望n元素(格式字符串中的元素数量).问题是ap现在只会给我们n - x个元素(x是您在自己的函数中弹出"的元素数).一个小例子:

When you finish manipulating the arguments, you must pass fmt and ap to vfscanf, which will then parse fmt and expect n elements (the amount of elements in the format string). The problem is that ap now will only give us n - x elements (x being the number of elements you "poped" in your own function). A little example:

myscanf("%d %d", &a, &b);
/* n = 2 */

...

int *a = va_arg(ap, int *);
/* x = 1 */

...
vfscanf(stdin, fmt, ap);
/* n = 2 cause fmt is still the same, however
 * x = 1, so the number of elements "popable" from the stack is only
 * n - x = 2 - 1 = 1.
 */

在这个简单的示例中,您已经可以看到问题. vfscanf会为找到的每个以字符串形式的元素调用va_arg ,该字符串为n,但是只有n - x是可弹出的.这意味着未定义的行为-vfscanf将会在不应编写的位置进行编写,并且很可能会使您的程序崩溃.

In this simple example you can already see the problem. vfscanf will call va_arg for each element it finds in the format string, which is n, but only n - x are popable. This means undefined behavior - vfscanf will be writing somewhere it shouldn't and most probably will crash your program.


为克服这一点,我建议使用 va_copy 进行一些工作. va_copy的签名是:

To overcome that, I propose a little work with va_copy. The signature of va_copy is:

void va_copy(va_list dest, va_list src);

关于它的一些知识(从手册中了解):

And something to know about it (from the manual):

va_copy()的每次调用必须与同一功能中 va_end()的相应调用相匹配.某些不提供 va_copy()的系统改为使用 __ va_copy ,因为这是提案草案中使用的名称.

Each invocation of va_copy() must be matched by a corresponding invocation of va_end() in the same function. Some systems that do not supply va_copy() have __va_copy instead, since that was the name used in the draft proposal.

解决方案:

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

int myscanf(char *fmt, ...)
{
    va_list ap, hack;

    /* start our reference point as usual */
    va_start(ap, fmt);

    /* make a copy of it */
    va_copy(hack, ap);

    /* get the addresses for the variables we wanna hack */
    int *a = va_arg(hack, int*);
    int *b = va_arg(hack, int*);

    /* pass vfscanf the _original_ ap reference */
    vfscanf(stdin, fmt, ap);

    va_end(ap);
    va_end(hack);

    /* hack the elements */
    *a = 666;
    *b = 999;
}

int main(void)
{
    int a, b;
    printf("Type two values: ");
    myscanf("%d %d", &a, &b);

    printf("Values: %d %d\n", a, b);
    return 0;
}


您应该注意几件事.首先,如果在调用vfscanf之前放置 hacking 元素,则设置的值将丢失,因为vfscanf会覆盖这些位置.

There are a couple of things you should note. First, if you put the hacking of the elements before calling vfscanf, the values you set will be lost, because vfscanf will overwrite those locations.

接下来,您还应该注意,这是一个非常特定的用例.我事先知道,我将传递两个整数作为参数,因此在设计myscanf时要牢记这一点.但这意味着您需要解析通行来找出哪些参数属于哪种类型-如果不这样做,您将再次输入未定义的行为.编写这种解析器非常简单,应该没问题.

Next, you should also note that this is a very specific use case. I knew beforehand that I was going to pass two integers as arguments, so I designed myscanf with that in mind. But this means you need a parsing pass to find out which arguments are of which type - if you don't do it, you'll enter undefined behavior again. Writing that kind of parser is very straight-forward and shouldn't be a problem.


在您进行澄清编辑后,我只能在vfscanf()周围提出一个包装函数,因为您不能直接操纵va_list变量.您不能直接写入堆栈(理论上来说,您不能,但是如果您进行了一些内联汇编,则可以,但这将是一个丑陋的操作,而且非常不可移植).

After what you said in your clarification edit, I can only propose a little wrapper function around vfscanf(), because you can't directly manipulate va_list variables. You can't write directly to the stack (in theory, you can't, but if you did some inline-assembly you could, but that's gonna be an ugly hack and very non-portable).

之所以会变得极为丑陋且不可移植,是因为内联程序集必须考虑该体系结构如何处理参数传递.单独编写内联汇编已经非常丑陋...请查看有关内联汇编的官方GCC手册.

The reason it's gonna be extremely ugly and non-portable is that the inline assembly will have to take into account how the architecture treats argument passing. Writing inline-assembly by itself is already very ugly... Check out this for the official GCC manual on it's inline assembly.

回到您的问题:

这个答案可以解释很多,所以我在这里不再赘述.答案的最后结论是不,您不这样做".但是,您可以__做的是一个包装器.

That answer explains a whole lot, so I won't say it here again. The final conclusion of the answer is **no, you don't do it". What you _can do however, is a wrapper. Like this:

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

int a, b, c, d;

void ms_wrapper(char *newfmt, ...)
{
    va_list ap;
    va_start(ap, newfmt);

    vfscanf(stdin, newfmt, ap);

    va_end(ap);
}

int myscanf(char *fmt, ...)
{
    /* manipulate fmt.... */
    char *newfmt = "%d %d";

    /* come up with a way of building the argument list */

    /* call the wrapper */
    ms_wrapper(newfmt, &c, &d);
}

int main(void)
{
    a = 111;
    b = 222;
    c = 000;
    d = 000;

    printf("Values a b: %d %d\n", a, b);
    printf("Values c d: %d %d\n\n", c, c);

    printf("Type two values: ");
    myscanf("%d %d", &a, &b);

    printf("\nValues a b: %d %d\n", a, b);
    printf("Values c d: %d %d\n", c, d);

    return 0;
}

请注意,您只能在编译时为可变参数函数构建参数列表.您不能动态更改参数列表.换句话说,您必须对要处理的每种情况进行硬编码.如果用户输入的内容不同,您的程序将表现得很奇怪,很可能崩溃.

Beware that you can only build argument lists for variadic functions in your compile-time. You can't have a dynamically changing list of parameters. In other words, you'll have to hard-code each case you'd ever wanna handle. If the user enters something different, your program will behave very oddly and most probably crash.

这篇关于如何替换va_list中的值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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