1 00:00:00,000 --> 00:00:02,490 [Powered by Google Translate] [CS50圖書館] 2 00:00:02,490 --> 00:00:04,220 內特 - 哈迪森] [哈佛大學] 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庫上的視頻,我們來談談如何#include頭文件 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 持股量,範圍,標準的布爾值,標準庫。 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 >> 接下來的事情,你會看到在圖書館,我們定義一個新類型,叫做“string”。 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 長期的渴望,和字符串,用我們自己的字符串類型。 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 他們使用一個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 無論是一個字符,雙,花車等。 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 這意味著,sscanf的將嘗試一個十進制整數 - % - 和一個char - %C。 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的尋找字母a,後面跟著一個int, 95 00:05:59,800 --> 00:06:04,360 ,雖然它試圖將其轉換為int,它不會做任何事情的一個。 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值將被轉換並存儲在int變量n 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 這可能是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 檢查手冊頁,谷歌,或兩者。 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 這是一個壞主意,原因有兩個。 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 在一個或兩個分離的情況下, 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字符塊。 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個整數的數組,沒有什麼可以阻止你試圖讀取或寫入 161 00:10:05,620 --> 00:10:07,810 15日的詮釋。 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 如認為第15的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 而我們跟踪的兩個數字 - 在緩衝區中的下一個空槽的索引 179 00:11:04,580 --> 00:11:08,390 和緩衝區的長度或容量。 180 00:11:08,390 --> 00:11:13,210 我們讀到chars中從用戶1在一個時間使用fgetc函數。 181 00:11:13,210 --> 00:11:19,360 fgetc函數需要的參數 - STDIN - 是一個參考的標準輸入字符串, 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 然後我們緩衝區的長度必須是6感謝0索引。 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 >> 最後,我們追加字符的緩衝區。 194 00:12:04,170 --> 00:12:08,200 一旦用戶點擊進入或返回,標誌著一個新的生產線, 195 00:12:08,200 --> 00:12:12,050 或特殊字符 - 控制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 我們增加了一個額外的1 malloc調用, 203 00:12:34,220 --> 00:12:37,810 有特殊的NULL終止符的空間 ​​- 在“\ 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 STRCPY複製,直到它擊中了\ 0。 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 >> 我的名字是Nate哈迪森,這是CS50。 212 00:13:06,470 --> 00:13:08,350 [CS50.TV]