1 00:00:00,000 --> 00:00:02,490 [Powered by Google Translate] [CS50ライブラリ] 2 00:00:02,490 --> 00:00:04,220 [ネイトHardison] [ハーバード大学] 3 00:00:04,220 --> 00:00:07,260 [これはCS50です。 CS50.TV] 4 00:00:07,260 --> 00:00:11,510 CS50ライブラリは、我々は、アプライアンスにインストールされている便利なツールです 5 00:00:11,510 --> 00:00:15,870 あなたがプログラムを書くことを簡単にするために、その入力を求めるユーザ。 6 00:00:15,870 --> 00:00:21,670 このビデオでは、カーテンを引くだろうし、まさにCS50ライブラリにあるものを見てみましょう。 7 00:00:21,670 --> 00:00:25,520 >> Cライブラリでのビデオの中で、我々はあなたが#ヘッダファイルをインクルードする方法について話 8 00:00:25,520 --> 00:00:27,570 ソースコード内のライブラリの、 9 00:00:27,570 --> 00:00:31,150 そして、あなたはリンクフェーズ中にバイナリライブラリファイルとリンク 10 00:00:31,150 --> 00:00:33,140 コンパイルプロセスの。 11 00:00:33,140 --> 00:00:36,440 ヘッダファイルは、ライブラリのインターフェイスを指定します。 12 00:00:36,440 --> 00:00:41,280 すなわち、彼らは詳細は、使用するためのライブラリが利用できる持っているすべてのリソースを、ある 13 00:00:41,280 --> 00:00:45,250 関数宣言、定数、およびデータ型と同じように。 14 00:00:45,250 --> 00:00:48,890 バイナリライブラリファイルは、ライブラリの実装が含まれています 15 00:00:48,890 --> 00:00:54,580 これらはライブラリのヘッダファイルとライブラリの。cソースコードファイルからコンパイルされます。 16 00:00:54,580 --> 00:00:59,820 >> バイナリライブラリファイルは、そのファイルがバイナリで、まあ、だから見て非常に興味深いものではありません。 17 00:00:59,820 --> 00:01:03,300 それでは、代わりにライブラリ用のヘッダファイルを見てみましょう。 18 00:01:03,300 --> 00:01:07,710 この場合、cs50.h.呼ばただ一つのヘッダファイルがあり 19 00:01:07,710 --> 00:01:11,040 我々は、ユーザーにそれをインストールしたディレクトリを含める 20 00:01:11,040 --> 00:01:15,150 他のシステムライブラリ 'ヘッダファイルと一緒に。 21 00:01:15,150 --> 00:01:21,530 >> あなたが気づく最初の事の一つは、cs50.h#は他のライブラリのヘッダファイルが含まれていることである - 22 00:01:21,530 --> 00:01:25,670 フロート、限界、標準ブール値、お​​よび標準lib。 23 00:01:25,670 --> 00:01:28,800 繰り返しになりますが、車輪の再発明をしないの原則に従って、 24 00:01:28,800 --> 00:01:33,490 我々は他の私たちのために提供されているツールを使用してCS0ライブラリを構築しました。 25 00:01:33,490 --> 00:01:38,690 >> あなたがライブラリに表示されます次のことは、我々はと呼ばれる新しいタイプ定義することである "という文字列を"。 26 00:01:38,690 --> 00:01:42,330 この行は、実際には単なるchar *型のエイリアスを作成し、 27 00:01:42,330 --> 00:01:46,000 ので、それは魔法のように属性を持つ新しい文字列型を鼓吹していません 28 00:01:46,000 --> 00:01:49,650 一般的に、他の言語で文字列オブジェクトに関連付けられている 29 00:01:49,650 --> 00:01:50,850 長さなど。 30 00:01:50,850 --> 00:01:55,180 我々はこれをやった理由は、血みどろの詳細から新しいプログラマを保護することです 31 00:01:55,180 --> 00:01:57,580 ポインタの彼らは準備が整うまで。 32 00:01:57,580 --> 00:02:00,130 >> ヘッダファイルの次の部分は、関数の宣言です 33 00:02:00,130 --> 00:02:04,410 CS50ライブラリはドキュメントとともに提供している。 34 00:02:04,410 --> 00:02:06,940 ここにコメントの詳細レベルに注意してください。 35 00:02:06,940 --> 00:02:10,560 人々はこれらの関数を使用する方法を知っているので、これは超重要です。 36 00:02:10,560 --> 00:02:19,150 私たちは、順番に、関数がユーザとリターン文字、倍精度、浮動小数点数、整数を求めるように、宣言 37 00:02:19,150 --> 00:02:24,160 長いlong、および文字列、私たち自身の文字列型を使用しています。 38 00:02:24,160 --> 00:02:26,260 情報隠蔽の原則に従い、 39 00:02:26,260 --> 00:02:31,640 私たちは別のCの実装ファイルに我々の定義を入れている - cs50.c- - 40 00:02:31,640 --> 00:02:35,110 ユーザーソースディレクトリにある。 41 00:02:35,110 --> 00:02:38,040 あなたがそれを見てとることができるように、我々は、そのファイルを提供してきました 42 00:02:38,040 --> 00:02:41,490 そこから学ぶこと、そして必要に応じて別々のマシン上でそれを再コンパイルし、 43 00:02:41,490 --> 00:02:45,510 我々はそれがこのクラスのアプライアンス上で動作するように良いことだと思うのに。 44 00:02:45,510 --> 00:02:47,580 とにかく、今それを見てみましょう。 45 00:02:49,020 --> 00:02:54,620 >> 機能GetCharなどgetDoubleは、getFloatは、場合、getInt、およびGetLongLong 46 00:02:54,620 --> 00:02:58,160 すべてのGetString関数の上に構築されています。 47 00:02:58,160 --> 00:03:01,510 それは、彼らはすべて基本的に同じパターンに従っていることが判明した。 48 00:03:01,510 --> 00:03:04,870 彼らは、入力の1行の入力をユーザーに求めるwhileループを使用しています。 49 00:03:04,870 --> 00:03:08,430 彼らは、空行がユーザ入力する場合は、特別な値を返します。 50 00:03:08,430 --> 00:03:11,750 彼らは、適切な型として、ユーザーの入力を解析しよう 51 00:03:11,750 --> 00:03:15,010 char型、倍精度、浮動小数点などのそれである 52 00:03:15,010 --> 00:03:18,710 入力が正常に解析された場合、その後、彼らはどちらかの結果を返す 53 00:03:18,710 --> 00:03:21,330 またはそれらがユーザーを求めるプロンプトを表示。 54 00:03:21,330 --> 00:03:24,230 >> 高レベルでは、本当に難しいものはここにはありません。 55 00:03:24,230 --> 00:03:28,760 あなたは過去にも同様に構造化されたコードを自分で書かれている場合があります。 56 00:03:28,760 --> 00:03:34,720 おそらく最も不可解に見える部分は、ユーザの入力を解析sscanf関数呼び出しです。 57 00:03:34,720 --> 00:03:38,160 sscanfは入力フォーマット変換ファミリの一部です。 58 00:03:38,160 --> 00:03:42,300 、これは、標準的なio.hで生きており、その仕事は、C言語の文字列を解析することです 59 00:03:42,300 --> 00:03:46,520 特定のフォーマットに従って、変数に解析結果を格納 60 00:03:46,520 --> 00:03:48,720 発信者によって提供されます。 61 00:03:48,720 --> 00:03:53,570 入力フォーマット変換機能は非常に有用で、広く使われている機能があるので 62 00:03:53,570 --> 00:03:56,160 最初は超直感的でないこと、 63 00:03:56,160 --> 00:03:58,300 我々は、sscanfがどのように動作するかをみることにします。 64 00:03:58,300 --> 00:04:03,330 >> 文字へのポインタ - sscanf関数の最初の引数はchar *である。 65 00:04:03,330 --> 00:04:05,150 正しく動作させる機能のために、 66 00:04:05,150 --> 00:04:08,340 その文字は、C言語の文字列の最初の文字でなければなりません 67 00:04:08,340 --> 00:04:12,270 ヌル\ 0文字で終了しました。 68 00:04:12,270 --> 00:04:15,120 これは、解析する文字列です 69 00:04:15,120 --> 00:04:18,269 sscanf関数の二番目の引数は書式文字列であり、 70 00:04:18,269 --> 00:04:20,839 一般的に、文字列定数として渡さ 71 00:04:20,839 --> 00:04:24,040 そしてあなたは、printfを使用するときに前にこのような文字列を見たことはあるかもしれない。 72 00:04:24,040 --> 00:04:28,650 フォーマット文字列内のパーセント記号は、変換指定子を示します。 73 00:04:28,650 --> 00:04:30,850 文字はすぐに、パーセント記号に続く 74 00:04:30,850 --> 00:04:35,430 我々はに変換するのはsscanfたいC言語の型を示します。 75 00:04:35,430 --> 00:04:40,090 場合、getIntでは、%dと%cが存在することを参照してください。 76 00:04:40,090 --> 00:04:48,690 %d個 - - とchar - %cのこれはsscanfが進intに試みることを意味します。 77 00:04:48,690 --> 00:04:51,510 フォーマット文字列内の各変換指定子については、 78 00:04:51,510 --> 00:04:56,620 sscanfは、後でその引数リスト内の対応する引数を期待しています。 79 00:04:56,620 --> 00:05:00,850 その引数が適切に型付けされた場所を指している必要があり 80 00:05:00,850 --> 00:05:04,000 変換の結果を格納するためインチ 81 00:05:04,000 --> 00:05:08,910 >> これを行うための一般的な方法は、sscanf関数呼び出しの前に、スタック上の変数を作成することです 82 00:05:08,910 --> 00:05:11,440 あなたは文字列から解析したい項目ごとに 83 00:05:11,440 --> 00:05:15,520 ポインタを渡す - アンパサンド - そして、アドレス演算子を使用する 84 00:05:15,520 --> 00:05:19,100 これらの変数にはsscanfコールへ。 85 00:05:19,100 --> 00:05:22,720 あなたは場合、getIntで我々が正確にこれを行うことがわかります。 86 00:05:22,720 --> 00:05:28,240 右sscanf関数呼び出しの前に、我々は、スタック上にnと呼ばれるintとcharコールcを宣言し、 87 00:05:28,240 --> 00:05:32,340 そして我々はsscanfコールにそれらへのポインタを渡します。 88 00:05:32,340 --> 00:05:35,800 スタックにこれらの変数を置くことは、割り当てられた領域を使用する上で好ましい 89 00:05:35,800 --> 00:05:39,350 mallocでヒープ上に、あなたはmalloc呼び出しのオーバーヘッドを回避するため 90 00:05:39,350 --> 00:05:43,060 そしてあなたは、メモリリークが発生して心配する必要はありません。 91 00:05:43,060 --> 00:05:47,280 パーセント記号によって前に置かれていない文字は変換を求めるプロンプトは表示されません。 92 00:05:47,280 --> 00:05:50,380 むしろ彼らはただフォーマット仕様に追加。 93 00:05:50,380 --> 00:05:56,500 >> たとえば、場合、getIntのフォーマット文字列ではなく%dのだったら、 94 00:05:56,500 --> 00:05:59,800 sscanfは、intが続く文字を探したい 95 00:05:59,800 --> 00:06:04,360 それはint型に変換しようと試みながら、それは、aと他に何もしないだろう。 96 00:06:04,360 --> 00:06:07,440 これに対する唯一の例外は、空白です。 97 00:06:07,440 --> 00:06:11,030 書式文字列内の空白文字は、空白の任意の金額を一致させる - 98 00:06:11,030 --> 00:06:12,890 まったくさえなし。 99 00:06:12,890 --> 00:06:18,100 コメントは先頭および/または末尾の空白を持つ可能性に言及なぜだから、それだ。 100 00:06:18,100 --> 00:06:22,910 だから、それは私たちのsscanf関数呼び出しのようになり、この時点では、ユーザの入力文字列を解析しよう 101 00:06:22,910 --> 00:06:25,380 可能な先頭の空白をチェックすることによって、 102 00:06:25,380 --> 00:06:29,300 int型の変数nに変換されて格納されるintが続く 103 00:06:29,300 --> 00:06:33,090 空白のいくつかの量が続くと、文字が続く 104 00:06:33,090 --> 00:06:35,810 char型の変数cに格納されている。 105 00:06:35,810 --> 00:06:37,790 >> 戻り値についてはどうですか? 106 00:06:37,790 --> 00:06:41,560 sscanfは、開始から終了までの入力行を解析します 107 00:06:41,560 --> 00:06:44,860 それが終わりに達したときに停止したりする場合、入力中の文字 108 00:06:44,860 --> 00:06:49,320 フォーマット文字と一致しないか、それは変換を行うことができないとき。 109 00:06:49,320 --> 00:06:52,690 それの戻り値は、それが停止する場合、単一のために使用されます。 110 00:06:52,690 --> 00:06:55,670 それは入力文字列の末尾に達したため、それが停止した場合 111 00:06:55,670 --> 00:07:00,630 どんな変換を行う前に、書式文字列の一部を一致に失敗するまで、 112 00:07:00,630 --> 00:07:04,840 その後、特殊な定数EOFが返されます。 113 00:07:04,840 --> 00:07:08,200 それ以外の場合は、成功したコンバージョン数を返します 114 00:07:08,200 --> 00:07:14,380 我々は2つ​​の変換のために求めてきましたので、それは、0,1、または2である可能性があります。 115 00:07:14,380 --> 00:07:19,000 我々の場合では、ユーザは、intとint型のみで入力したことを確認したい。 116 00:07:19,000 --> 00:07:23,370 >> そこで、我々は、sscanfが1を返すようにしたい。理由を参照してください? 117 00:07:23,370 --> 00:07:26,850 sscanfが0を返した場合は、変換は行われませんでした、 118 00:07:26,850 --> 00:07:31,690 したがって、ユーザーは入力の先頭にint型以外のものを入力しました。 119 00:07:31,690 --> 00:07:37,100 sscanfが2を返す場合、そのユーザーは、適切に、入力の開始時にそれを入力した 120 00:07:37,100 --> 00:07:41,390 しかし、彼らはその後、その後、いくつかの非空白文字で入力 121 00:07:41,390 --> 00:07:44,940 %cの変換が成功したからである。 122 00:07:44,940 --> 00:07:49,570 うわー、それは一つの関数呼び出しのために非常に長い説明だ。 123 00:07:49,570 --> 00:07:53,460 とにかく、あなたはsscanfとその兄弟の詳細情報が必要な場合、 124 00:07:53,460 --> 00:07:57,130 manページは、Google、または両方をチェックアウト。 125 00:07:57,130 --> 00:07:58,780 フォーマット文字列のオプションがたくさんあり​​ますが、 126 00:07:58,780 --> 00:08:03,830 C言語で文字列を解析しようとしたときに、これらを使用すると、手作業を大幅に節約することができ 127 00:08:03,830 --> 00:08:07,180 >> 見て、ライブラリ内の最後の関数は、GetStringメソッドです。 128 00:08:07,180 --> 00:08:10,310 それは、GetStringメソッドを正しく書くためのトリッキーな関数であることが判明 129 00:08:10,310 --> 00:08:14,290 それはそのような単純な、一般的な作業のように見えても。 130 00:08:14,290 --> 00:08:16,170 なぜこのような場合ですか? 131 00:08:16,170 --> 00:08:21,380 さて、それでは、例としては、ユーザがタイプインチその行を格納しようとしているかを考える 132 00:08:21,380 --> 00:08:23,880 文字列は文字の配列であるため、 133 00:08:23,880 --> 00:08:26,430 我々は、スタック上の配列に格納したい場合があり 134 00:08:26,430 --> 00:08:31,250 しかし、我々は我々がそれを宣言するときに配列があることを行っているどのくらい知っている必要があります。 135 00:08:31,250 --> 00:08:34,030 同様に、我々は、ヒープ上にそれを置きたい場合、 136 00:08:34,030 --> 00:08:38,090 我々は、malloc関数に我々が確保したいバイト数を渡す必要があります 137 00:08:38,090 --> 00:08:39,730 しかし、これは不可能です。 138 00:08:39,730 --> 00:08:42,760 我々は、ユーザーが入力しますどのように多くの文字全く分からない 139 00:08:42,760 --> 00:08:46,590 ユーザが実際にそれらを入力する前に行います。 140 00:08:46,590 --> 00:08:50,720 >> この問題に対する解決策は、素朴な、と言う、単にスペースの大きな塊を予約することです 141 00:08:50,720 --> 00:08:54,540 ユーザーの入力のために1000文字のブロック、 142 00:08:54,540 --> 00:08:57,980 ユーザーがその長い文字列を入力することはないと仮定します。 143 00:08:57,980 --> 00:09:00,810 これは2つの理由のために悪い考えです。 144 00:09:00,810 --> 00:09:05,280 まず、ユーザーは、通常、その長い文字列で入力しないと仮定 145 00:09:05,280 --> 00:09:07,610 あなたは多くのメモリを無駄でした。 146 00:09:07,610 --> 00:09:10,530 あなたがこれを行う場合、最近のマシンでは、これは問題ではないかもしれません 147 00:09:10,530 --> 00:09:13,890 1つまたは2つの孤立した事例では、 148 00:09:13,890 --> 00:09:17,630 しかし、あなたは、ループ内でユーザーの入力を取って、後で使用するために保存している場合、 149 00:09:17,630 --> 00:09:20,870 あなたはすぐにメモリのトンを吸うことができます。 150 00:09:20,870 --> 00:09:24,450 さらに、もしあなたが書いているプログラムが小さいコ​​ンピュータ用です - 151 00:09:24,450 --> 00:09:28,100 限られたメモリを搭載したスマートフォンや他の何かのようなデバイス - 152 00:09:28,100 --> 00:09:32,060 このソリューションは多くの高速化の問題が発生します。 153 00:09:32,060 --> 00:09:36,450 これをしないための第二は、より深刻な理由は、それが脆弱なプログラムを残すということです 154 00:09:36,450 --> 00:09:39,710 バッファオーバーフロー攻撃と呼ばれるものに。 155 00:09:39,710 --> 00:09:45,840 プログラミングでは、バッファは、一時的に入力または出力データを格納するために使用されるメモリです 156 00:09:45,840 --> 00:09:48,980 この場合には当社の1000-charのブロックどちらです。 157 00:09:48,980 --> 00:09:53,370 データブロックの末尾を超えて書き込まれたときにバッファオーバーフローが発生します。 158 00:09:53,370 --> 00:09:57,790 >> たとえば、ユーザーが実際に1000以上の文字でタイプをしている場合。 159 00:09:57,790 --> 00:10:01,570 配列を使用してプログラミングをするときに、誤ってこれを経験しているかもしれません。 160 00:10:01,570 --> 00:10:05,620 あなたは10 intの配列を持っている場合は、何も読み込みまたは書き込みしようとしているからあなたを止めるものはありません 161 00:10:05,620 --> 00:10:07,810 第十五intです。 162 00:10:07,810 --> 00:10:10,000 全くコンパイラの警告やエラーはありません。 163 00:10:10,000 --> 00:10:13,250 プログラムはただ直進失策とメモリにアクセス 164 00:10:13,250 --> 00:10:18,150 それが思うところ第十五int型は次のようになりますが、これはあなたの他の変数を上書きすることができます。 165 00:10:18,150 --> 00:10:22,040 最悪のケースでは、あなたのプログラムの内部の一部を上書きすることができます 166 00:10:22,040 --> 00:10:26,820 制御機構は、実際には別の命令を実行するようにプログラムを引き起こす 167 00:10:26,820 --> 00:10:28,340 あなたが意図したよりも。 168 00:10:28,340 --> 00:10:31,360 >> さて、それは誤ってこれを行うのが一般的ではないが、 169 00:10:31,360 --> 00:10:35,150 しかし、これは悪者がプログラムを破るために使用することはかなり一般的な技術である 170 00:10:35,150 --> 00:10:39,080 そして他の人のコンピュータ上で悪意のあるコードを置く。 171 00:10:39,080 --> 00:10:42,910 したがって、私達はちょうど私達の素朴なソリューションを使用することはできません。 172 00:10:42,910 --> 00:10:45,590 我々は、脆弱性があるから我々のプログラムを防止するための方法が必要 173 00:10:45,590 --> 00:10:47,880 バッファオーバーフロー攻撃へ。 174 00:10:47,880 --> 00:10:51,430 これを行うには、我々が読んとして私達のバッファが成長できることを確認する必要があります 175 00:10:51,430 --> 00:10:53,850 ユーザからの入力。 176 00:10:53,850 --> 00:10:57,440 解決法は?私たちは、ヒープに割り当てられたバッファを使用します。 177 00:10:57,440 --> 00:10:59,950 我々はそれがサイズ変更を使用してrealloc関数のサイズを変更することができるので、 178 00:10:59,950 --> 00:11:04,580 バッファ内の次の空きスロットのインデックス - と我々は2つ​​の番号を追跡 179 00:11:04,580 --> 00:11:08,390 とバッファの長さや容量。 180 00:11:08,390 --> 00:11:13,210 我々は、fgetc関数を使用して、一度に1ユーザから文字を読み取ります。 181 00:11:13,210 --> 00:11:19,360 STDIN - - fgetc関数が取る引数は、標準入力から文字列への参照である 182 00:11:19,360 --> 00:11:23,810 これは、ユーザの入力を転送するために使用される事前接続入力チャネルです 183 00:11:23,810 --> 00:11:26,270 端末からプログラムへ。 184 00:11:26,270 --> 00:11:29,890 >> 新しいキャラクターでユーザータイプは、我々は、インデックスかどうかを確認するたびに 185 00:11:29,890 --> 00:11:35,810 次の空きスロットに1を加えたバッファの容量を超えています。 186 00:11:35,810 --> 00:11:39,690 1は、次の空いているインデックスが5の場合、入ってくるので、 187 00:11:39,690 --> 00:11:44,150 その後、私たちのバッファの長さは0〜6インデキシングおかげでなければなりません。 188 00:11:44,150 --> 00:11:48,350 我々はバッファ内の領域を使い果たしてしまったなら、私たちはそれのサイズを変更しようとすると、 189 00:11:48,350 --> 00:11:51,690 我々はリサイズした回数を減らすようにそれを倍増 190 00:11:51,690 --> 00:11:54,760 ユーザーは本当に長い文字列で入力された場合。 191 00:11:54,760 --> 00:11:57,950 文字列が長すぎたどっているか、我々は、ヒープ·メモリーが不足した場合場合、 192 00:11:57,950 --> 00:12:01,350 私達は私達のバッファーと戻りnullを解放します。 193 00:12:01,350 --> 00:12:04,170 >> 最後に、我々はバッファにchar型を追加します。 194 00:12:04,170 --> 00:12:08,200 一旦ユーザヒットは、EnterキーまたはReturn、新しい行をシグナリング 195 00:12:08,200 --> 00:12:12,050 または特別CHAR - 制御d - 入力の終了を通知し、 196 00:12:12,050 --> 00:12:16,240 我々は、ユーザーが実際にはまったく何もで入力しているかどうかを確認を行う。 197 00:12:16,240 --> 00:12:18,820 そうでなかったら、私達はnullを返します。 198 00:12:18,820 --> 00:12:22,280 そうでなければ、私たちのバッファは、おそらく我々は必要以上に大きくなるので、 199 00:12:22,280 --> 00:12:24,830 最悪の場合、それは我々が必要とほぼ二倍だ 200 00:12:24,830 --> 00:12:27,830 私達は私達がサイズ変更する​​たびに倍増以来、 201 00:12:27,830 --> 00:12:31,840 我々は我々が必要とするスペースの量だけを使用して、文字列の新しいコピーを作成します。 202 00:12:31,840 --> 00:12:34,220 我々は、malloc呼び出しに余分な1を追加 203 00:12:34,220 --> 00:12:37,810 そんなに特別なヌル終端文字のためのスペースがあること - \ 0は、 204 00:12:37,810 --> 00:12:41,990 我々は残りの文字でコピーかつて我々は文字列に追加している、 205 00:12:41,990 --> 00:12:45,060 strncpyを使用する代わりにstrcpyの 206 00:12:45,060 --> 00:12:48,830 我々は我々がコピーしたい正確にどのように多くの文字を指定できるようにします。 207 00:12:48,830 --> 00:12:51,690 それは\ 0に達するまでは、strcpyをコピーします。 208 00:12:51,690 --> 00:12:55,740 その後、我々は我々のバッファを開放して、呼び出し元にコピーを返す。 209 00:12:55,740 --> 00:12:59,840 >> 誰がこのような単純な見せかけの機能を知っていたので複雑なのだろうか? 210 00:12:59,840 --> 00:13:02,820 今、あなたはCS50ライブラリに入るか知っている。 211 00:13:02,820 --> 00:13:06,470 >> 私の名前はネイトHardisonであり、これはCS50です。 212 00:13:06,470 --> 00:13:08,350 [CS50.TV]