1. ホーム
  2. c

scanfのデメリット

2023-08-31 02:14:30

疑問点

のデメリットを知りたいのですが。 scanf() .

多くのサイトでは scanf を使用すると、バッファオーバーフローが発生する可能性があると書かれています。この理由は何でしょうか?他に scanf ?

どのように解決するのですか?

scanfの問題点は、(最低でも)以下の通りです。

  • を使って %s を使用してユーザーから文字列を取得するため、文字列がバッファより長くなる可能性があり、オーバーフローを引き起こします。
  • スキャンに失敗すると、ファイル ポインタが不確定な場所に残る可能性があります。

私は非常に好んで fgets を使用して行全体を読み込む方が、読み込むデータ量を制限することができます。もし、1K のバッファがあり、そこに fgets で読み込んだ場合、その行が長すぎるかどうかは、行末に改行文字がないことで判断できます (改行のないファイルの最終行はともかくとして)。

それから、ユーザーに文句を言うか、残りの行にもっとスペースを割り当てることができます (必要であれば、十分なスペースができるまで継続的に)。どちらの場合でも、バッファオーバーフローのリスクはありません。

一旦行を読み込んだら、あなたは を知ることができます。 で、次の行の位置がわかるので、問題はありません。次に、あなたは sscanf という文字列を作成することができます。

これは、私が頻繁に使用するコードのスニペットで、ユーザーに情報を求めるときにバッファオーバーフローが発生しないことを保証します。

必要であれば、標準入力以外のファイルを使用するように簡単に調整できます。また、呼び出し元に返す前に、それ自身のバッファを割り当てさせることもできます (そして、十分に大きくなるまで増やし続けます) (もちろん、呼び出し元はそれを解放する責任があります)。

#include <stdio.h>
#include <string.h>

#define OK         0
#define NO_INPUT   1
#define TOO_LONG   2
#define SMALL_BUFF 3
static int getLine (char *prmpt, char *buff, size_t sz) {
    int ch, extra;

    // Size zero or one cannot store enough, so don't even
    // try - we need space for at least newline and terminator.

    if (sz < 2)
        return SMALL_BUFF;

    // Output prompt.

    if (prmpt != NULL) {
        printf ("%s", prmpt);
        fflush (stdout);
    }

    // Get line with buffer overrun protection.

    if (fgets (buff, sz, stdin) == NULL)
        return NO_INPUT;

    // Catch possibility of `\0` in the input stream.

    size_t len = strlen(buff);
    if (len < 1)
        return NO_INPUT;

    // If it was too long, there'll be no newline. In that case, we flush
    // to end of line so that excess doesn't affect the next call.

    if (buff[len - 1] != '\n') {
        extra = 0;
        while (((ch = getchar()) != '\n') && (ch != EOF))
            extra = 1;
        return (extra == 1) ? TOO_LONG : OK;
    }

    // Otherwise remove newline and give string back to caller.
    buff[len - 1] = '\0';
    return OK;
}

そして、そのためのテストドライバ。

// Test program for getLine().

int main (void) {
    int rc;
    char buff[10];

    rc = getLine ("Enter string> ", buff, sizeof(buff));
    if (rc == NO_INPUT) {
        // Extra NL since my system doesn't output that on EOF.
        printf ("\nNo input\n");
        return 1;
    }

    if (rc == TOO_LONG) {
        printf ("Input too long [%s]\n", buff);
        return 1;
    }

    printf ("OK [%s]\n", buff);

    return 0;
}

最後に、動作を確認するためのテスト実行です。

$ printf "\0" | ./tstprg     # Singular NUL in input stream.
Enter string>
No input

$ ./tstprg < /dev/null       # EOF in input stream.
Enter string>
No input

$ ./tstprg                   # A one-character string.
Enter string> a
OK [a]

$ ./tstprg                   # Longer string but still able to fit.
Enter string> hello
OK [hello]

$ ./tstprg                   # Too long for buffer.
Enter string> hello there
Input too long [hello the]

$ ./tstprg                   # Test limit of buffer.
Enter string> 123456789
OK [123456789]

$ ./tstprg                   # Test just over limit.
Enter string> 1234567890
Input too long [123456789]