49

私はこれをC/C++でやろうとしています。

可変長引数に出くわしましたが、これはlibffiを使用した Python と C のソリューションを示唆しています。

printfさて、関数をラップしたい場合myprintf

私がしていることは以下のようなものです:

void myprintf(char* fmt, ...)
{
    va_list args;
    va_start(args,fmt);
    printf(fmt,args);
    va_end(args);
}

int _tmain(int argc, _TCHAR* argv[])
{
    int a = 9;
    int b = 10;
    char v = 'C';
    myprintf("This is a number: %d and \nthis is a character: %c and \n another number: %d\n",a, v, b);
    return 0;
}

しかし、結果は期待通りではありません!

This is a number: 1244780 and
this is a character: h and
another number: 29953463

どこで見逃したのですか??

4

7 に答える 7

67

問題は、va_args で「printf」を使用できないことです。可変引数リストを使用している場合は、vprintfを使用する必要があります。vprint、vsprintf、vfprintf など (Microsoft の C ランタイムには、バッファ オーバーランなどを防ぐ「安全な」バージョンもあります)

サンプルは次のように機能します。

void myprintf(char* fmt, ...)
{
    va_list args;
    va_start(args,fmt);
    vprintf(fmt,args);
    va_end(args);
}

int _tmain(int argc, _TCHAR* argv[])
{
    int a = 9;
    int b = 10;
    char v = 'C'; 
    myprintf("This is a number: %d and \nthis is a character: %c and \n another number: %d\n",a, v, b);
    return 0;
}
于 2008-09-03T14:30:29.167 に答える
8

また、あなたが純粋とはどういう意味かわかりません

C++ では、

#include <cstdarg>
#include <cstdio>

class Foo
{   void Write(const char* pMsg, ...);
};

void Foo::Write( const char* pMsg, ...)
{
    char buffer[4096];
    std::va_list arg;
    va_start(arg, pMsg);
    std::vsnprintf(buffer, 4096, pMsg, arg);
    va_end(arg);
    ...
}
于 2008-09-03T10:33:49.017 に答える
5

va_list実はラッパーからバージョンを持たない関数を呼び出す方法があります。アイデアは、アセンブラを使用し、スタック内の引数に触れず、関数の戻りアドレスを一時的に置き換えることです。

Visual C x86 の例。call addr_printfコールprintf():

__declspec( thread ) static void* _tls_ret;

static void __stdcall saveret(void *retaddr) {
    _tls_ret = retaddr;
}

static void* __stdcall _getret() {
    return _tls_ret;
}

__declspec(naked)
static void __stdcall restret_and_return_int(int retval) {
    __asm {
        call _getret
        mov [esp], eax   ; /* replace current retaddr with saved */
        mov eax, [esp+4] ; /* retval */
        ret 4
    }
}

static void __stdcall _dbg_printf_beg(const char *fmt, va_list args) {
    printf("calling printf(\"%s\")\n", fmt);
}

static void __stdcall _dbg_printf_end(int ret) {
    printf("printf() returned %d\n", ret);
}

__declspec(naked)
int dbg_printf(const char *fmt, ...)
{
    static const void *addr_printf = printf;
    /* prolog */
    __asm {
        push ebp
        mov  ebp, esp
        sub  esp, __LOCAL_SIZE
        nop
    }
    {
        va_list args;
        va_start(args, fmt);
        _dbg_printf_beg(fmt, args);
        va_end(args);
    }
    /* epilog */
    __asm {
        mov  esp, ebp
        pop  ebp
    }
    __asm  {
        call saveret
        call addr_printf
        push eax
        push eax
        call _dbg_printf_end
        call restret_and_return_int
    }
}
于 2014-02-26T17:01:46.103 に答える
1

C または C++ を使用していますか? 次の C++ バージョンである C++0x は、その問題の解決策を提供する可変個引数テンプレートをサポートします。

別の回避策は、巧妙な演算子のオーバーロードによって達成でき、次のような構文を実現できます。

void f(varargs va) {
    BOOST_FOREACH(varargs::iterator i, va)
        cout << *i << " ";
}

f(args = 1, 2, 3, "Hello");

これを機能させるには、プロキシ オブジェクトを返すvarargsオーバーライドするようにクラスを実装する必要があります。ただし、現在の C++ でこのバリアント型を安全にすることは、型消去によって機能する必要があるため、私の知る限り不可能です。operator =operator ,

于 2008-09-03T10:20:06.397 に答える
0
void myprintf(char* fmt, ...)
{
    va_ list args;
    va_ start(args,fmt);
    printf(fmt,args); ----> This is the fault. vprintf(fmt, args); should have been used.
    va_ end(args);
}
If you're just trying to call printf, 
there's a printf variant called vprintf that takes 
the va_list directly :  vprintf(fmt, args);
于 2012-04-30T07:55:53.093 に答える
0

純粋な C/C++ ソリューションとはどのような意味ですか?

C ランタイムでは、rest パラメーター (...) がクロスプラットフォームでサポートされています。

http://msdn.microsoft.com/en-us/library/kb57fad8.aspx

于 2008-09-03T10:19:33.903 に答える