本文介绍了vsprintf 和 va_list 的平台不一致的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景:我目前正在尝试扩展"标准 C 格式以支持处理某个结构,类似于 Objective-C 如何扩展 C 格式以允许支持带有%@"序列的 NSString.

Background: I am currently trying to "extend" standard C formatting with support for handling a certain struct, similar to how Objective-C extends C formatting to allow support for NSString with the "%@" sequence.

我遇到的一个问题是 vsprintf 在 OS X 和 Linux 上的行为似乎不同(我已经用 Ubuntu 10.10 和 12.04 进行了测试).在 OS X 上,它的行为就像我认为的那样,在调用 vsprintf 之后,调用 va_arg 返回 ms 指针(就好像 vsprintf 函数调用 va_arg 来获取 5).然而,在 Linux 上,va_list 不会从 vsprintf 改变,并且调用 va_arg 返回 5.

The one problem I'm struggling with is that vsprintf seems to be behaving differently on OS X versus Linux (I've tested with Ubuntu 10.10 and 12.04). On OS X, it is behaving how I thought it should, where after calling vsprintf, calling va_arg returns the ms pointer (as if the vsprintf function called va_arg to get the 5). On Linux, however, the va_list does not change from vsprintf, and calling va_arg returns 5.

我真的很想找出一种方法来实现此功能,以便它在跨平台的行为上保持一致.假设您可以期望 vsprintf 始终更改 va_list 内的指针,以便下次调用 va_arg 时它返回下一个尚未使用的参数,这是错误的吗?

I would really like to figure out a way to implement this functionality so that it behaves consistently across platforms. Is it wrong to assume that you can expect vsprintf to consistently change the pointer inside va_list so that the next time you call va_arg it returns the next not-yet-used argument?

我已尽可能简化代码以演示该问题.在 OS X 上,此代码打印从 malloc 返回的指针的正确地址.在 Linux 上,foo 中 ms 的值变为 5,因此打印 5.

I have simplified my code as much as possible to demonstrates the issue. On OS X, this code prints the correct address of the pointer returned from malloc. On Linux, the value of ms in foo becomes 5, so it prints 5.

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

static void foo(void *, ...);

typedef struct {
    char *value;
} mystruct;

int main(int argc, char *argv[]) {
    mystruct *ms = malloc(sizeof(mystruct));
    foo(NULL, "%d %@", 5, ms);
}

void foo(void *dummy, ...) {
    va_list args;
    va_start(args, dummy);
    char buffer[512];
    int buffer_ptr = 0;
    int i = 0;
    char *format = va_arg(args, char *);

    buffer[0] = '\0';

    for (i = 0; i < strlen(format); i++) {
        if (i <= strlen(format) - 1 && (format[i] == '%' && format[i+1] == '@')) {
            vsprintf(buffer, buffer, args);

            /* can expect the next argument to be a mystruct pointer */
            mystruct *ms = va_arg(args, mystruct *);
            buffer[buffer_ptr+1] = '\0';
            fprintf(stderr, "%p", ms); /* SHOULD NOT PRINT 5 */

            /* concatenate here */  
        } else {
            buffer[buffer_ptr++] = format[i];
            buffer[buffer_ptr] = '\0';
        }
    }

    va_end(args);
}

推荐答案

你需要使用va_copy 如果您打算多次使用一个参数列表——不这样做是未定义的行为.您的代码应如下所示:

You need to use va_copy if you're going to use an argument list more than once -- failure to do so is undefined behavior. Your code should look something like this:

va_list args;
va_start(args, dummy);
...
char *format = va_arg(args, char *);
...
va_list argsCopy;
va_copy(argsCopy, args);
vsprintf(..., argsCopy);
va_end(argsCopy);
...
mystruct *ms = va_arg(args, mystruct *);
...
va_end(args);

这篇关于vsprintf 和 va_list 的平台不一致的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-20 22:16