GCCでフォーマット切り捨ての警告を回避する方法は?

2018年07月26日に質問されました。  ·  閲覧回数 24.4k回  ·  ソース

Marius Melzer picture
2018年07月26日

次のgcc形式の切り捨ての警告が表示されます。

test.c:8:33: warning: ‘/input’ directive output may be truncated writing 6 bytes into a region of size between 1 and 20 [-Wformat-truncation=]
snprintf(dst, sizeof(dst), "%s-more", src);
                             ^~~~~~
test.c:8:3: note: ‘snprintf’ output between 7 and 26 bytes into a destination of size 20
snprintf(dst, sizeof(dst), "%s-more", src);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

このようなコードで:

char dst[20];
char src[20];
scanf("%s", src);
snprintf(dst, sizeof(dst), "%s-more", src);
printf("%s\n", dst);

切り捨てられる可能性があることは承知していますが、そもそもこれが私がsnprintfを使用している理由です。 これが意図されていることをコンパイラーに明確にする方法はありますか(プラグマまたは-Wno-format-truncationを使用せずに)?

回答

KamilCuk picture
2018年07月26日
25

  1. 警告はgcc7.1に侵入されました。gcc7.1リリースの変更を参照してください。
  2. gccドキュメントから:

-Wformat-truncation [...]のレベル1は、戻り値が使用されておらず、出力が切り捨てられる可能性が最も高い有界関数の呼び出しについてのみ警告します。

  1. この問題はバグレポートであり、NOTABUGとしてクローズされました。

未処理の出力の切り捨ては、通常、プログラムのバグです。 [...]
切り捨てが予想される場合、呼び出し元は通常、関数からの戻り値をチェックし、何らかの方法でそれを処理します(たとえば、関数を分岐することによって)。 そのような場合、警告は発行されません。 警告によって出力されたソース行は、これがそれらのケースの1つではないことを示唆しています。 警告は、設計されたとおりに実行されています。

  1. ただし、エラー時に負の値を返すsnprintfの戻り値を確認するだけです。

#include <stdio.h>
#include <stdlib.h>
void f(void) {
    char dst[2], src[2];
    // snprintf(dst, sizeof(dst), "%s!", src);

    int ret = snprintf(dst, sizeof(dst), "%s!", src);
    if (ret < 0) {
         abort();
    }

    // But don't we love confusing one liners?
    for (int ret = snprintf(dst, sizeof(dst), "%s!", src); ret < 0;) exit(ret);
    // Can we do better?
    snprintf(dst, sizeof(dst), "%s!", src) < 0 ? abort() : (void)0;
    // Don't we love obfuscation?
#define snprintf_nowarn(...) (snprintf(__VA_ARGS__) < 0 ? abort() : (void)0)
    snprintf_nowarn(dst, sizeof(dst), "%s!", src);
}

https://godbolt.org/でgcc7.1gcc7.2 gcc7.3 gcc8.1を-O{0,1,2,3} -Wall -Wextra -pedanticテストしました。 警告は一切ありません。 gcc8.1は、 -O1より大きい最適化でabort()への呼び出しを最適化/削除します。

Daniel Griscom picture
2020年09月24日
4

このエラーは、長さが制限された*printf関数が呼び出された場合にのみトリガーされます(例: snprintfvsnprintf )。 つまり、sprintfで発生する可能性があるように、バッファがオーバーフローしている可能性があることを示すものではありませんsnprintfがその仕事をしていて、切り捨てているかどうかをチェックしていないことを通知するだけです。

それを知っていると、特定のインスタンスを無視するようにgccを誘導しようとするのではなく、 -Wno-format-truncationを使用してグローバルに無効にすることについてはるかに楽観的です。