4

背景: 私は現在、Objective-C が C フォーマットを拡張して "%@" シーケンスで NSString をサポートできるようにする方法と同様に、特定の構造体の処理をサポートする標準 C フォーマットを "拡張" しようとしています。

私が苦労している 1 つの問題は、OS X と Linux で vsprintf の動作が異なるように見えることです (Ubuntu 10.10 と 12.04 でテストしました)。OS X では、vsprintf を呼び出した後、va_arg を呼び出すと、ms ポインターが返されます (vsprintf 関数が va_arg を呼び出して 5 を取得したかのように)。ただし、Linux では、va_list は vsprintf から変更されず、va_arg を呼び出すと 5 が返されます。

プラットフォーム間で一貫して動作するように、この機能を実装する方法を見つけたいと思っています。vsprintf が一貫して va_list 内のポインターを変更し、次に va_arg を呼び出したときに次の未使用の引数が返されると期待できると仮定するのは間違っていますか?

問題を示すために、コードを可能な限り単純化しました。OS X では、このコードは malloc から返されたポインタの正しいアドレスを出力します。Linux では、foo の ms の値が 5 になるため、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);
}
4

2 に答える 2

5

va_copy引数リストを複数回使用する場合は、を使用する必要があります。使用しないと、未定義の動作になります。コードは次のようになります。

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);
于 2012-05-29T22:06:42.677 に答える
1

問題は、どのように実装するかは実装次第であるということですva_list-- 引数を直接抽出するためのすべての情報と状態が含まれる場合もあれば、状態を間接的に保持する何かへのポインタが含まれる場合もあります。したがって、それを vsprintf に渡すと、関連するすべての状態のコピーが作成される場合と作成されない場合があります。

あなたがしようとしていることのために欲しいのは、va_list *ではなくを取る vspintf のような関数ですva_list。したがって、返された後に適切な状態を確保できます。残念ながら、標準ではそのような機能は提供されていません。

于 2012-05-30T00:48:37.023 に答える