91

私はこのコードを使用します:

while ( scanf("%s", buf) == 1 ){

ランダムな長さの文字列を渡すことができるように、バッファオーバーフローの可能性を防ぐための最良の方法は何でしょうか?

たとえば、次のように呼び出すことで、入力文字列を制限できることを知っています。

while ( scanf("%20s", buf) == 1 ){

しかし、ユーザーが入力したものは何でも処理できるようにしたいと思います。または、scanfを使用してこれを安全に行うことはできません。また、fgetsを使用する必要がありますか?

4

6 に答える 6

74

Kernighan と Pike は、彼らの著書The Practice of Programming (読む価値があります) でこの問題について議論しており、関数ファミリにsnprintf()渡すための正しいバッファー サイズで文字列を作成するために を使用して解決しています。scanf()実際には:

int scanner(const char *data, char *buffer, size_t buflen)
{
    char format[32];
    if (buflen == 0)
        return 0;
    snprintf(format, sizeof(format), "%%%ds", (int)(buflen-1));
    return sscanf(data, format, buffer);
}

これにより、入力が「バッファ」として提供されるサイズに制限されることに注意してください。さらにスペースが必要な場合は、メモリ割り当てを行うか、メモリ割り当てを行う非標準のライブラリ関数を使用する必要があります。


scanf()関数ファミリーの POSIX 2008 (2013) バージョンは、m文字列入力 ( %s%c%[) のフォーマット修飾子 (割り当て割り当て文字) をサポートしていることに注意してください。引数を取る代わりに、char *引数を取り、char **読み取る値に必要なスペースを割り当てます。

char *buffer = 0;
if (sscanf(data, "%ms", &buffer) == 1)
{
    printf("String is: <<%s>>\n", buffer);
    free(buffer);
}

sscanf()関数がすべての変換仕様を満たさない場合、-like 変換用に割り当てられたすべてのメモリは%ms、関数が戻る前に解放されます。

于 2009-10-25T20:21:58.787 に答える
33

gcc を使用している場合は、GNU 拡張a指定子を使用して、入力を保持するためのメモリを scanf() に割り当てることができます。

int main()
{
  char *str = NULL;

  scanf ("%as", &str);
  if (str) {
      printf("\"%s\"\n", str);
      free(str);
  }
  return 0;
}

編集: Jonathan が指摘したように、指定子が異なる可能性があり ( )、コンパイル時に特定の定義を有効にする必要がある場合があるため、 scanfman ページを参照する必要があります。%m

于 2009-10-25T18:50:46.427 に答える
7

Most of the time a combination of fgets and sscanf does the job. The other thing would be to write your own parser, if the input is well formatted. Also note your second example needs a bit of modification to be used safely:

#define LENGTH          42
#define str(x)          # x
#define xstr(x)         str(x)

/* ... */ 
int nc = scanf("%"xstr(LENGTH)"[^\n]%*[^\n]", array); 

The above discards the input stream upto but not including the newline (\n) character. You will need to add a getchar() to consume this. Also do check if you reached the end-of-stream:

if (!feof(stdin)) { ...

and that's about it.

于 2009-10-25T17:59:50.417 に答える
4

scanf(3)とそのバリアントを直接使用すると、多くの問題が発生します。通常、ユーザーと非対話型のユースケースは、入力行の観点から定義されます。十分な数のオブジェクトが見つからない場合、より多くの行で問題が解決するケースはめったにありませんが、それがscanfのデフォルトモードです。(ユーザーが最初の行に数字を入力することを知らなかった場合、2番目と3番目の行はおそらく役に立ちません。)

少なくともfgets(3)、プログラムに必要な入力行の数がわかっていて、バッファオーバーフローが発生しない場合は...

于 2009-10-25T17:04:28.420 に答える
1

入力の長さを制限する方が断然簡単です。ループを使用し、一度に少しずつ読み取り、必要に応じて文字列のスペースを再割り当てすることで、任意の長さの入力を受け入れることができます...

しかし、それは大変な作業なので、ほとんどの C プログラマーは、任意の長さで入力を切り取っています。これはすでにご存じだと思いますが、fgets() を使用しても、任意の量のテキストを受け入れることはできません。制限を設定する必要があります。

于 2009-10-25T17:11:18.157 に答える
0

文字列に必要なメモリを割り当てる関数を作成するのはそれほど面倒ではありません。それは私が少し前に書いた小さなC関数です。私はいつもそれを使って文字列を読み込んでいます。

読み取った文字列を返すか、メモリ エラーが発生した場合は NULL を返します。ただし、文字列を free() し、常に戻り値を確認する必要があることに注意してください。

#define BUFFER 32

char *readString()
{
    char *str = malloc(sizeof(char) * BUFFER), *err;
    int pos;
    for(pos = 0; str != NULL && (str[pos] = getchar()) != '\n'; pos++)
    {
        if(pos % BUFFER == BUFFER - 1)
        {
            if((err = realloc(str, sizeof(char) * (BUFFER + pos + 1))) == NULL)
                free(str);
            str = err;
        }
    }
    if(str != NULL)
        str[pos] = '\0';
    return str;
}
于 2013-04-05T07:11:12.630 に答える