CS50 lecture1 C ハーバード大学 コンピュータサイエンス講座 メモ

YouTubeで無料公開されているハーバード大学のコンピュータサイエンス講座を引き続き受講。
2時間16分とかなり長いので3回くらいに分けて観たけど、これ一回でやってる先生と学生、すげえな…
ただ、講義あるあるで最後の方時間無くなってきて先生がかなり巻いてて追いつけないとこもあって、それはそれでリアル。

CS50 lecture1 C

情報量が多いが、全て理解しきれなくても良い
基本は前回のscratchで学んだことと同じ

コードを書く際の指針
①正しさ
②デザイン 良く設計された効率的なコードかどうか
③スタイル 読みやすさ

まずは「Hello,world」と書く

使うツール CS50 IDE
Integrated Development Environment(統合開発環境)
ウェブベースのプログラミング環境
ウェブベースなので使っているPCの環境に関わらず同じ体験が可能

ログインすると画面にウィンドウが表示される
上のウィンドウでコードを書く
下のターミナルウィンドウで結果を表示、または実行

File⇒New File
Untitled1⇒「hello.c」でFile⇒Save
※Cのファイルの拡張子は「.c」

書いたコードを実行
GUI(グラフィカルユーザーインターフェース)とは対照的
コマンドラインインターフェース⇒アイコンでなくコードで実行
ターミナルウィンドウにコマンドを入力する必要がある
コンピュータが唯一理解できる言語=バイナリ
上のウィンドウで書いたコードをコンピュータはそのままでは理解できない
C言語と呼ばれる言語で書かれたソースコードをコンピュータが理解できるように0と1に変換するにはどうしたらいいか?
ソースコード⇒■⇒マシンコード
ソースコード=C言語
マシンコード=0と1
■=コンパイラ ※無料でダウンロードできるプログラム、あるいは昔は有料だったプログラムで、ソースコードをマシンコードに変換するために設計されたプログラム

CS50 IDEに用意されているコンパイラ=make
プログラムを作ろうと思ったら、make helloと入力
「./hello」というちょっと暗号めいた構文で実行する

ターミナルウィンドウにカーソルを入れる
「make hello」と入力 ※hello.cではないことに注意
※コンピュータはこれによってプログラマが「hello」というプログラムをコンパイルしたいのだと理解する
⇒Enterを押すと、謎の文字の羅列が表示される
黄色や赤字でなければ正常にコンパイルされたということ
⇒コンパイルされたプログラムを実行するには「./hello」
「hello,world」と表示される

関数(functions) 動詞、動作 ミニプログラム
←引数(arguments=パラメータ)を入力できる

scratchの「say」=Cの「printf」
フォーマットされた何かを印刷する
printf(引数)
引数を文字とすると、左右に二重引用符「"」を付けないといけない
最後にセミコロン「;」を付けないといけない

ブラックボックスとはアルゴリズム
アルゴリズムとは関数
関数とはアルゴリズムをプログラム化したもので、アルゴリズムをコードやソフトウェアで実装したもの
関数は引数を受け入れる
「hello,world」がScratchのsayブロックやCのprintf関数の入力であるように、関数が入力=引数を取る場合、関数は副作用(side effect)と呼ばれるものを伴う事ができる
「hello,world」と画面上に表示される=視覚的な副作用
関数のもう一つの特徴
「戻り値(return values,variables)」⇒関数は単に値を返すことができる
画面上で値を表示するわけではなく、関数の出力が何であれ、プログラマが再利用できるように変数に格納して値を返してくれる
scratchのaskブロック 「あなたの名前はなんですか?」に対する答え
答えをanswerという変数に格納した
answerをどうにかするには?
Cには様々な方法があるが、ライブラリと呼ばれるものを使用しない限り、どれも簡単ではない
※ライブラリ=誰かが書いたコード
CS50ライブラリを使用する
簡単なことを簡単にできるようにした

