質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.49%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

if

if文とは様々なプログラミング言語で使用される制御構文の一種であり、条件によって処理の流れを制御します。

Q&A

6回答

2333閲覧

c言語のif文について

triagain23

総合スコア9

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

if

if文とは様々なプログラミング言語で使用される制御構文の一種であり、条件によって処理の流れを制御します。

0グッド

1クリップ

投稿2018/06/21 13:53

編集2018/06/21 13:54

c言語のif文の条件について
最近c言語の勉強をし始めたものです
以下のようなif文の条件の書き方を教えてください。

キーボード入力(key)から
自然数を入力→
● a を入力で処理1へ
● b を入力で処理2へ
● c を入力で処理3へ


入力:10a →処理1
入力:80c→処理3
入力:5b→処理2

これに合うような条件文はどう書けばよろしいでしょうか。
if(key==数字)

if(key==a)

処理1;


↑をa,b,cの3つ作るのでしょうか?
もっと簡単な書き方があれば教えてください

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答6

0

こんなふうに出来そうです。

C

1int num; 2char ch; 3 4int ret = scanf("%d%c", &num, &ch); // 一時変数retは省いても良いが、可読性はやや落ちる 5if(ret != 2) { 6 exit(1); 7} 8 9switch(ch) { 1011}

10abというような入力は考慮していませんのでご注意ください。

scanfを使う際に注意すべきことについて

上記のコードで、危険が生じ得る部分は次の二か所です。


オーバーフローを感知できないこと
例えば10000000000000000000000000aなどという入力に対しては未定義の動作となります。

業務で使うコードでは避けるべきです。しかし、個人で使う分には無視して良いように思います。
悪意を持った人でない限りそんな入力しないからです。

入力の最大桁数を決め打てるのなら、次のように書いても回避できます。

C

1scanf("%2d%c", &num, &c);

バッファに食べ残しができること
10abと入力するとb\nが、10aと入力すると\nがバッファに残ります。
入力処理を何度かにわたって行う場合に注意が必要です。

標準入力をクリアするためのコードとしてfflush(stdin)がよく紹介されています。
しかし、これは規格上未定義の動作を引き起こす(らしい)ので、使うべきでないです。
きじねこ - [迷信] fflush で入力バッファをクリア

改行文字まで空読みするのが確実な方法です。

C

1while(getchar() != '\n');

結局のところ

  • fgets + strtol でがっつりセキュアなコードを書くか
  • scanf でお手軽に書くか

どっちかじゃないですかね。中途半端なのが一番良くないと思います。

書いてみた:比較的安全なコード

調べながら書きましたが、これでも見落としがある気しかしないです。

参考

C

1#include <ctype.h> 2#include <errno.h> 3#include <stdio.h> 4#include <stdlib.h> 5#include <string.h> 6 7int main(void) { 8 char buffer[14 +1+1] = {0}; // + newline + null 9 10 // get input 11 { 12 char *ret = fgets(buffer, sizeof(buffer), stdin); 13 if(ret == NULL) { 14 fprintf(stderr, "Failed: At fgets.\n"); 15 exit(1); 16 } 17 18 char *nl = strchr(buffer, '\n'); 19 if(nl) { 20 *nl = '\0'; 21 } 22 else { 23 while(1) { 24 int ret = getchar(); 25 if(ret == '\n') { 26 fprintf(stderr, "Failed: Too long input.\n"); 27 break; 28 } 29 if(ret == EOF) { 30 fprintf(stderr, "Failed: Input must end with a linefeed.\n"); 31 break; 32 } 33 } 34 35 exit(1); 36 } 37 38 if(strlen(buffer) == 0) { 39 fprintf(stderr, "Failed: Empty input.\n"); 40 exit(1); 41 } 42 } 43 44 int num; 45 char type; 46 { 47 char *begin = buffer; 48 char *end; 49 50 // parse integer 51 errno = 0; 52 const long sl = strtol(begin, &end, 10); 53 54 if(begin == end) { 55 fprintf(stderr, "Failed: Integer parsing.\n"); 56 exit(1); 57 } 58 if( 59 // NOTE: これ errno 見るだけじゃダメなのだろうか...? 60 (sl == LONG_MIN || sl == LONG_MAX) && errno == ERANGE 61 ) { 62 fprintf(stderr, "Failed: Overflow is occurred.\n"); 63 exit(1); 64 } 65 if(sl < INT_MIN) { 66 fprintf(stderr, "Failed: %ld is less than INT_MIN\n", sl); 67 exit(1); 68 } 69 if(INT_MAX < sl) { 70 fprintf(stderr, "Failed: %ld is greater than INT_MAX\n", sl); 71 exit(1); 72 } 73 74 num = sl; 75 begin = end; 76 77 // parse type 78 if(strlen(begin) != 1) { 79 fprintf(stderr, "Failed: Invalid format.\n"); 80 exit(1); 81 } 82 83 type = *begin++; 84 85 // 文字コードが連続していると仮定: 86 // http://www.kijineko.co.jp/tech/superstitions/A-to-Z-is-sequence.html 87 if ('A' <= type && type <= 'Z'); 88 else if('a' <= type && type <= 'z'); 89 else { 90 fprintf(stderr, "Failed: Invalid type character '%c'.\n", type); 91 exit(1); 92 } 93 } 94 95 printf("num: %d, type: %c\n", num, type); 96 97 return 0; 98}

