2013-06-26 11 views
6

私が持っている:scanf関数を使用して、最大文字列長は、 - > ANSI C

#define MAX_STR_LEN 100 

をし、私は文字列の長さを制御することができるようにscanfパターンに入れたい:

scanf("%100[^\n]s",sometext) 

私が試した:

scanf("%MAX_STR_LEN[^\n]s",sometext) 
scanf("%"MAX_STR_LEN"[^\n]s",sometext) 
scanf("%",MAX_STR_LEN,"[^\n]s",sometext) 

これは機能しませんでした。私はちょうど "sometext"はmalloc(MAX_STR_LEN)と割り当てられているので、バッファオーバーフローを避けたい...

アイデア?

+0

討議[こちら](http://stackoverflow.com/questions/9457325/how-to-use-sscanf-correctly-and-safely) –

+0

フォーマット文字列内で 'MAX_STR_LEN'をどのように使うことができますか? – amulous

+0

@amulous、厳密なANSI Cなので、実際の文字列変数には実際にアクセスできません。私はする必要があります:char * somestringし、malloc ... – tomdavies

答えて

10

は、私はこれらのソリューションのいずれかに満足していませんでしたので、私はさらに調査、およびマクロGNU GCCを発見として使用することができます

stringification

#define XSTR(A) STR(A) 
#define STR(A) #A 
#define MAX_STR_LEN 100 
scanf("%"XSTR(MAX_STR_LEN)"[^\n]s", sometext) 

たぶんVS2010は、似たようなを提供していますか?

+5

これはANSI C標準にあります.VS 2010で実際に*動作するはずです。(VSの特質を考えればわかりません) – michaelb958

+2

これは適切な答えです。誰もが "scanf()を使わないでください"と言っている:これは、OPによって記述されたバッファオーバーフローを避けるために十分であるべきである。 – jimis

+0

残念なことに、これはほとんどのIDEとコンパイル時の書式文字列検査を中断します。 –

7

私はその後scanf()を使用していないバッファオーバーフロー

を避けたいです。まったく。

テキスト行をスキャンする場合は、#define MAX_STRも入力しないでください。

char buf[LINE_MAX]; 
fgets(buf, sizeof(buf), stdin); 

トリックを行う必要があります:あなたは<limits.h>LINE_MAX(あなたがPOSIX互換システムをターゲットにしている場合は)来れることができます。

-3

どの程度

scanf("%.*[^\n]s", MAX_STR_LEN, sometext) 
+0

お願いします、ありがとうございます、***いいえ!*** –

+3

'scanf()'で '*'使い方を再読することをお勧めします。あなたの投稿を変更したいと思っています。 printf()の使用法では、 '*'はsizeの整数を読み込みますが、 'scanf()'では読み込みません。 '*'は "オプションの開始アスタリスクは、データがストリームから読み込まれるが無視されることを示します"を意味します。非常に異なる機能。 – chux

0

fgets(buffer, sizeof(buffer), stdin)アプローチを推奨します。

まだを使用したい場合は、実行時にその形式を作成できます。

#define MAX_STR_LEN 100 
char format[2 + sizeof(size_t)*3 + 4 + 1]; // Ugly magiC# 
sprintf(format, " %%%zu[^\n]", (size_t) MAX_STR_LEN); 
scanf(format, sometext); 

またはMAX_STR_LENが文字列

#define MAX_STR_LEN "100" 
scanf(" %" MAX_STR_LEN "[^\n]", sometext); 

であることを再定義それでもfgets()をお勧めします。
fgets()は先頭のスペースと末尾に\nを入れ、バッファには" %[^\n]"を入れません。
ところで、あなたの書式の末尾にあるsは、あなたが思うように行動する可能性は低いです。

4

ほとんどの人が言うように、fgets(..., stdin)を使用してこの問題を処理する方が良いです。

次のリンクで

、私はあなたが固体マクロによって、より安全な方法でscanf()を交換してみましょう、安全で正しい手法を提案している:

A macro that safely replaces scanf()

私が持っているマクロ次のプログラムに示すように、提案された(互換C99コンパイラでの作業)、safe_scanf()ある:

#include <stdio.h> 
#define safe_scanf(fmt, maxb, ...) { \ 
    char buffer[maxb+1] = { [maxb - 1] = '\0' }; \ 
    fgets(buffer, maxb+1, stdin); \ 
    if ((buffer[maxb - 1] != '\0') && (buffer[maxb - 1] != '\n')) \ 
     while(getchar() != '\n') \ 
      ; \ 
    sscanf(buffer, fmt, __VA_ARGS__); \ 
    } 