askブロックのような、ユーザーからのテキスト取得
文字列=string テキスト、単語
get_string(あなたの名前はなんですか?)
人間の名前で何かするにはscratchのように変数に入れてくれることを期待するだけでは十分ではない
何かを変数に入れたいのであれば自分でやらなければならない
欲しい変数の名前を考えて、それがxであれyであれanswerであれ、等号を使う
answer = get_string("what's your name?")
数学の世界で等号は一般的に等価であることを意味するが、
プログラミング言語では代入と呼ばれるものを意味する
=「右にあるものを左にあるものにコピーする」
関数get_stringに入力された引数(人間の名前)はanswerという変数に入る
どんな種類の変数が欲しいのかをあらかじめコンピュータに伝えなければならない
今回のanswerは文字列
string answer = get_string("what's your name?")
↑何が足りない?
最後のセミコロン;
最初は忘れてムカつくけど、慣れれば大丈夫

answerを表示するには?
printf("hello, %s" );
%s ※フォーマットコード フォーマットコードなのでprintfにはfが付く
関数の引数や入力としてカッコの間でカンマを使うと、左のものと右のものが分離され、scratchの正解で2つの楕円があるのと同じことになる
printf("hello, %s" , answer);

先ほどのhello.cを編集

ライブラリを使用するために
#include<cs50.h>
という行を追加⇒get_string関数にアクセスできる

再コンパイルしないと更新されない
※最近のプログラミング言語では再生ボタンを押すだけで再コンパイルされるが、C言語は何十年も前のプログラミング言語で、ユーザーの使いやすさよりパフォーマンスが重視されていた。現在はユーザーの使いやすさにも配慮されたプログラミング言語が主流になっている

バックスラッシュn ※""の中の最後に挿入することで「カーソルを次の行に移動してください」という指示になる

Scratchで言うところの「緑の旗」を意味する構文
int main(void)
{
}

ヘッダファイル
.hで終わるファイル
#include<stdio.h> stdio.hというファイルを参照
「standard input output dot h」
io ※コンピュータの世界ではinputとoutputの略
stdio.hはCプログラムで使用される非常に一般的なファイルで、ユーザーからの入力と出力を得るための機能を提供する
例えばprintfを提供
scratchの拡張メニュー=Cのヘッダファイル

タイプミスや構文入力漏れ(;など)によってプログラムが上手くいかない…
⇒help50でサポートできる
ターミナルウィンドウで「help50」と書いて、問題のあるコマンドを書いてEnter
自分のコードのトラブルシューティングを可能にするコマンド

エラーコードの解読
プログラムがソースコードからマシンコードにコンパイルされないようなコード上のミスを犯した場合、エラー出力の先頭に、ミスを犯したファイル名と行番号を示す手がかりが表示される(何行目の何列目か)

Style50
スタイルが適切かどうかをチェックしてくれるコマンド

コメントも読みやすくするのに重要
// ※二つのスラッシュの後に続くのはコメント

Check50
コースのプログラミング課題で自分のコードが正しいかどうかをチェックするためのツール

IDEの左上のフォルダアイコンを操作することで自分が作ったファイルにアクセスできる
⇒ターミナルウィンドウでもコードによる指示で同じことができる

ls リストの省略
フォルダ内のファイルやフォルダを一覧表示する
⇒緑で表示され、右に*がついているファイルは「./」で実行可能なプログラムであることを示す

rm remove(削除)
rm hello ⇒helloファイルを削除 ⇒本当に削除するか聞かれるので「y」か「yes」

mv move(移動) 名前を書き換える
mv hello.c goodbye.c ⇒hello.cという名前がgoodbye.cに書き換えられる

フォルダ=ディレクトリを作る
mkdir make ディレクトリ
mkdir lecture ⇒lectureという名前のフォルダを作る
ファイルをフォルダに入れる
mv hello.c lecture ※mv ファイル名 フォルダ名

この状態でlsを入力すると、lectureフォルダしか表示されない
フォルダに入るには?
cd change directory
cd lecture
~(チルダ) ホームディレクトリを表す
「..」ファイルを一つ上の階層に戻す
mv hello.c ..
自分は今lectureフォルダに入っている⇒一つ上の階層に上がるには?
cd ..
lectureフォルダを削除
rmdir lecture

./hello ←この「.」はカレントディレクトリ(現在自分がいるディレクトリ)を表していた