『不正な入力がない』と仮定できるなら、これは次のコードに置き換えられます。

C

1#include <stdio.h> 2 3int main(void) { 4 int num; 5 char type; 6 7 scanf("%d%c", &num, &type); 8 printf("num: %d, type: %c\n", num, type); 9 10 return 0; 11}

これに気休めでちょっとだけエラーチェックを加えると、回答先頭のコードになるわけです。

投稿2018/06/21 14:02

編集2018/06/23 17:04
LouiS0616

総合スコア35660

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2018/06/21 23:08

入力はscanfではなくfgetsにして(最後の改行を消して)、strcmpで文字の比較をした方がいいと思います。scanfは色んな意味で危険な関数のうえ,strcmpで比較すると10abのような文字の対処もできます。
LouiS0616

2018/06/22 00:15 編集

以前もどこかで書きましたが、どこまで異常な入力について考慮するか検討する必要があります。 悪意のある入力にまで対応しようと思うと可読性を犠牲にしてしまいます。 初心者のうちはscanfで充分だと思っています。
退会済みユーザー

退会済みユーザー

2018/06/22 00:40 編集

危険なscanfを初心者にすすめるのはよくないと思います。(いろいろあるがここでは略) 自分だけが試すテストプログラムならともかく,他人に見せるプログラムならscanfは使うべきではありません.(同じ理由でfgetsなら良いですがgetsは使えません.) それに、そこまで可読性は落ちません。
LouiS0616

2018/06/22 00:47 編集

> いろいろあるがここでは略 省略しないで、Stars1024さんの言葉で懸案すべき事項を説明してください。 --- > それに、そこまで可読性は落ちません。 実際に比較した方が早いです。 先の懸念を全て解決、あるいは妥当に対処できる代替コードをご提示ください。
退会済みユーザー

退会済みユーザー

2018/06/22 01:05

scanfの危険性 1.scanf関数の使い方を間違っても 例えばscanf("%d",n);と&を書かなくてもコンパイル時にエラーが検出されず実行したときにはじめてコードが間違っていることがわかる。 2.scanfで文字列入力をした場合,参照渡しで渡した文字配列の領域を越えた入力が可能なため,バッファオーバーフローまたはヒープオーバーフローの「セキュリティホール」を作り込むことになる. (例) char buf[2]; scanf("%s",buf); printf("%s",buf); で文字列が2以上の"123"なども受け付けてしまう。 なお、コードの方は私の回答があるはずです。コード全体を書いたので長く見えるだけであり、 実際、入力 -> 分類は for(i = 0; i < 3; i++){ printf("%sを入力 -> 処理%d\n",text[i],i + 1); } fgets(buf,sizeof(buf),stdin); buf[strlen(buf) - 1] = '\0'; //最後の改行を削除 for(i = 0; i < 3; i++){ if(strcmp(buf,text[i]) == 0){ type = i + 1; } } までです。
pepperleaf

2018/06/22 01:11

