# みんなで頑張るプロ言テスト対策 * このテキストはC言語のテスト対策用のまとめで、分かりやすくするため厳密ではない部分があります。 * このテキストは印刷して持っていってはいけないので、内容を理解しながらノートに取りましょう。 * このテキストに書いてあることが正しい保証はできません、自己責任でどうぞ * **20/1/19追記** 新たにテスト範囲に追加された命名規則(キャメルケース等)についての項目を追加しました。 * **20/2/10追記 予想問題ページを作成しました。** Moodleのテスト概要をもとに勝手に予想して作っています。どうぞ https://hackmd.io/@sakarush/ryVz1tCfI * **20/2/12/追記** snake_caseについて、snake_caseは全て小文字にするのが主流らしいです * **20/12/19追記** 北先生の課すコーディング規約について、一部適切でないものがあります。北先生のMoodle小テストは一字一句先生の想定と違うと点がもらえない仕組みなので、コーディング規約を詳しく確認してください。 # 1. 条件分岐編 ## if 文 if 文は条件分岐するのに使う。 * 使い方 ```c if (条件式) { 実行する内容 } ``` * 例 ```c if (x == 1) { printf("x is 1!!!!!!"); } ``` ## else 文 ifの内容を満たしてなかったとき(そうでなければ)のためのelseという構文 * 使い方 ```C if (条件式) { 実行する内容 }else{ そうでなかった時に実行する内容 } ``` * 例 ```C if (x == 1) { printf("x is 1!!!!"); }else{ printf("x is not 1!!!"); } ``` ## else-if 文 ifの内容を満たしてなかった上で、さらにifを重ねたい場合にはelse if というものを使う。 else if の後ろにelseを使うこともできる。 * 使い方 ```C if (条件式){ 実行する内容 }else if (条件式){ 最初の条件式ではない上で、次のifにはひっかかった時に実行する内容 }else{ どれにもひっかからなかった時に実行する内容 } ``` * 例 ```C if (x == 1){ printf("x is 1!!"); }else if (x == 2) { printf("x is 2!!"); }else{ printf("x is not 1 or 2!!"); } ``` ## switch文 ifに似ているが、複数のケースについて一括で条件分岐を書く * 使い方 ```C switch(見る変数) { case 数: 実行する内容; break; case 数: case 数: 実行する内容; break; default: どれにも当てはまらなかった時に実行する内容; } ``` * 例 ```C switch(x) { case 1: printf("x is 1"); break; case 2: printf("x is 2"); break; case 3: printf("x is 3"); break; default: printf("x is not 1 or 2 or 3"); } ``` case 数:やの後ろには **break;** を忘れないようにしよう default:の後ろには要らない。 # 2. 繰り返し編 ## while whileは最も単純な繰り返し構文 条件式の間が正しい(TRUE,真)の間はずっと実行される * 使い方 ```C while(条件式){ 実行する内容 } ``` * 例 ```C int x = 0; while(x < 100) { x++; printf("KUSA"); } ``` このプログラムではKUSAという文字が連続で100回表示される。 これはxを1ずつ足していって、x<100じゃなくなるまで(x >= 100まで)繰り返すからだ。 ## for文 forは、whileのよく使う回数を記録するような動作をまとめて書く書き方だと思えばよい。 つまり、上の例でやった``int x = 0;``とか`` x < 100`` とか ``x++;``とかをまとめて書く書き方なのだ。 * 使い方 ```C for (初期化;条件式;ループの毎回行う処理){ 実行する内容 } ``` * 例 ```C for (int i = 0; i < 100; i++){ printf("KUSA"); } ``` このプログラムは上のwhileの例と全く同じである。 しかしこっちのほうが綺麗に書けるし、よく使う ## do-while文 do-while文はwhileの特殊版。 while文は最初に条件式を満たしてなかったら一度も実行されないが、do-whileはされる。 例えば次のコード ```C int x = 0; while (x == 1) { printf("KUSA"); } ``` このコードはxに0を入れていて、whileには(x == 1)を指定した、つまり条件式を満たしておらず、一度も実行されない(悲しみ) しかし、do-whileを使えば一度だけは実行してくれる ```C int x = 0; do { printf("KUSA"); }while (x == 1); ``` 一度だけは「KUSA」と表示してくれるのだ。 ちなみに、最後のwhileの後のセミコロン;を忘れないようにしよう。 ## 演算子の優先順位 演算子っていうのは+とか-とか==とか諸々の記号 これらの優先順位についてテストに出るかも 例えば \*(掛け算)は\+(足し算)より優先順位が高い この表によれば、 10 + 30 * 2 > 10 は * + > の順番に演算子が実行される。 # 3. 変数編 ## 変数について 変数はアルファベットで名前を付ける。 基本的にどんな名前を付けてもいいけど、「int」とかほかの意味で使われてる名前は避けるべき * 例 ```C int x = 3; int Aho = 4; ``` 変数には型がある。 例えばintっていう型なら整数(Integer)っていう意味 テストで出るような変数は以下の通り |型名|語源|意味|使用例| |:-:|:-:|:-:|:-:| |int | Integer(整数) | 整数の変数|int x = 100 | |char|Character(文字)| 1文字| char a = 'A'| |float|float(浮動小数)| (精度が小さめの)少数| float x = 1.111 | |double|double(倍精度) | (floatより2倍精度の高い)小数 | double x = 1.111111| ※floatを使う意味は今日あまりないので、基本的にはdoubleを使いましょう。 文字は 「'」シングルコーテーションで囲って表す * 例 ```C int x = 10 double ensyuritu = 3.141592; char c = 'S'; ``` ※文字はアルファベットや記号1文字しか表せない。 例えば char x = 'A'; は問題無いが、char x = 'AA'; や char x = 'あ'; は許されない。 このように、2文字以上の集まり(日本語は2文字分で1文字を表す)のことを **文字列** と言う。 文字列は 「'」ではなく「"」で囲う。ちょうどprintf("kusa")とかの「"」だ。 そして、まだ授業では文字列の変数の扱い方を習っていないので **扱えない** 。 ```C 発展内容:文字列と配列変数 来年以降、もしくは今年も時間があれば配列変数というのを学ぶだろう。 配列変数とは簡単に言えば沢山の変数を一つにまとめて扱うものだ。 例えば1000個のcharのデータをまとめて扱えば、1000文字分の文字列が入れられるデータとなる。 (厳密には最後のchar変数は、文字列の終了を示す終了コード"\0"が入っているので999文字だが) この時、 char x[1000]; とすると1000個のchar型変数が用意出来る。 そしてx[0] で1文字目, x[99]で100文字目、x[i]でi文字目を取り出したり、代入したり出来る。 (1000個のデータを用意した時は 0から 999までの1000個を用意することに注意しよう) もし1000文字全てをA で埋めたいならば char x [1000]; for (int i = 0; i < 999;i++) { x[i] = 'A'; } x[999] = '\0'; とすれば良い。 余談だが、 char x[10] = "KUSAHAETA"; とすると自動的に'K' 'U' 'S' ... 'A' という配列変数を作ってくれる。 ちなみにこれらはテスト範囲では無いので、いつもprintfで使う "KUSA" などを変数にしまう場合には、 変数の集まりである配列変数が必要なんだな、と思っておけば十分。 ``` ## キャストについて 例えばfloat型(小数)の数値をint型(整数)に変えたい! そんなときに行うのが、キャスト(型変換) 例えば 1.1111という小数は、1という整数に変換される。 ちなみにキャストは使い方をミスったりしてエラーが起きることが多いので、本来はあんまり使ってはいけない。 * 使い方: キャストしたい値の前に、キャストする型をかく ```C (型名)変数名 ``` * 具体例: ```C double x = 1.111111; int y = (int)x; ``` これでyは1.111を整数に変換した、結果としてyは1になる。 キャストのエラーについて ``` 発展内容: intよりも大きな桁を保存できるlong long int型というものがある。 intは大体10桁くらいまで扱えて、long long intは20桁くらい扱える。 例えばlong long intは 999999999999 という数を扱えるが、 intでははみ出してしまう。 そしてこれを無理やりキャストしようとすると、わけのわからない値になったり、エラーになったりする。 このようにデータがはみ出す現象を「OverFlow(オーバーフロー)」という。 ゲームのバグとかで聞いたこともあるのではないだろうか。 とにかく、大きな範囲の型から小さな型へキャストするときは、危険なのだ(int < long long int) (char < int) ``` # 4. 入出力編 ## printf関数 printf はメッセージを出力(表示)するやつ 基本的な使い方は文字列といわれるメッセージ(""で囲まれたやつ)を入力する 文字列には何を入れてもいい。 * 使い方 ```C printf("文字列",変数1,変数2,...); ``` 変数1,変数2については次の「書式指定子」で説明する。 まずはふつうの使い方 * 例 ```C printf("kusa haeta"); printf("President Trump"); ``` などなど... しかし、決まった文ではなくて変数を表示したいときは次の工夫が必要となる。 ## 変数の表示 書式指定子というのはprintfの文字列の中に入れる「%d」とかのこと。 「%d」の部分が変数の中身に置き換えられると考えればOK * 例 ```C int x = 100; printf("kusa %d haeta",x); ``` これを実行すると ``` kusa 100 haeta ``` となる。これは%dの部分が100に置き換えられたからだ。 複数の変数を置き換えたいときは、たくさん書けばOK * 例 ```C int x = 100; int y = 999; printf("kusa %d haeta %d",x,y); ``` これを実行すると ``` kusa 100 haeta 999 ``` と表示される。左の%dから順番に100と999に置き換えられたのだ。 ## 主な書式指定子 大体いつも表示しているのがint(整数)だけだから「%d」を使うが、場合によっては変えなければいけない。 例えば小数を使うときは%f(%lf)、文字を使うときは%cとなる。 簡単に表にしておく |書式指定子 |変数の型| 意味 | |:-:|:-:|:-:| | %d |int | 整数 | | %f |float | 精度の低い少数| | %lf | double | 精度の高い小数| | %c | char | 文字| * 例 ```C int a = 100; double ensyuritu = 3.141592; char mozi = 'A'; printf ("a is %d",a); printf("ensyuritu is %lf",ensyuritu); printf("mozi is %c",mozi); ``` これらを実行するとこうなる ``` a is 100 ensyuritu is 3.141592 mozi is A ``` それぞれ対応する小数や文字に置き換わっている。 ## 桁数指定(発展,出るかわかんねえ) さっきの例では円周率を7桁まで表示した。 書式指定子では表示する桁数を変えられる。 * 使い方 ```C %桁型 ``` * 例 ```C double ensyuritu = 3.14159265; printf("%3lf",ensyuritu); ``` これで実行すると次のようになる ``` 3.14 ``` つまり,%とlfの間に3という数字を入れることで、表示する桁数を3桁までに抑えてくれたのだ。 このほかにも便利な指定方法があるので、気になったら調べてみて「C言語 フォーマット指定子」で検索 * [メモ] 少数には %f を使うべきか %lf を使うべきか ``` 先に述べたように少数はfloatとdoubleの2つの型で表せて、doubleの方が精度が良い。 そしてdoubleを表示する場合には %f でもバグらないのだが %lf が望ましい。(多分精度を落とさずに表示する) しかし、テストにおいて悩ましいのはC言語の絵本には %lf は乗っていないのだ。 ならば、教科書の内容に従って float を使って %f を使えばいいではないかと思うかもしれない。 それはそう、なのだが、工学部教室での授業では先生は「まあ、この時代floatを使う利点などないので、doubleを使ってくださいね」と言っていた。 これはとても悩ましい問題なのだ。 doubleと %lf を使うべきか、 float と %f を使うべきか、 double と %f を使うべきなのか.... 正直な所、私も迷ってます。 ``` --- ## scanf関数 さっきはprintfで出力を学んだけど、次は入力を学びましょう。 入力にはscanfを使う。 * 使い方 ```C scanf("書式指定子",&変数名); ``` これで変数に入力された値をしまえる(代入できる)。 * 具体例 ```C int x; scanf("%d",&x); ``` これで、入力した値がxに代入される。 注意するべきなのは **必ず変数名の前に&をつけること** 。 これは、テスト範囲ではscanfでしか使わないですが、忘れると絶対減点されます。 なぜ&が必要なのかを一応書いておきます ```C 発展内容 将来的にポインタという概念を習うときに一緒にならうでしょう。 ふつう、printfみたいにxをそのままかいた場合は、それはxの中身の数字を指します。 例えば int x = 3;でxに3を代入したら、 printf("%d", x); では、xは3という値に変わります。 しかし、このscanfの場合はこの変数に入力されたデータを入れたいのであって、 変数の数値を指したいわけではありません。 このとき、&をつけるとxの中身ではなく、xそのものを指すようになります。 これによって、xに値が代入できるのです。 このように、xそのものを指す、具体的にはxの変数があるアドレスの数値のことをポインタといい、 C言語の初心者の学習上での、最難関事項です。 今回は知らなくても大丈夫ですが、なんにせよ変数名の前につける&は忘れてはいけません。 ``` これでテスト範囲の基本的な内容は十分です。 ここからはそのほかの説明とテスト時のチェックポイントを書いておきます。 # 5. キャメル方式等の命名規則 テスト範囲に新たに追加されたので追記しました。 ## 変数の名前を適切につける プログラムというのは、大規模になればなるほどわけが分からなくなるものです。 そして、変数の名前は何でもいいとは言ったのですが、分かりやすく名前をつけるとコードも見やすくなります。 例えば次の例を見て下さい * 例1. 変数の名前の付け方が適切な場合 ```C int sum = 0; for (int i = 1; i <= 10; i++){ // 10まで1個ずつ足していく sum = sum + i; // sumの内容にiを足していく } int average = sum / 10; printf("sum: %d, average: %d",sum,average); ``` * 例2. 変数の名前の付け方がゴミな場合 ```C int a1 = 0; for (int i = 0; i <= 10; i++){ a1 = a1 + i; } int a2 = a1 / 10; printf("sum : %d, average: %d",a1,a2); ``` どちらも全く同じコードで、1から10までの和と、その平均を求めるプログラムです。 上の例では変数名はそれぞれ"sum(和)"とか"average(平均)"とか分かりやすい名前をつけましたが、2つめの例では"a1" とか"a2"とかです。 やっぱり名前でわかるほうが良いですよね。 もちろん、例2も全く同じ動作をしますが、人間にとって読みやすいコードはやっぱり例1です。 ## 複数の単語をつなげた場合 私達がいつも使っているprintf()関数ですが、将来的にこういう関数を自分で作ることが出来るようになります。 そして例えば次の動作をする関数を作るとしましょう ``` 店員(Shopkeeper)の給料(Sammary)の全て(All)を削除(Delete)する ``` C言語や、特に先生の意識するJavaではよくこの単語全てをつなげて一つの関数名とします。(Javaではメソッドと呼ぶ別のものを作るときのほうが多く、また話が変わりますが) とにかく上の関数の名前をそのまんまつけてみましょう。 すると、次のようになるかもしれません ``` deleteallshopkeepersammary(); ``` なんて分かりにくい名前でしょう。 これを少しでも見やすくしよう、ということでみんなで決めるルールが **命名規則** です。 そして先生が調べてこいと言ったのはこの命名規則です。 一つずつ見ていきましょう。 ## パスカル形式(パスカルケース、アッパーキャメルケース) これは全ての単語の先頭を大文字にしようという方針です。 例えば先程の例はこうなります。 ``` DeleteAllShopkeeperSammary(); ``` 幾分か見やすくなりましたね。 ## キャメル形式(キャメルケース、ローワーキャメルケース) これも単語の先頭を大文字にしようというルールですが、最初の単語だけは大文字にしません。 例えば先程の例だとこうなります。 ``` deleteAllShopkeeperSammary(); ``` 実は先程のパスカル形式は、キャメル形式の一部で「アッパーキャメルケース(形式)」と言います。 つまり パスカル形式 = アッパーキャメル形式 で、今回のやつを ローワーキャメル形式と呼び、その2つの総称をキャメル形式と言うそうです。 多分先生の用法ではこのローワーキャメル形式をキャメル形式と呼んでも良いのでしょう。 Javaのメソッドと呼ばれる関数っぽいやつにはよくこのキャメル形式が使われているように思います。 ## スネーク形式(スネークケース) これは単語間をアンダーバー「\_」でつなぐという形式です。 ~~単語は大文字でも小文字でも良いそうです~~ **追記** 一応小文字にするのがスネーク形式らしいです(しらんけど) 例 ``` delete_all_shopkeeper_sammary(); ``` データベースで扱う値だったり、統計用プログラミング言語での値だったりでよく見る気がします。 変数とか定数(円周率とかもう変えない数)、データ構造の名前によく使う気がします。 ## まとめ こういう名前の話は、別に守らないからプログラムが動かないとかいうものではありません。 だって変数の名前は一定のルール(最初が英字で、他は英数字とか)を守れば"iojfeoji"とかいうめちゃくちゃな名前でも大丈夫なのですから。 あくまでこれは**人間が見やすくするための方針**でしかありません。 一応まとめておきましょう |名前|別名|例|方針| |:-:|:-:|:-:|:-:| |パスカル形式(ケース)|アッパーキャメルケース|**P**ascalCase|全単語最初大文字| |キャメル形式(ケース)|ローワーキャメルケース|**c**amelCase|最初を除く全単語最初大文字| |スネーク形式(ケース)|知らん|snake_case|単語間にアンダーバーを入れる| ## おまけ ちなみに、さっき言った円周率とか変えない値を定義するときには全て大文字かつスネークケースで記されることが多いです。 例えば円周率は次のような名前がつけられています 見るからに定数って分かりますね ```C MATH_PI ``` # 6. その他 * '\n'というのは改行コードといって、改行することをさします。 * ``#include <stdio.h>`` は、Standard Input OutPut の略のstdio.hというデータを読み込んでいて、これのおかげでscanfやprintfが使えます。 * ``int main(void ) {}`` はプログラムが起動されたときに呼び出される場所という意味で、今習った範囲では、このmainの{}かぎかっこ中に書かないとプログラムは動きません。 * xに1つ値を足したいときはx++;で良いですが、好きなだけ足したいときは``x = x + 2``という式を使います。数学的にはやばいけど、C言語では``=``は右の値を左の変数に代入するという意味なので、これで大丈夫ですね。(イメージは=は左矢印と思えばいい x ← x + 2) * コード中に // と書くと、その行の後ろが意味のない文字としてみなされ、メモが書けます。 例: ```C // これはコメントだからなんでもメモ書ける int x = 3; // x に 3入れてやったぜえ! printf("%d",x) // xを表示する みたいなプログラムの説明をメモできる ``` * プログラムは最後の行まで行けば終了しますが、わざと終了させたい時にはreturn 0;を使います。 * = は代入演算子なので、 x = 3; のように使います、 逆に == は比較演算子なので、ifとかの中で使います。 これを間違えたり、ifの中で=を使うと大変なことになります。 * ダメな例と良い例 ```C ↓これやばいです、ダメです if (x = 3) {} } ↓これは良いです if (x == 3) { } ``` # 7. テストのときの見直し項目 * [x] 命令の後にはセミコロン ; はつけたか? * [x] {はちゃんと}で閉じているか? * [x] scanfの変数の前には&はつけたか?それ以外ではつけていないか? * [x] }の後にセミコロン ; をつけていないか?(つけてはいけない) * [x] switchのcaseやdefaultに、ちゃんとbreak;は書いたか? * [x] forの内容は間違えてないか?無限ループしないか? * [x] do while(); のセミコロンを忘れてはいないか? * [x] { }の中は字下げ(横にずらす)をしているか? * [x] if文の比較には == を使って、代入に = を使っているか? * [x] #include <stdio.h> は <studio.h>では無いぞ、間違えていないか? * [x] #include <stdio.h> の後ろにセミコロンをつける必要はない、つけていないか? * [x] 不等号の向きは間違えていないか? * [x] printfの 文字列 "%d"等 と 変数の間にはコロン「,」を忘れていないか? printf("%d",x); # 8. 便利なコード集(随時足していきます) ここのコードは基本的に おまじないのint main(void) {} のカッコの中に書いて下さい。 毎回おまじない書くのめんどくさいので省略してます **20/2/10追記 予想問題ページを作成しました。** Moodleのテスト概要をもとに勝手に予想して作っています。どうぞ https://hackmd.io/@sakarush/ryVz1tCfI --- ## ○○の倍数かどうかを確かめる * 方針:%で割ったあまりが出てくる。割ったあまりが0ならば、要は割り切れているのでその倍数である。 * コード ```C if (x % 11 == 0){ printf("warikireta"); } ``` if xが11で割り切れるならメッセージを表示 --- ## 1から100までループをする * 方針:forを使う * コード ```C for (int i = 1;i <= 100; i++){ printf("count : %d\n", i); } ``` --- ## 100から逆順に1までループする 逆向きにするだけですが、いつも同じやり方でforを書いてると解けない問題です。 forを理解しているかどうかを確かめるには良い問題かもしれません。(出そう?) * 方針:iに100を入れて、i >= 0 まで 毎回iを減らしていきます。 * コード ```C for (int i = 100; i >= 1; i--) { printf("%d\n",i); } ``` ``i--`` は1ずつ減らすという意味です。 結果としては ``` 100 99 98 (~省略~) 2 1 ``` こんな感じになります。 --- ## FizzBazz問題 数字を1から100まで表示して、3で割り切れるときはFizz、5で割り切れるときはBazz、どっちもで割り切れるときはFizzBazzと表示するプログラムをかけ、という初歩的な有名な問題がある。 テストに出るかもしれないのでこれも書いてみよう。 * 方針:forを使って、中で倍数かどうかを確かめる。 * コード ```C for (int i = 1; i <= 100; i++){ if (i % 15 == 0) { printf("FizzBazz\n"); // x が15で割り切れたらFizzBazzと表示 }else if (i % 3 == 0) { printf("Fizz\n"); // 15で割れなくて、3で割れたらFizz }else if (i % 5 == 0) { printf("Bazz\n"); // 3でも割れなくて、5で割れたらBazz }else { printf("%d\n", i); // 3でも5でも割り切れなかったらただ数字を表示するだけ } } ``` これを実行するとこうなる ``` 1 2 Fizz 4 Bazz Fizz (~省略~) 13 14 FizzBazz 16 17 Fizz (~省略~) 98 Fizz Bazz ``` --- ## おまじない C言語の基本的な形です。 * 方針:先生が言ってたようにやります プログラムはmain(void) { }の中に入れなければいけません。 * コード: ```C #include <stdio.h> int main(void) { printf("Kusa"); } ``` --- ## 素数判定 その数が素数(英語でPrime number)かどうかを判定します。 問題の難易度もテストにちょうど出しやすいように思います。 ここには最も簡単なやり方を書きますが、実は色々なやり方があって、 エラトステネスの篩(ふるい)とか、頭の良いやり方にすると、爆速になったりします。 このような「やり方」のことをアルゴリズムといい、3年で学びます。 今回は要らないでしょう。最も単純なやり方でやります。 * 方針: 素数は1とその数以外で割り切れない数です。 ということで、2からその数の半分までループをして、どの数で割っても割り切れなかったらそれは素数と言えます。 * コード ```C int x = 113; // 113が素数かどうかを判定する。 for (int i = 2; i <= x / 2 ; i++) { // xの半分までループを回す if (x % i == 0) { // xを回している数iで割り切れたなら、素数でないので printf("%d is not a Prime Number\n",x); //素数ではないと表示してその時点で終了 return 0;//終了 } } // もし2から113の半分まで全て割り切れなかったら素数なので printf("%d is a Prime Number\n",x); // 素数だと表示して終了 ``` 今回、xに113を入れて判定しました。 113は素数なので今回は ``` 113 is a Prime Number ``` と表示されます 仮に最初にx = 75 を入れたとすると、 2で割れない 3で割れない 4で割れない 5で割れた! となって ``` 75 is not Prime Number ``` と表示されます。 これで素数判定が出来ました --- ## 2から1000までの素数を列挙して表示する 上の応用です。 上では一つの数xについて素数を判定したので、このxを更にループします このようにループの中にループを書くことを二重ループと言います(そのまんま) * 方針:上のコードを更にループで囲います。 しかしさっきはreturnで終了すればよかったですが、今回は沢山の数について判定するのでそうは行きません。flagという変数に、予め0を入れておいて、割り切れた時だけ1を入れます。 そして最後にflagが0か1かで素数か素数でないかを見て表示します。 * コード ```C for (int x = 2; x <= 1000; x++){ // さっきはxを固定したけど、xについて1から1000までループする int flag = 0; for (int i = 2; i <= x / 2 ; i++) { // xの半分までループを回す if (x % i == 0) { // xを回している数iで割り切れたなら、素数でないので flag = 1; // 割り切れたという目印を立てておく break; } } if (flag == 0) { // 一度も割り切れなかったらflagは0のままなので printf("%d is a Prime Number\n", x); // 素数だと表示する }else{ // 素数ではないので printf("%d is not a Prime Number\n",x); //素数ではないと表示する } } ``` * ちょっと解説 今回の問題を2から1000にしたのは、このやり方では「1」が素数だと判定されてしまうからです。(数学的には1は素数でないとされています) もし1から1000まで素数判定せよと言われたならば1の時だけ特別に除外して一番最初に ```C printf("1 is not a Prime Number"); ``` とかをいれてやれば良いでしょう。 今回flagという変数をたてて、割り切れたのか割り切れてないのかという状況を保存しておきました。 よく言う「死亡フラグが立った」という言葉はここから来ています。 要はもうすぐ死ぬような情報がflagに保存されたということなのですね。 この辺りはゲーム開発でもよく使います(例えば村人に話しかけたらflagをたてる 等) --- ## 5の階乗を求めるプログラム * 方針:1から順番にforで掛けていくだけ * コード ```C int x = 1; for (int i = 1; i <= 5; i++){ x = x * i; // xにiをかけて代入する(*1 *2 *3 *4 *5) } printf("%d\n",x); ``` --- ## 9個のデータを入力して、平均を求めるプログラム * 方針:9回データを入力させて、10で割って出力する 9で割った値は整数とは限らないので、少数を用意しておく。 また、キャスト(型変換)を行う。 * コード ```C int input; int sum = 0; // 和を保存するデータは0にしておく for (int i = 1; i <= 9; i++){ printf("Please input a number : \n"); scanf("%d",&input);// inputに入力した値を保存する。&を忘れない sum = sum + input; // 今のsumの値にinputを新たに足して代入する } double average = (double)sum / 9; // 9で割った値を少数である可能性があるのでdoubleに保存する(キャストする) printf("Average : %lf", average); ``` 今回は初めて少数を使った。 少数を使う時には %d は使わないこと。 書式指定子の説明の所で、 %f を使うべきか %lf を使うべきか迷っているという話を書いたが、ここでは %lf を使った (誰か先生に聞いてきてくれないかな) 平均を求める時に sum は整数(int)である。 そして保存先の average は実数(double)である。 もし ```C double average = (double)sum / 9; ``` を、これではなくて ```C double average = sum / 9; ``` にしてしまうと、 sum / 9 の値が整数の範囲内で出てきてしまう。(具体的には、小数部分が切り捨てられる) これは sum が整数型であるためだ。 だから、(double) を前につけることで、sumをキャストして実数型に変えなければいけない。 キャストはこのような目的の時に扱うのだ。 --- ## うるう年じゃない年で、与えられた月が何日かを表示するプログラム * 方針:授業で問題10,11,12でやった時と同じようにdo whileで入力させて、switchで何月かを場合わけします。 * コード ```C int x; do{ printf("Please input month (1 <= x <= 12) : "); // 英語なんもわからん怪しい scanf("%d",&x); // scanfでは & を忘れない! }while(x < 1 || x > 12); // 1 から 12 の範囲外ならもう一度繰り返す。do whileの後はセミコロンを忘れない int days; switch(x) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: // まとめて一つの記述をする時は並べれば良い days = 31; break; // breakを忘れない! case 4: case 6: case 9: case 11: days = 30; // 4 6 9 11 月は30日ですね break; case 2: days = 29; break; // 2月は29日ですね default: days = -1; // 今回は、defaultが呼び出されることは無いですが、一応書いておくべきです。(この辺りはコーディング規約と言われる思想の違いですが、書いて損はありません) } printf("Inputted : %d , days : %d",x,days); ``` これは先ほどの問題に比べれば比較的簡単ですね switchを使う時はbreakを忘れない!ということを抑えておきましょう。 また、今回printfで「%d」を2つ書きました。 「%d」を2つ書いて、変数を2つ渡すとそれぞれ左から詰めて置き換えてくれます。 下のコーディング規約に関する内容は発展なので読まなくても良いです。 ```C ちょっと発展 コーディング規約とdefault:を書くかどうかのお話 プログラミング言語では、結構乱雑な書き方をしても動きます。 C言語では結構そういう傾向が強くて、例えば全て改行せず1行で書いても多分動きます(多分) でもそういうプログラムを書くと、読みにくく、メンテナンスしにくくなりますよね。 そこでプログラミング言語では色んな人、組織によって「コーディング規約」と言う、従うべきルールを定めています。 (従わなかったから直ちにバグったりエラーになったりということは無い。) ある意味マナーのようなものです。 そしてこのコーディング規約もバラバラで、色んな派閥があります。 例えばint main(void) {} といつも書いているやつ。 int main(void) { } とかく派閥と int main(void) { } とかく派閥がいます。(C言語の絵本では後者で、先生は前者です。私も前者です) さて、switchにもコーディング規約があり、最も大事なのはdefault:を作るかどうかということです。 基本的にはdefault:は(呼び出されることが想定されていなくても)作るべきとされています。 これはよきせぬ時にdefaultが無いがためのやばいエラーを防ぐためです。 予め想定しておくべきだ、ということです。 ちなみに先生の示したJavaのコーディング規約集には、defaultについての言及はありませんでした。 どっちでも良いんだから、書いておけば安全ですね ちなみに先生自身もこのコーディング規約に従ってなくて、規約には大小比較する時には < <= を好んで使えと書いてありますが、先生は変数を左に揃えるようにおっしゃっています。 例: 1. Javaコーディング規約 if (x < 0 || 100 < x) が好ましい 2. 先生 if (x < 0 || x > 100) が好ましい まあ、この授業においてはそれくらい適当で良いよということでしょう。 (字下げは必要なのでしましょう) 後、どっちで書くにしてもルールの統一はしましょう。 ``` --- ## うるう年かどうかを判定するプログラム せっかく月と日にちをやったのでうるう年も書いておきます。 ``` うるう年の定義 (1)西暦年号が4で割り切れる年をうるう年とする。 (2)(1)の例外として、西暦年号が100で割り切れて400で割り切れない年は平年とする。 ``` * 方針:do whileで入力してもらったら、うるう年の定義に従ってifで判定する * コード ```C int x; do{ printf("Please input year (1 <= x <= 5000) : "); // 英語なんもわからん怪しい scanf("%d",&x); // scanfでは & を忘れない! }while(x < 1 || x > 5000); // 1 から 5000 の範囲外ならもう一度繰り返す。 if (x % 4 == 0) { // 4 で割り切れたらうるう年っぽいけど if (x % 100 == 0 && x % 400 != 0) { // 例外で100で割れて400で割れない時は平年 printf("%d is not UruuDoshi\n", x); // 英語なんも分からん }else{ //例外に当てはまらなかったのでうるう年 printf("%d is UruuDoshi\n", x); } }else{ // 4で割り切れなかったら問答無用でうるう年ではありません printf ("%d is not UruuDoshi\n", x); } ``` 頭を使いますね... 実は上のifの部分、&&とかでうまくまとめるともう少し綺麗にできて ```C if (x % 4 != 0 || (x % 100 == 0 && x % 400 != 0)) { printf("%d is not UruuDoshi", x); }else{ printf("%d is UruuDoshi", x); } ``` こう出来ます。 xが4で割り切れなかった、または(100で割り切れたが400で割り切れなかった)ならば平年 そうでなければうるう年 という意味です これはプログラミングというより数学ですね。 --- # 9. 超おまけ  完全にテスト範囲外のコラムです プログラムやりたい人が増えてほしいのでプログラム全般の雑学です。 ## エディターのすすめ 皆さん、大学で開発する時にテキストエディターには何を使っていますか? とりあえず言えることは、メモ帳はやめておいた方が良いです。 醜い / 編集しにくい / バグる の三拍子です。 大学のPCにデフォルトで入ってるやつで、最もおすすめなのはK2Editorです。 K2Editorはこのテキストのようにある程度色付けをしてくれます。 ↓こんな感じ ```C int main(void) { printf("Kusa"); } ``` 大学のPCには入っていませんが、Visual Studio Codeなどのプログラミング用の高機能な テキストエディターを使うととても見やすかったり、バグを指摘してくれたり、開発が楽になります。 * VSCode(Visual Studio Code) ![](https://i.imgur.com/dtHVE21.png) * メモ帳 ![](https://i.imgur.com/by1w1j4.png) みやすさの違いは一目瞭然ですね --- ## プログラミング言語について 世の中には大量のプログラミング言語が存在します。 そしてほとんどの言語の目的は、なにかを達成する手段として使うことです。 (例えば統計処理とか、エクセルの自動化とか、車を動かすとか、家電を作るとか...) そしてプログラミング言語はそれぞれの目的に応じたもので選ぶべきです。 では仮にあなたが、自分でなにかの作業を自動化したり、人工知能を学びたいとしましょう。 その現場で全てC言語を使うことは無いでしょう。 C言語はプログラミング言語の中でもめちゃくちゃ"辛い"言語です そして、今の時代はC言語ではなくC++言語と言われるCの拡張のもっと難しい(けど楽な)言語を使います。 簡単に用途を分けておきましょう。 汎用的で簡単なことをする * Python * Basic などなど 組み込み系(家電や車)を作る * C言語 * C++ * Java * アセンブリ言語 などなど 人工知能をする * R * Python * Go などなど Windowsのアプリを作る * C / C++ * C#(マイクロソフトが作った言語です) などなど ゲームを開発する * C# + (Unity) * C++ + (UnrealEngine) などなど Android アプリを開発する * Kotlin * Java iPhone / iPad アプリを開発する * Swift * Objective-C ウェブアプリ(ウェブページ)を開発する * Python * JavaScript * TypeScript * PHP などなど こんな風に、用途によって得意不得意が別れています。 しかしその中でもやはりPythonは簡単な用途に汎用性が高く、重宝されています。 (実際に書くのはC言語の数十倍楽です) C言語は古くからある基本の言語ですが、この後に出てきた最近の(モダンな)言語はもっと色んな考え方、概念、便利で難しい色々な記法を積んでたりします。 例えば来年以降学ぶJavaや、上にあげた言語の多くが実装している **オブジェクト指向** という考え方は、特にうまく習得するのが難しいとされています。 (とはいえC言語を理解するのに比べれば楽だと思いますが) 言いたいことは、今回C言語をやって出来たことは少ないと思いますが、これに幻滅せずに、自学するならばpythonなどの言語に切り替えてみて下さい。 pythonは導入が難しいんですが、プログラムを書き始めるとびっくりするくらい簡単です。 そして数行書くだけで色んなことが出来ます。(C言語は数十行 / 数百行かかることもあります) 長々と蛇足を話しましたが、プログラミングに興味を持ってもらえる人が一人でも増えれば幸いです。 このテキストは20000字になりました。~~これだけで単位くんねえかな~~