2016-11-03 6 views
3

次のプログラムは64ビットLinuxマシンで動作しますが、32ビットLinuxマシンではクラッシュします。個数別にva_listを処理する

#include <cstdarg> 
#include <iostream> 

void proc_int(std::va_list va) 
{ 
    std::cout << va_arg(va, int); 
} 

void proc_str(std::va_list va) 
{ 
    std::cout << va_arg(va, const char*); 
} 

void outputv(std::va_list va) 
{ 
    proc_int(va); 
    std::cout << " "; 
    proc_str(va); 
    std::cout << "\n"; 
} 

void output(int dummy, ...) 
{ 
    va_list va; 
    va_start(va, dummy); 
    outputv(va); 
    va_end(va); 
} 

int main() 
{ 
    output(0, 42, "hello"); 
} 

私はva_listが64ビットに32ビットが、struct __va_list_tag[1]char*であるためであると考えています。このプログラムを移植可能にするために、私はどのような変更を加えることができますか?outputvの署名を変更することはできません。 cppreferenceから

+1

ポインタで渡す:http://ideone.com/bhKalu –

+0

@ n.m。 64ビットでは、エラー: '__va_list_tag **'を '__va_list_tag(*)[1] 'に変換できません。 – aschepler

答えて

1

これはva_listのが配列かどうかであることをtypedefedされているかどうかを動作するはずです:

#include <cstdarg> 
#include <iostream> 
#include <type_traits> 

using my_va_list = std::decay<std::va_list>::type; 

void proc_int(my_va_list &va) 
{ 
    std::cout << va_arg(va, int); 
} 

void proc_str(my_va_list &va) 
{ 
    std::cout << va_arg(va, const char*); 
} 

void outputv(std::va_list va) 
{ 
    my_va_list mva = va; 
    proc_int(mva); 
    std::cout << " "; 
    proc_str(mva); 
    std::cout << "\n"; 
} 

すべての実装で動作することは保証されていませんが、少なくともgcc、clang、およびvC++では32ビットモードと64ビットモードの両方で動作します。

3

If a va_list instance is created, passed to another function, and used via va_arg in that function, then any subsequent use in the calling function should be preceded by a call to va_end

それはその(その後の使用)かどうかを完全に明らかではないが、別の関数に渡す含まれていますが、これは確かにもっともらしいです。

比較のために(Linux)のローカルのmanページをチェック:

If ap[the va_list] is passed to a function that uses va_arg(ap,type) then the value of ap is undefined after the return of that function

だから、あなたは、単に周りva_listを通過させると、それをあなたがしている方法、および32ビットバージョンを使用していないだけに起こりますそれを取り除く。

What changes can I make to make this program portable, preferably without changing the signature of outputv?

まあ、ただ他の機能にva_listを渡すと、それはまだ、その後に動作することを期待しないでください:

void outputv(std::va_list va) 
{ 
    std::cout << va_arg(va, int); 
    std::cout << " "; 
    std::cout << va_arg(va, const char *); 
    std::cout << "\n"; 
} 
+0

'va_list&'をたくさん渡している伝統的なコードがあります。しかし、 'va_list 'は' va_list'パラメータがポインタに変換された配列であれば 'va_list'パラメータにバインドできません。 – aschepler

+1

おそらく、破損は32ビットからの呼び出し規約の変更によるものですが、それは修正するのには役立ちません。 'va_list'を使わなくても、あなたが実際にやっていることを実装する方法について質問することができます。 – Useless

0

(その可変引数機能を直接C++に含まれている)、C99、セクション7.15パラグラフ3(強調鉱山)C標準によると:あなたが関数にva_listを渡しているので

The type declared is va_list which is an object type suitable for holding information needed by the macros va_start , va_arg , va_end , and va_copy . If access to the varying arguments is desired, the called function shall declare an object (generally referred to as ap in this subclause) having type va_list . The object ap may be passed as an argument to another function; if that function invokes the va_arg macro with parameter ap , the value of ap in the calling function is indeterminate and shall be passed to the va_end macro prior to any further reference to ap .212)

、それがありますその内部にva_argを使用している場合、その関数が返ってからそのva_listを再利用することはできません。そして、「212」の注記は、解を与える:

関連する問題