勉強用のプログラムにどこまで悪意への対応が必要なんでしょうか? そもそも、C言語を使った時点で、かなり危ない気がします。 LouiS0616さんの書かれたように最初は、シンプルな方が良いと思います。
LouiS0616

2018/06/22 04:01 編集

@Stars1024 さん --- > 1.scanf関数の使い方を間違っても 例えばscanf("%d",n);と&を書かなくてもコンパイル時にエラーが検出されず実行したときにはじめてコードが間違っていることがわかる。 Wallオプションを付ければ警告が出ます。 警告を無視すれば 数値 ⇔ ポインタ で好き放題出来るのはC言語全体に言える話です。 --- > 2.scanfで文字列入力をした場合,参照渡しで渡した文字配列の領域を越えた入力が可能なため,バッファオーバーフローまたはヒープオーバーフローの「セキュリティホール」を作り込むことになる. 今回のコードは文字列は受け取っていませんので、その指摘には当たりません。 文字列を扱う場合もフォーマット文字列の工夫次第でバッファオーバーランは回避できます...が。そのような場面では私もfgetsを優先すべきかと思います。 --- > コードの方は私の回答があるはずです。 入力を数値化していないじゃないですか。
LouiS0616

2018/06/22 03:57

@pepperleaf さん ですよね。 また、ネット上でもscanfを用いているサンプルが広く見られることを考えると、少なくとも読めるようになるまではフォーマット文字列に慣れる必要があるように思います。
cateye

2018/06/22 11:27 編集

横槍です・・・私はfgets(),sscanf() 派(w?)なのですが、初心者の方の質問でよく問題になるのがscanf()の取り残し(バッファに残る改行など)を再度読み取り込もうとして失敗する現象です。(これは教育する側、あるいは参考書側にちゃんと説明が成されていない等、落ち度がある場合が多いと思いますが)従って、scanf()を否定するのではなく、ちゃんとした使い方を説明した上で使用するように諭す必要があると思います。ただ「危険だから」と言うだけでは教わる方も納得いかないと思いますので、なぜ使わないほうがいいのか? また、使った場合には〜〜に注意するように等、一言添えるべきではないかと思います。恥ずかしながら、私はscanf()の最新の仕様は把握していませんので、いらぬ事を言っているようなら御指摘ください。
LouiS0616

2018/06/22 11:47

@cateye さん > 使った場合には〜〜に注意するように等、一言添えるべきではないかと思います。 ご意見ありがとうございます。 私の書いたコードの穴について追記しておきました。
退会済みユーザー

退会済みユーザー

2018/07/21 03:36 編集

******************
退会済みユーザー

退会済みユーザー

2018/07/21 03:36 編集

******************
LouiS0616

2018/06/23 09:52

@Stars1024 さん scanfが危険ということ自体は間違いないです。 ただ、それなら『危険!危険!』と主張するだけでなく、安全なコードを書いてください。 本当に安全なコードを書くのは非常に難しいですから、入力はさくっと受け取って他の学習に力を注いだ方が良い、というのが私の主張です。
LouiS0616

2018/06/23 10:03

ついでに。Stars1024さんはしばしば atoi を使われていますが、それ、危険です。 危険性を鑑み scanf を使わないという方針ならば、 atoi も使わない方が良いです。 https://www.jpcert.or.jp/sc-rules/c-int06-c.html
退会済みユーザー

退会済みユーザー

2018/07/21 03:36 編集

******************
asm

2018/06/23 10:44

scanfのバッファに1文字残す問題は、stdinのバッファを全部消す方法を標準化してくれりゃいいのに 複素数なんぞよりよっぽど求められてるハズなんだが
LouiS0616

2018/06/23 17:11

@asm さん 裏で動かすものだと割り切った方が良いような気もします。。。 私はCで入出力は絶対したくないですね。アマグラマなのでこんな選り好みが出来るわけですが。
guest

0

if condition is like a gatekeeper or guard.
if(number > 0 ) // then come in
else
// get out

char gate = '\0';
gate = getche();
or try this
scanf("%c",&gate);

if(gate == 'a') // process 1
else if(gate == 'b') //process 2
else if(gate == 'c') // process 3

