のような文字列を意味します"(%d%%%d) some text %s , %d%% %x %o %#x %#o \n"
。関数の printf ファミリは、この文字列から必要な引数の数を何らかの形で知っているため、別の引数は必要ありません。この機能は c/c++ で別の関数として提供されているので、独自の printf のような関数を作成できますか? (はい、これらのパーセントは、複雑になる可能性があることを強調するために意図的に付けているため、単純なパーセント文字のカウントではありません)
3 に答える
ファミリの問題printf
は、安全ではないことであり、文字列または引数リストにいくつの形式要素があるかを実際には認識していないか、気にしていません。
printf
ファミリは、「変数関数」と呼ばれるものを使用します。1 つのパラメーター (フォーマット文字列) のみが指定され、他のパラメーターはその型やサイズを考慮せずにスタックから取得されます。それらの型はフォーマット文字列から推定されるため、タイプセーフではなく、使用する引数の数を反復処理によって見つけます。文字列とすべての書式指定子を見つけるため、引数番号が安全ではなくなります。可変個引数関数を書くことができます。構文は
void foo(...);
後でこれらのマクロと型を使用できますが、C++ を使用している場合は、可変個引数テンプレートはタイプ セーフであるため、C 可変個引数関数の代わりに C++11 の可変個引数テンプレートを使用する必要があります。型情報はどこにも失われません。ユニバーサル参照(ただし、あまり感謝していません) は、可変個引数関数よりも強力です。
関数の printf ファミリーは、最初のパラメーターの 1 つだけを認識します...
基本的に文字列をスキャンし、理解できるフォーマット指定子に遭遇するたびに、引数リストからそのサイズの次の引数を取得します...これは簡単に破損する動作です。想像:
printf("%i",someInt); // fine
printf("%i",someLong); // depending on endianness and sized
// of those types could be the high or low 32 bits(probably)
printf("%i %i",someInt); // depending on abi could crash, read a int sized chunk
// of stack, or a register that would correspond to that
// parameter.
本質的に安全ではありません..
警告に注意を払い、移植可能なコードを書くときは次のようにする必要があります。
size_t t = 5; //can be 32 or 64 bit depending on arch.
printf("%ull",(unsigned long long)t);
編集。私は質問に半分しか答えていないと思います...独自の可変引数関数を定義できます。次の例では、count
いくつかのパラメーターを追加して結果を返します。
int sumList(int count, ...);
int main(int argc, const char * argv[])
{
printf("the answer is: %i",sumList(4,1,2,3,4));
return 0;
}
int sumList(int count, ...)
{
va_list args;
va_start(args, count);
int sum = 0;
for (int i = 0; i<count; i++) {
sum += va_arg(args, int);
}
va_end(args);
return sum;
}
いいえ、printf スタイルのフォーマット文字列を解析するための標準ライブラリ関数はありません。独自の文字列書式設定関数を作成する場合は、printf の例に従わないことを強くお勧めします。他の回答に記載されている理由により、本質的に安全ではありません。一部の言語では語順が変わるため、ローカリゼーションにも問題があります。個人的には、テンプレートを使用し、.NET スタイルのフォーマット文字列をコピーして、タイプ セーフな関数を作成します。(実際、私はそれをやりました。大変な作業ですが、楽しい作業でもあります。)