dポイントプレゼントキャンペーン実施中!

C言語を勉強しています。

自分が知っている入出力を行う関数は
printf
scanf
fprintf
fscanf
sprintf
sscanf
gets
puts
getc
putc
fgets
fputs
fgetc
fputc
なのですがそれぞれのメリット・デメリットの違いがまだあいまいです。
というかまだ上の4つくらいしかまともに使ったことがありません。
自分の考えでは、
・上の4つは書式を指定でき、ファイルから読み込みするときなどは
fgetsよりもfscanfが使いやすいと思っています。

・スペースを読み込みたいときはscanfではなくgetsを使わなければならない。

・システム開発でscanfを使うことは危険なのでほとんどない。

間違った考え方をしていたり、どのようなデータのときに
どの関数を使うのが正しい、常識、と知っている方いらっしゃいましたら
教えてください。

A 回答 (7件)

まずはそれぞれの関数の仕様を正確に把握しましょう。


それを怠っていると、実際以上に物事が複雑に見えてしまい、難度を上げてしまいます。

どの関数を使うかの基準ですが...

ちょっとした実験プログラムや使い捨てのツールなどであれば、使いやすい関数を使うのが一番楽です。
このような場合は、文字列の読み込みはgets、数値入力ならscanfというように、入力部分は楽をして、本質的な部分に注力する方が得策です。

ある程度以上信頼性が求められる状況であれば、機能面だけではなく、エラー処理のことも考えて使う関数を選ばなければなりません。

たとえば、scanfやfscanfは、文字列を読み込むのであれば、(たとえ空白文字を含む文字列の入力であっても)特に問題なく使うことができます。ただし、入力された文字列に対してバッファサイズが不足していた場合は、読み込めなかった残りの文字をどうするか、十分注意しなければなりません。
一方で、scanfやfscanfでは、数値(整数値も浮動小数点数値も)を読み込む場合、オーバーフローの検出がまったくできません(未定義の動作になります)。また、"%lc"や"%ls"でマルチバイト文字(列)を読み込むときも、表現形式に異常があってもエラーを検出できません。ですので、信頼性に配慮するなら、(ワイドではない)文字列以外の入力にscanfやfscanfを使うべきではありません。これはsscanfでも同様です。

fgetsには大きな問題はありませんが、バッファサイズが足りずに1行分の文字列全体を読み込めなかった場合の対応に注意しなければなりません。ここのところを無視して、getsをやめて単純にfgetsに置き換えることを勧める人が少なくありませんので、気を付けてください。

入力に関していえば、fgetc(またはgetc)だけを使うのが、もっとも細かい制御が可能です。ただし、マルチスレッド環境の場合には排他制御を考慮しなければなりません。処理系によっては、getc_unlockedとflockfile/funlockfileを組み合わせる方が便利でしょう。

wprintfやwscanfなどのワイド文字版の関数もありますが、この場合はロケールの影響に注意しなければなりません。ロケールについては、普通のprintfでも影響を受けます。たとえば、浮動小数点数の小数点文字とか、ワイド文字から多バイト文字への変換とか。

ところで...

将来ある程度以上の権限を持てるようになった場合、scanfではなくfgetsとsscanfを使うべきだと盲目的に信じているようなプログラマは使うべきではありません。
前述したように、scanfと同じ問題はsscanfも抱えているので用途は制限されますし、また、問題があっても状況次第では使えるからです。
にも関わらず、そうしたことを妄信しているプログラマは、仕様を正しく理解する努力が足りないか、日常的に実験プログラムを書いて勉強する習慣がないかであり、結果としてまともな基礎力が期待できません。
    • good
    • 0
この回答へのお礼

>信頼性に配慮するなら、(ワイドではない)文字列以外の入力にscanfやfscanfを使うべきではありません。
覚えておきます。

>処理系によっては、getc_unlockedとflockfile/funlockfileを組み合わせる方が便利でしょう。
初めて聞きました(^_^;)
もう少し勉強してみます。

>fgetsとsscanfを使うべきだと盲目的に信じているようなプログラマは使うべきではありません。
そうなんですか?!
いろいろな意見があるのですね(^_^;)臨機応変に使いわけるのが大切なんですね!

お礼日時:2011/08/28 10:30

int main() {


int a,b,c ;
scanf("%d",&a);
scanf("%d",&b);

c=a+b ;

print("%d + %d = %d\n",a,b,c ) ;

return 0;
}

いかにも入門書の最初のころにありそうなプログラムです。
これを scanf(fscanf等の同系列を含む)を使わず、まだ説明されていないであろう「文字列(charの配列)」も使わず(文字列が使えないのだから、atoi等の変換関数も使えない)、「安全」なプログラムを作れ、と言われて、hello,world. を動かしたばかりの初心者にできるでしょうか?
あるいは、そんなプログラムが載っていて、勉強を続ける気になるでしょうか?

使い方さえ間違えなければ、便利ですよ
    • good
    • 0
この回答へのお礼

適材適所でがんばります!

お礼日時:2011/08/29 23:43

「なぜ scanf をC言語の入門で最初に教えるのか」については, 「とりあえず適切に入力する限り最も簡単だから」ということもありますな. 結局のところ, どんな方法をとってもそれぞれに「問題」が発生しうる.



ちなみに fgets+sscanf だと
・scanf では読み込めるが fgets+sscanf では読み込めない入力がある
・sscanf では「読み込み位置」が変化しないことに気付かない人がいる (事実)
ということもあったりします.

少なくとも, 「単純に置き換えられる」ものでもない.

