2016-07-18 11 views
8

このようなfgets機能の外観の宣言:fgets()を使用してint型の2番目の引数をキャストしないようにするにはどうすればよいですか?

char *fgets(char *str, int n, FILE *stream); 

これは、第二引数はintことが期待されていることを意味します。

次のプログラムでこのキャスティングを避ける適切な方法はどれですか?ここで

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

int main(void) { 
    const char *buffer = "Michi"; 
    size_t len = strlen(buffer) + 1; 
    char arr[len]; 

    printf("Type your Input:> "); 
    if (fgets(arr, (int)len, stdin) == NULL) { 
     printf("Error, fgets\n"); 
     exit(1); 
    } else { 
     printf("Arr = %s\n", arr); 
    } 
} 

私は正常に見えるどの(int)lenを使用しますが、bufferは非常に長い文字列を格納した場合はどうなりますか?

は言うことができます:

const char *buffer = "Very long long ..."; /* where length is beyond the range of the `int` type */ 

Iここで結構ですが、私はfgetsに渡します場合ためのOKでないタイプsize_tのすでに宣言された長さ:

conversion to ‘int’ from ‘size_t {aka long unsigned int}’ may alter its value 

intにキャストすると、のサイズがlengthのサイズより小さくなり、一部の情報が失われるため、適合しません。

は、たぶん私は

任意の方法は、どのように私はこのような状況を避ける必要があります...ここに何か欠けていますか?

+1

で議論されているような病的な例を使用して、コーナーケースを考えます; '正しく動作するのでしょうか?とにかくgccとスタックしています。 –

+1

@SouravGhoshここではmallocを使うことができましたが、それについての質問はありません – Michi

+0

多分http://stackoverflow.com/questions/8317295/convert-unsigned-int-to-signed-int -cはあなたに役立ちます。 – Sergio

答えて

5
#include <stdio.h> 
char *fgets(char * restrict s, int n, FILE * restrict stream); 

をキャストすることができ、入力バッファs[INT_MAX]以降は使用できません。


OPのsize_t lenコードは、文字列に変換しintに戻って変換することによりlenintキャストを避けることができ、それはただ無駄です。キャストは正しいことです。

コードを(int)で捨てるのではなく、その使用を減らしたり制御したりして、制限付きヘルパー機能でキャストをラップします。

int fgets_len(size_t len) { 
    return (len < INT_MAX) ? (int) len : INT_MAX; 
} 


size_t len = something_big; 
char *arr = malloc(len); 

... 
if (fgets(arr, fgets_len(len), stdin) == NULL){ 
    printf("Error, Fgets\n"); 
    exit(1); 
}else{ 
    printf("Arr = '%s'\n", arr); 
} 

コードが本当に長い行を読み取る必要がある場合は、定義されたhereとしてssize_t getline(char **lineptr, size_t *n, FILE *stream);を考えます。これは非標準のCライブラリ関数ですが、そのソースコードはすぐに利用できます。 fgets()の知識をひけらかす使用に関する


NULL返すためにfgets()のために少なくとも2つの理由があります:エンド・オブ・ファイルと入力エラーが。今OPのコード

size_t len = strlen("") + 1; 
char arr[len]; 
if (fgets(arr, (int)len, stdin) == NULL){ 

if (fgets(arr, 0, stdin) == NULL){ 
if (fgets(arr, -1, stdin) == NULL){ 

両方がちょうどその場合、あなたは必ず `char型のARR [LEN]は、言っIs fgets() returning NULL with a short bufffer compliant?

+0

*関数fgets() '、入力バッファは[INT_MAX]だ'以降使用することができない* 'と:。より正確には、任意のサイズのバッファを使用することができるが、それは'よりも長い場合INT_MAX'そのサイズができません直接使用することができます。 '[INT_MAX]'は問題ではないことに注意してください。 – chqrlie

2

整数オーバーフローの問題を回避したい場合は、値チェックを行い、それに応じて動作させることができます。 strlen()size_tタイプの値を返すので、2つのオプションがあることに注意してください。 intの変数を宣言し、戻り値をstrlen()に割り当てます。これは、size_tからintへの暗黙的な変換を行います。または、関数の呼び出しと同じようにキャストします。ここ

は可能な解決策である。

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

int main(void) 
{ 
    const char *buffer = "Michi"; 
    //size_t len = strlen(buffer) + 1; 
    size_t len = 9999999999; 

    if (len > INT_MAX) { 
     fprintf(stderr, "Error, length exceeded desired value.Aborting...\n"); 
     return 1; 
    } 

    char arr[len]; 

    printf("Type your Input:> "); 
    if (fgets(arr, (int)len, stdin) == NULL) { 
     printf("Error, Fgets\n"); 
     exit(1); 
    } 
    else { 
     printf("Arr = %s\n", arr); 
    } 
    return 0; 
} 
+0

私はすでに 'len> INT_MAX'を知っていますので、ここで' if(len> INT_MAX) '私のプログラムは間違った動作を止めます。 – Michi

+0

@Michi - 'n <= 0'で' fgets'を呼び出すと、あなたのプログラムがとにかく終了するように、入力を待たずに 'NULL'を返します。だから最善のことは、行く前に 'len'をチェックすることです。 – 4386427

+0

@Michi - また 'size_t'から' int'までは、肯定的だが無意味な値に終わる可能性があります。例 '0x100000003'はあなたのプログラムが奇妙な動作をするようになる' 3'になる可能性があります。コールを行う前に 'len'が' INT_MAX'を超えていないことを確認してください。 – 4386427

0

Draftその

符号付きと符号なし整数を変換した結果をより短い符号付き整数に整数変換、または結果言います 値が表現できない場合は同じ長さ

は実装定義です。結果として、符号なしタイプの値を安全に符号付きに変換する場合は、すべてのソース値をターゲットの符号付きタイプで表すことができるようにする必要があります。

1

引数の次元をfgets()にリンクするのは、bufferという次元では全く気にしません。

代わりに、読み込みに使用するバッファの長さがint(たとえば、INT_MAXを超えない)を使用して表現できることを確認します。真に移植したい場合は、バッファ長が32767を超えないようにしてください(規格では、最小許容値INT_MAX32767です)。

次に、fgets()は、行の長さがバッファの長さを超えた場合、部分的に行を読み取るという事実を利用します。

たとえば、lenが、読み取られる行の長さをstdinから超えていると仮定すると、ファイルの終わりがすぐ'\n'が付いていない場合は、上記の最後の'\n'の後にテキストを破棄し、

char arr[len] = {0}; 
char read_buffer[10]; /* I'm reasonably confident that 10 < 32767 */ 

while (fgets(read_buffer, 10, stdin) != NULL) 
{ 
     size_t read_length = strlen(read_buffer); 
     if (read_length > 0) 
     { 
      if (read_buffer[read_length-1] != `\n`) 
      { 
       strcat(arr, read_buffer); 
      } 
      else 
      { 
       strncat(arr, read_buffer, read_length-1); 

       printf("Arr = %s\n", arr); 

       arr[0] = '\0'; /* clear arr so next line may be read */ 

       /* break here if want to stop reading after the first line */ 

      } 
     } 

注意。上記で

以下INT_MAXに等しい値を有するsize_tが常に安全intに変換することができるので、fgets(read_buffer, sizeof read_buffer, stdin)fgets(read_buffer, 10, stdin)を交換することは、安全です。あなたは警告を発行するコンパイラを黙らせたいのであれば、あなたは安全にfgets()で、すなわちfgets(read_buffer, (int)(sizeof read_buffer), stdin)

関連する問題