#define MAXBUFF 20  

int main(void) { 
    int x; float f;  
    safe_scanf("%d %g", MAXBUFF+1, &x, &f); 
    printf("Your input was: x == %d\t\t f == %g", x, f); 
    return 0; 
} 

マクロsafe_scanf()はかなりしっかりしているが、あなたは...あなたのニーズに応じてチューン
MAXBUFFの値を持つことになり、
マクロ的なアプローチを使用して、いくつかの弱点があります。
ミッシング型チェックのパラメータについては、戻り値がありません(実際には「scanf()関数とは異なりますが、を返し、エラーチェックのための貴重な情報を返します)。
問題が解決策を持っているが、それは別のトピックの一部であるすべての...

はたぶん、ほとんどの厳密解を組み合わせ、関節、stdarg.hライブラリを呼び出すことによって、パラメータの数が可変で機能my_scanf()を定義することですfgets()およびvsscanf()である。ここでは、コードを持っている:(

#include <stdio.h> 
#include <stdarg.h> 

int my_scanf(const char* fmt, const unsigned int maxbuff, ...) { 
    va_list ptr; 
    int ret; 

    if (maxbuff <= 0) 
     return EOF; /* Bad size for buffer[] */ 

    char buffer[maxbuff+1]; 
    buffer[maxbuff-1] = '\0'; /* Quick buffer cleaning... */ 

    if (fgets(buffer, maxbuff+1, stdin) == NULL) 
     return EOF; /* Error detected */ 
    else { 
     if ((buffer[maxbuff-1] != '\n') && (buffer[maxbuff-1] != '\0')) 
      /* Condition logically equivalent to: 
        fgets() has not reached an '\n' 
      */ 
      while (getchar() != '\n') 
       ; /* "Flushing" stdin... */ 

     va_start(ptr, maxbuff); 
     ret = vsscanf(buffer, fmt, ptr); 
     va_end(ptr); 
     return ret; 
    }  
} 

#define MAXBUFF 20 
int main(void) { 
    int x; 
    float z; 
    int scanf_ret = my_scanf("%d %g", MAXBUFF, &x, &z); 
    printf("\nTest:\n x == %d\n z == %g\n scanfret == %d", x, z, scanf_ret); 
    getchar(); 
    return 0; 
} 

機能my_scanfを)それは他のどのscanf()様が行うのと同じように動作フォーマット文字列fmtを受け入れプロトタイプ

int my_scanf(const char* fmt, const int maxbuff, ...); 

を持っています。
第2パラメータは、文字のの最大数であり、標準入力(キーボード)から効果的に受け入れられます。
戻り値はであり、maxbuffに意味がない場合、または入力エラーが発生した場合はEOFとなります。負でない値が返された場合は、標準関数sscanf()またはvsscanf()によって返される値と同じです。

fgets()は、追加の '\ 0'文字のための余裕があるため、maxbuffは1でインクリメントされます。
maxbuffの非正の値はすぐに破棄されます。
fgets()文字列は、maxbuff文字( '\ n'を含む)の大文字でstdin(キーボード)から読み取られます。
ユーザーが非常に長い文字列を入力した場合、buが切り捨てられ、すべての文字を次の '\ n'(ENTER)に破棄するには、ある種の "フラッシュ"メカニズムが必要です。そうでない場合、次のキーボードの読みは古い文字を持つ可能性があります。
「フラッシング」の条件は、fgets()stdinを読み取った後に '\ n'に達していないことです。
これは、buffer[maxbuff - 1]が '\ 0'でも '\ n'でもない場合にのみ適用されます。
はそれをチェック!
最後に、stdarg.hマクロと機能vsscanf()のappropiateの組み合わせは、パラメータの変数リストを処理するために採用されています。

関連する問題