投稿2018/06/22 17:30

AliHassan

総合スコア351

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

if文の書き方だけで言えば、それほど簡単にはできないと思います。
if・・・ else if ・・・
の連続になります。
3個ぐらいなら、switch も if も同じようなものでしょう。

入力可能な数字が「小さくて、とびとびな値ではない」場合は、関数ポインタの配列を使う方法があります。
「小さくて、とびとびな値ではない」とは、入力可能な数字が
1, 30, 5000
とかではなく、
1, 2, 3
のような場合です。

関数ポインタの配列の例
この例では、四則演算の関数を呼び出しています。(128個の要素を持つ配列を作っていますが)

投稿2018/06/22 01:35

nob.

総合スコア711

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

C

1if(key==a) 2 3```は、変数keyを変数aと比較するif文ですよ。aという文字は 'a' です。 4 5 6分割して考えてください。 71.キーボードから数字列に続いてアルファベットが一文字入る前提。 82.数字列部分を10数値とみなしてint変数numに入れる 93.続くアルファベット1文字をchar変数keyに入れる 104.処理分岐 11 12処理分岐のところはこれでいいでしょう。 13 14```C 15if(key=='a')16 処理1; 17else if(key=='b')18 処理2; 19else if(key=='c')20 処理3; 21else22 想定外の入力の処理 2324

elseをつけておかないと、処理1の中でkeyの値を変化させて'b'や’c’に変えてしまった場合に処理2や3が引き続いて実行されてしまいます。
分岐が今後もっと増えるのならswitch文の習得をお勧めします。

投稿2018/06/22 00:57

a_saitoh

総合スコア702

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

こんにちは、私が試した方法の説明をします。
まず初めにchar text[3][10] = {"10a","5b","80c"};を用意します。
1.fgetsで文字列の入力を受け付ける。
2.最後の改行を消す。
3.strcmpで比較し、一致したら変数typeに(その番号 + 1)を代入する。
例えば入力した文字が10aだったらtype = 1になります。
4.typeに応じた処理をswitch文で処理する。
これは入力した文字が3つのどれとも一致しなかった場合にdefaultで対処するためです。

以下、私が書いたソースコードを載せておきます。

C

1#include <stdio.h> 2#include <string.h> 3 4#define BUFSIZE 1024 5 6int main(void) 7{ 8 char text[3][10] = {"10a","5b","80c"}; 9 char buf[BUFSIZE]; 10 int i; 11 int type = -1; 12 13 for(i = 0; i < 3; i++){ 14 printf("%sを入力 -> 処理%d\n",text[i],i + 1); 15 } 16 fgets(buf,sizeof(buf),stdin); 17 buf[strlen(buf) - 1] = '\0'; 18 for(i = 0; i < 3; i++){ 19 if(strcmp(buf,text[i]) == 0){ 20 type = i + 1; 21 } 22 } 23 switch(type){ 24 case 1: 25 printf("処理1\n"); 26 break; 27 case 2: 28 printf("処理2\n"); 29 break; 30 case 3: 31 printf("処理3\n"); 32 break; 33 default: 34 printf("何もしない\n"); 35 break; 36 } 37 return 0; 38}

投稿2018/06/21 23:05

編集2018/06/24 09:13
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

LouiS0616

2018/06/21 23:50

> 自然数を入力→ > ● a を入力で処理1へ > ● b を入力で処理2へ > ● c を入力で処理3へ 『20a』などと言う入力にも対応する必要があります。
can110

2018/06/22 08:32 編集