そもそも fgets が「1行読み込む」というものでもないので....
    • good
    • 0
この回答へのお礼

ありがとうございます。
経験積んで身につけるしかないですね。

お礼日時:2011/08/29 23:27

> なぜそんな危険な関数をC言語の入門で最初に教えられるんでしょうかね?(笑)



例えるなら、鉛筆は凶器になりますが、そんな危険な道具を小学一年生にも(多くの場合は幼稚園児にも)使わせます。
大切なのは、危険なものを一切排除することではなく、安全に使うにはどうすればよいかをきちんと理解させることなのです。
    • good
    • 0
この回答へのお礼

スケールの大きいたとえでわかりやすかったです(笑)

危険を理解しながら勉強していこうと思います!
ありがとうございました!

お礼日時:2011/08/28 16:55

まずは、どっかで関数のリファレンスマニュアルを探しましょう。

読めば違いがちゃんと書いてあります。

まず、同等の機能で
・1文字入力: getchar, fgetc, getc
・1文字出力: putchar, fputc, putc
・1行入力: fgets, gets
・1行(or文字列)出力: puts, fputs
・書式付き入力: scanf,fscanf
・書式付き出力: printf,fprintf
と言うかんじに分類され、それぞれ
・fで始まる: ファイルポインタを使って指定したファイルから入力/ファイルに出力する
・それ以外(getc,putcは例外): 標準入力から入力/ 標準出力に出力する
という大体のルールがあります。

標準入出力というのは、実行時にすでにオープンしている特別な「ファイル」だと考えてください。
何もしなければ、標準入力は「端末でのキーボードからの入力」、標準出力は「端末の画面への出力」になっていることが多いです。入門書によく「scanfはキーボードから入力」「printfで画面に出力」とありますが、これは厳密には間違いです。
これらは、 stdin, stdout という変数に設定済みなので
scantf( format,.. ) = fscanf( stdin, format, .... )
printf( format,.. ) = fprintf( stdout, format, .... )
となります。
ただ、完全に対称では無くて、fgetsとgets, fputsとputsのように、仕様に違いがあるものもあります(なので、リファレンスマニュアルで確認してください)

> ・上の4つは書式を指定でき、ファイルから読み込みするときなどはfgetsよりもfscanfが使いやすいと思っています。
> ・スペースを読み込みたいときはscanfではなくgetsを使わなければならない。
> ・システム開発でscanfを使うことは危険なのでほとんどない。

これにもいろいろと誤解があります。

・どちらが使いやすいか、は、なにをするか、によって違います。単純に決めないことです。
たとえば、「単純にテキストを一行読み出して、先頭に行番号を付けたい」と言うケースでは、fscanfではかなり難しいですが、fgetsなら簡単です。

・(f)scanfでスペースを入力する方法はあります。%cを指定したり%[]で空白を含めるように指定したりできます。リファレンスマニュアルの「書式」をよく読んでください。
逆に(f)getsで1行読んでしまうと面倒なケースもあります。Cのプログラムのように、「1行」であることに意味の無いデータなら、読み込んだ分を「戻す」ような処理が必要でしょう。

・(f)scanfはいろいろと誤解されている関数です。たしかに、手を抜くと大変危険です。が、真面目に対処すればとても便利な関数です。このあたりは「scanfの誤解」で検索するといろいろみつかります。
何が危険かも考えずに安易に他の関数を使う方が危険です。scanf("%s",str)をやめて、空白までgetharで読み込む関数を自作したとしても、strの大きさを考えずに処理すれば、結局同じことになります。

> どのようなデータのときにどの関数を使うのが正しい、常識

言い方悪いですけど、慣れや経験で身に付けるしか無いと思います。
「AにはB」って定石があったとして、では、ちょっと変わったA'には「Bを少し変えたB'」なのか「別なC」なのかってことになりますから。
    • good
    • 0
この回答へのお礼

1文字入力・出力、
1行入力・出力、
ファイルから・標準入力から、
の違いはわかっていたのですが使い分けがわかりませんでした。

今までの回答を見させていただいてふと疑問に思っていたのですが、
なぜそんな危険な関数をC言語の入門で最初に教えられるんでしょうかね?(笑)
自分はscanfとprintfを最初に教えられたので。

数をこなして身につけようと思います。

お礼日時:2011/08/28 10:03

gets はバッファオーバーフローを防げないので、絶対に使ってはいけない。

→ fgets を使う。
scanf は使い方が非常に難しいので、使うケースはまずない。特に初心者には絶対に使わせてはいけない。→ fgets と sscanf を使う。もしくは fgets とほかの関数の組み合わせ。

fscanf,sscanf にしても書式指定の考慮漏れが無いかのテストが大変なので、十分心して使う。ほかに手があればそちら。

ほかの関数は適材適所でしょうか。
    • good
    • 0
この回答へのお礼

No1の回答者の方も初心者はfgets と sscanfがいいとおっしゃっていたので
やはりこれを使う人が多いのですかね!
getsはだめなんですね(´A`;)
ありがとうございました。

お礼日時:2011/08/28 09:51

printfとscanfは絶対に使いません。



ファイルから何かを読み込む場合はfgets()とsscanf()の組み合わせ。

いわるゆprintfデバッグをする場合もfprintf()を出力先:stderrで使います。

あとはたまにsprintf()を使うくらいでしょうか。

処理をある場所で意図的に止めたい場合なんかにgetchar()を使ったりもします。
    • good
    • 0
この回答へのお礼

具体的な組み合わせを教えていただきありがとうございます。

お礼日時:2011/08/28 09:46

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!