3

[明確にするために組織と内容を更新]

本当の質問

C にとって、プログラマーがタイピングしている間に、プロジェクト固有の printf のようなデバッグ関数への安全で正しい呼び出しを作成するのに役立つ良い方法は何でしょうか?

Cマクロ?C ラッパー関数? コード エディターのマクロまたはテンプレート? 他の?

背景に関する質問と回答

多くのソフトウェアは、問題が発生した場合のアドホックまたはデバッグ ログのいずれかで、デバッグに printf または printf に似た関数を使用します。それでも、エラーが発生しやすいです。

Q1: どのように私たちは知っていますか?
A1: スタティック アナライザーには、printf-mismatch エラーのカテゴリがあります。これはエラーの一般的なクラスです。これらのツールが C コードでこれらの警告を呼び出すのをよく目にします。

Q2: このエラーのサブクラスは何ですか?
A2: 主に、書式指定子の誤りと、書式指定子の数の誤りです。多くの場合、実際のエラーは逆です: 変数の型が間違っているか、出力する変数の数が間違っています。

Q3: なぜ気にするのですか?
A3: せいぜい、間違ったログ情報を引き起こし、デバッグを妨げます。最悪の場合、ソフトウェアがクラッシュします。

Q4: この問題について何かしようとした人はいますか?
A4: 確かに、(C++ などとは対照的に) C 用のものは見たことがありませんが、たとえば、次のようになります。

http://www.ddj.com/cpp/184401999?pgno=1 http://mi.eng.cam.ac.uk/~er258/cvd/tag/html/group__printf.html

これらの製品やその他の製品で私が欠けているのは、現在 C で書かれた製品を見ており、C の問題を解決する必要があるという事実に加えて、それらが事後の解決策であるということです。彼らはクラッシュを回避でき、何がうまくいかなかったのか、何かがうまくいかなかったという警告の説明を提供できます、プログラマーの意図が何であったかを推測することはできません (特に上記の Q&A #2 を参照)。

Q5: printf を使用するとエラーが発生しやすいのはなぜですか?
A5: printf 呼び出しを記述するには、プログラマーが変数の型と数、書式指定子、フリー テキスト文字列定数、および句読点 (これらはすべて互いに非常によく似ている) を 1 行に混在させる必要があるためです。

4

5 に答える 5

8

gccは-Wformat、printf / scanf / strftime/strfmonフォーマットエラーについて警告するために提供します。

$ gcc -Wformat -c -o test.o test.c
test.c: In function ‘main’:
test.c:5: warning: format ‘%s’ expects type ‘char *’,
          but argument 2 has type ‘int’
$ cat test.c
#include <stdio.h>

int main(int argc, const char *argv[])
{
     printf("%s\n", 0);
     return 0;
}
于 2009-12-28T15:14:36.407 に答える
4

GCC には、関数の使用を防ぐ方法が組み込まれています。

#pragma GCC poison printf

警告ではなくエラーであるため、「-Wall」よりも優れています。printf がどのように置き換えられるかはわかりません-おそらく多くの特殊な関数に置き換えられます。

GCC プラグマを参照

于 2009-12-28T15:47:30.757 に答える
3

の型と形式をチェックできるコンパイラを使用してくださいprintf()。最近のほとんどのコンパイラはそれを実行できるはずです。GCCの場合、-Wallはあなたの友達です(または-Wformat、フォーマットチェックだけが必要な場合)。詳細については、警告オプションを参照してください。

それ以外の唯一のオプションは#define printf ILLEGAL_DO_NOT_USE、プロジェクトの共通ヘッダーファイルにを追加し、同じジョブを安全に実行する別の関数を提供することです。そのアプローチで頑張ってください;)

[編集]あなたの問題は、Cがそれ自体でパラメータとして渡されたものに型情報を添付できないことです。だからあなたができることはこれらの線に沿った何かです:

safe_printf_like_function("%d %s %c\n", INT_TYPE(value), STRING_TYPE(s), CHAR_TYPE(c));

マクロには、型へのキャストが含まれている必要があり(コンパイラーは、型を渡すときに型が間違っていることに気付くことができます)、さらに型情報を運ぶものに展開する必要があります。

欠点:Cプログラマーは、そのようなAPIを提示すると、苦しみながら悲鳴を上げることになります。Cはこのようになることを意図したものではありません。Cは安全ではありません。限目。それは安全ではないことを意味します。設計、習慣および伝統による。セーフティネットが必要な場合は、Cは適していません。

そうは言っても、Cである程度の安全性を達成することはできますが、コストがかかります。コード内のどこでもvarargsの使用を禁止する必要があります。ポインタと配列は、サイズなどをチェックするコードでラップする必要があります。そうです、それは可能ですが、Cではなくなりました。

それに直面して、Cは1972年からです。それは古代であり、それは示しています。ほぼ35年間、Cを安全にするための賢い方法を見つけることができた人は誰もいませんでした(試行と期待できる成功の量については、C ++を参照してください)。

于 2009-12-28T15:15:13.923 に答える
1

lint私が持っていた最高の幸運は、 やのようなツールを使用してsplint、物事が 100% 合格していないときに完全に適合するものを提案できたことです。なんらかのコード インスペクション (警告が適切に抑制されている場合に対処するため) と組み合わせると、理想的なソリューションではありませんでしたが、これで作業を完了できました。

于 2009-12-28T14:58:27.097 に答える
0

簡単に解決できる問題ではありません。特定のパターン (たとえば、 など) を作成"%s: %d"する場合は、マクロまたは (できれば) ラッパー関数を使用できますprintf()。複雑さを複製せずに の機能を複製することはできません。

値の型を決定するには (不一致を避けるために必要です)、C プログラムを解析する必要があるため、コード エディターはチェックするのが難しいことに気付くでしょう。C コンパイラ自体が警告を発する可能性はありますが、警告を発するものは多くありません (警告があればいいのですが)。

C++ は、演算子のオーバーロードを利用する iostream の問題を回避しますが、それは C のオプションではありません。

私が与えることができる 1 つの規則は、のような文字列変数を出力しないことですprintf(string);printf(%s, string);文字列にパーセント記号がある場合に見つけにくいバグを回避するため、代わりに常に使用してください。

于 2009-12-28T15:00:42.987 に答える