回答コードにおいて (1) EOFのみを入力すると、意図しない危険な動作をします。 →[FIO37-C. fgets() や fgetws() が読み取り成功時に空でない文字列を返すと想定しない](https://www.jpcert.or.jp/sc-rules/c-fio37-c.html) (2) 255文字以上入力すると、最終結果には影響しませんが、途中、意図しない動作をします。 →[FIO36-C. fgets() が改行文字を読み取ると仮定しない](https://www.jpcert.or.jp/m/sc-rules/c-fio36-c.html) fgets(やstrlen)も使い方によっては危険です。
退会済みユーザー

退会済みユーザー

2018/06/23 10:11

@can110さんへ (1)入力の際Enterを押すので必ず一文字以上入力されますが、一応if文でNULLの時の処理をしました。 (2)ポインターを使えばいいのでしょうか?
can110

2018/06/23 16:20

「ポインタを使う」というのがよく分かりませんが リンク先には詳しい説明や適合コードが記載されていますので確認のうえ判断ください。 なお、LouiS0616さんが回答で指摘されている「バッファの食べ残し」の問題はfgetsでも発生し、これに対処するのは意外と大変です。
rubato6809

2018/06/23 23:34

Stars1024さんのお手元では動作するんですか? 入力バッファへのポインタ char *buf; を初期化しないままfgets(buf, …); してるから大抵コアダンプしますけど。
退会済みユーザー

退会済みユーザー

2018/06/24 09:02

@rubato6809さんへ 私はCを動かすときコマンドプロンプトを使いますが、エラーは出てきませんでした。 でも「たいていコアダンプする」とおっしゃったのでポインタを使わないことにしました。
退会済みユーザー

退会済みユーザー

2018/06/24 09:17

@can110さんへ (1)fgetsは入力が空白の時、動作がおかしくなることはわかったのですが、fgetsで入力するときは 必ずEnterキーを押し、その際必ず1文字以上の文字ができるのでエラーは出ないと思うのですが... (実際入力の時Enterキーだけを押しましたがエラーは出ませんでした。) (2) 一応1024に増やしました。  また、「バッファの食べ残し」についてどういうことかはわかりましたが、私が書いたソースコードでは エラーは出ませんでした。どんな入力をすると「バッファの食べ残し」に関する問題に直面するのですか?
rubato6809

2018/06/24 10:16 編集

Starts1024さん 動いてましたか。動いたとしても、以前のコードは明確なバグです。どなたも言及されなかったのが不思議な位です。メモリのどこかを破壊していた可能性が大いにあります。 char buf[BUFSIZE]; は正解です。配列にすれば問題ありません。 1000バイトなんて必要無いとは思いますけど。
rubato6809

2018/06/24 11:03 編集

バッファの食べ残しとは、用意したバッファより長い一行が入力された場合に起こります。 fgets(buf, size, fp) は最大 size バイトまで一行を読み込んでくれます。ここで、タイプした一行が size バイトより長い場合、納まりきらなかった'\n'までの文字がストリーム fp に残ってしまいます。そして、次回 fgets() などをした時に、それが読み出され、おかしな動きに見えてしまうわけです。 fgets() で読み込んだバッファ中に '\n' が無いなら食べ残しがあります。その場合は、’¥n’を読みだすまで fgets() するとか、fgetc() してストリームをクリアすればよいと理解してます。
退会済みユーザー

退会済みユーザー

2018/06/25 05:12

詳しい説明ありがとうございます。要するに、入力する文字列が256以上でfgets()を複数使うときにエラーが出るということはわかりました。 (ということは入力する文字列が256未満の時は大丈夫)
can110

2018/06/25 09:12

> (1)fgetsは入力が空白の時、~ > (実際入力の時Enterキーだけを押しましたがエラーは出ませんでした。) いえ、EOFを入力したときです。Windows環境であれば[Ctrl]+[z] と [Enter] を入力した場合です。 この場合、bufの中身は不定となり、strlen(buf)も意図しない結果になり、大抵はその後メモリ範囲外を参照してしまいます。
guest

0

キーボードからの入力 fgets 関数
文字列から整数への変換 strtol関数


ピント外れついでに。
無理してif使わずに、switch使いましょう
ちょっとはスッキリします

C

1switch(key){ 2case 'a': 3 // 数字の処理A 4 break; 5case 'b': 6 // 数字の処理B 7 break; 8case 'c'; 9 // 数字の処理C 10 break; 11default: 12 // aでもbでもcでもない場合 13 break; 14}

投稿2018/06/21 14:00

編集2018/06/22 01:54
y_waiwai

総合スコア87747

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

y_waiwai

2018/06/21 14:04

またおまえか案件なのか。。
y_waiwai

2018/06/22 01:38

ああ、tetatailのバグなんですね。 酷いバグだなあ。。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.49%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問