C言語には様々なデータ型がある
文字列は1つのデータ型に過ぎない
整数、浮動小数点数、真か偽かのブール型…
char 1文字
int、long 整数
float(floating point value)、double 浮動小数点数
intやfloat⇒特定のビット数を持つ
C言語のint 32ビットしか持たない
long 64ビットまで使える
double floatより多くの桁数を使える

printfで数を表示したい時
%c 1文字を表示するためのプレースホルダ char
%f 浮動小数点数を表示
%i 整数を表示
%li longを表示
C言語ではプログラマが正確に作業する必要がある
printfに渡す変数や値をどのように表示するかを指示しなければならない

演算子 operators 数学的なもの、そうでないもの
+

/
%

#include <cs50.h>
#include <stdio.h>

▼整数同士を足し合わせるだけのプログラム
int main(void)
{
int x = get_int("x:");
int y = get_int("y:");
printf("%i\n",x + y);
}
xやyに整数以外の文字列などを入力しようとしても受け付けない
(何度もxを聞いてくる)
40億も受け付けない←intでは扱えない
32ビットあれば40億までサポートしているはず…?
実はintは正の数だけでなく0や負の数もサポートしているため、20億までしか扱えない
⇒longなら扱える

▼二つの整数の割り算
#include <cs50.h>
#include <stdio.h>

int main(void)
{
int x = get_int("x:");

int y = get_int("y:");

float z = x / y;

printf("%f\n",z);

}
割り切れる数字だと割り算の答えが出るが、小数点数になると上手くいかない
4/3が1.000000になってしまう
xとyをintにしたから整数しか認識しない
⇒xとyをfloatで定義し直す必要があるが、全部直すのは大変
←キャストで置き換えられる
あるデータ型を別のデータ型に変換するには文字通り()の中に必要な新しいデータ型を入れる
float z = (float) x / (float) y
⇒ちゃんと1/2が0.5になる

▼加算
int counter = 0;
counter = counter + 1;
↓シンタクティクシュガー(代替案)がある
counter += 1;
↓これの更なるシンタクティクシュガー
counter++;

▼条件
xがyより小さかったら…
if(x < y)
{
printf("x is less than y");
}
※()で囲んでいるのはブール式

xがyより小さかったら、そうでなかったら…
if(x < y)
{
printf("x is less than y");
}
else
{
printf("x is not less than y");
}

xがyより小さかったら、そうでなくxがyより大きかったら、そうでなくxとyが同じだったら…
if(x < y)
{
printf("x is less than y");
}
else if (x > y)
{
printf("x is greater than y");
}
else if (x == y)
{
printf("x is equal to y");
}
※等号は==

実は論理的に1つ目の条件と2つ目の条件のどちらにも当てはまらない状況は等価しかない
最後のelse ifのブール式(x == y)は無くても良かった(elseだけでOK)

#include <cs50.h>
#include <stdio.h>

int main(void)
{
char c = get_char("Do you agree? ");
if(c == "y")
{
printf("Agreed.\n");
}
else if(c == "n")
{
printf("Not agreed.\n");
}
}
charの前後の引用符は一重引用符でないといけない
"y" ⇒ 'y'

上のプログラムだと小文字のyしか受け付けない
大文字のYも受け付けるようにしたいなら?
if(c == 'y' || c=='Y')
||  論理OR演算子 「または」
&& 論理AND演算子 「かつ」

▼ループ
何かが起こっている間はこうしなさい
条件に合っているかどうか常に質問し続けている
ブール式を持つ条件と非常に良く似ている
while()
{
}
()にtrueを入れると決して止まらない無限ループを意図的に作り出せる
C言語ではtrueは常に定義上真であるため
有限回行うにはforループかwhileループを使う

50回実行させたい while編
int i = 0;
while( i < 50)
{
printf("hello,world");
}
iが50より小さいかどうかをチェックし続ければ良い
反復する事に1回ずつカウント(数える)したい
iに1ずつ加算する
int i = 0;
while( i < 50)
{
printf("hello,world");
i++;
}
※プログラミングの慣習として、数え始めは「0」にする(1から数え始めない)
この場合は0から49まで数えることで50回繰り返す

50回実行させたい for編

for
{
}
forはあとに続く()の中で好きな変数をある値に初期化することができる
for(int i = 0; i < 50; i++)
{
printf("hello,world");
}
for(初期化 条件 更新)
※これがC言語や他のプログラミング言語であることを有限回繰り返すための最も一般的な方法

抽象化

▼3回meowと鳴くプログラム
#include <stdio.h>

int main(void)
{
for(int i =0;i<3;i++)
{
printf("meow\n");
}

}
抽象化 meowと鳴くというプログラムをカスタム関数にしたい

void meow(void)
{
printf("meow\n");
}
↑オリジナルの「meow」関数が出来た
「meow();」で呼び出せる
※カスタム関数はファイルの一番下に置くのが普通
でも、一番下に置いてコンパイルしようとするとエラーになってしまう
コンパイラは上から下に順にコンパイルしていくので、meow関数が定義される前にmeow関数に出会ってしまって混乱してしまう
「void meow(void);」←この部分だけ上に持ってくる
コンパイラに「このプログラムの中にこういう関数が出てくるよ」と教えておく

何回鳴かせるか決められるプログラム
void meow(int n)
{
for(int i=0; i < n; i++)
{
printf("meow");
}
}
void meow(3);
meow関数に引数を入れられる
meowの左側「void」は関数の戻り値または出力を意味する←今回は戻り値・出力は無し
右側()内の「void」は入力←これを用意する

▼何回鳴くかの入力⇒正の数だけを受け付けたい
positive.c

#include <cs50.h>
#include <stdio.h>

int get_positive_int(void);

int main(void)
{
int i = get_positive_int();
printf("%i\n",i);
}

int get_positive_int(void)
{
int n;
do
{
n = get_int("Positive Integer:");
}
while(n < 1)
return n;
}
※get_positive_intの左側は今回はvoidでなくint
↑戻り値・出力がある、という事
※do whileループ 条件をチェックする前に盲目的に一つの事を先に行う
※return n;⇒この関数の出力(戻り値)である整数nを返す
※なぜint n;を{}の外で宣言しているのか?
 {}の中に入れてしまうとスコープの問題に遭遇してしまう
変数のスコープとは、その変数が含まれるコード範囲のこと
⇒{}の外では宣言していない(使えない)ことになってしまう

▼マリオの?ボックスを4つ出力するプログラム
?の下面に触れるとコインが出るやつ
mario.c
#include <stdio.h>

int main(void)
{
printf("????\n");
}

????と表示される⇒ループにできる

#include <stdio.h>

int main(void)
{
for(int i=0;i<4;i++)
{
printf("?");
}
printf("\n");
}

▼任意の数の?ブロックを横に伸ばせるプログラム
#include <cs50.h>
#include <stdio.h>

int main(void)
{

int n;
do
{
n = get_int("Width:");
}
while(n < 1);

for(int i=0; i<n;i++)
{
printf("?");
}
printf("\n");
}
※Widthは幅のこと

▼縦横にどのくらいブロックを伸ばすか?
###を3行縦に並べたい

#include <cs50.h>
#include <stdio.h>

int main(void)
{
for(int i=0;i < 3;i++)
{
for(int j=0;j < 3;j++)
{
printf("#");
}
printf("\n");
}
}
※forの入れ子
①#を横に3つ並べる
②3回改行する

マリオに限らず、PCやスマホ、ゲーム機のマップ生成はこういった方法で行われている

▼コンピュータには限界がある
floatで1/10を行う⇒小数点以下の桁数を10~50桁にしようとすると、変な計算結果になってしまう
コンピュータのRAMメモリには限界があるのである一定の情報量を超えると近似値でしか計算できない
数学、金融、軍事的な用途でこのような計算をしようとして現実とは異なる計算結果になりミスにつながることは良くある
2000年問題 年号を2桁でカウントしていたので2000年と1900年が混同されてしまった
2038年問題 秒数を1970年1月1日から32ビットでカウントアップしている⇒2038年に限界が来る(40億秒を超える)⇒全世界のコンピュータをアップグレードしないと宇宙まで巻き込んだ大惨事になりかねない
このような制約にも対処できるようなプログラムにしていくことが重要

この記事が気に入ったらサポートをしてみませんか?