[Powered by Google Translate] [第5條:不太舒服] [內特 - 哈迪森,哈佛大學] 這是CS50。[CS50.TV] 所以歡迎回來,伙計們。 第5節。 在這一點上,已完成測驗,並看到了你所做的, 希望你覺得真的很不錯,因為我的分數在本節留下了非常深刻的印象。 對於我們的在線觀眾,我們有幾個問題 有關問題上的最後兩個問題 - 或測驗,而。 因此,我們要對這些真的很快,讓大家看到發生了什麼事 以及如何通過實際的解決方案,而不是僅僅查看解決方案本身。 我們要在過去的幾個問題真的很快,32和33。 只是,再次,使在線的觀眾可以看到這一點。 如果你把你的問題是32,第13頁, 13 16 32,問題是所有有關掉期。 這是所有關於交換兩個整數。 這是問題,一對夫婦在演講的時候,我們已經走了。 在這裡,我們要你做的是一個快速的記憶痕跡。 要填寫的值的變量,因為它們是在棧上 的代碼通過這個交換功能。 特別是我們正在尋找的 - 我打算把這款iPad - 特別是,我們看到的是,這條線在這裡6。 而它的編號為6的連續性與前面的問題。 我們想要做的是顯示或標籤的內存狀態 因為它是在的時候,當我們執行這條線6號, 這實際上是一個回報,我們這裡的交換功能。 如果我們向下滾動在這裡,我們看到的一切都在內存中的地址提供給我們的。 這是非常關鍵的,我們會回來的,在短短的時刻。 然後在這裡的底部,我們有一個小的內存圖,我們要參考。 其實我已經做到了這一點,在我的iPad。 所以,我要交替之間來回iPad和這個代碼僅作參考。 讓我們開始。首先,讓我們把注意力集中在這裡的第一對夫婦行的主要。 首先,我們要初始化x為1和y 2。 因此,我們有兩個整型變量,它們都是被放置在棧上。 我們打算把他們的A 1和A 2。 所以,如果我翻轉過來,希望我的iPad,讓我們來看看 - Apple TV的鏡像,和我們走吧。好吧。 所以,如果我翻轉過來,以我的iPad, 我想初始化x為1和y 2。 我們做到這一點很簡單,寫一個包裝盒中標記為x 1 2包裝盒中標記為Y。相當簡單的。 所以,現在讓我們回到筆記本電腦,看看接下來會發生什麼。 因此,這下一行是在事情變得棘手。 我們傳遞的地址的x和y的地址作為參數a和b的交換函數。 x和y的地址,該地址是我們無法計算的東西, 沒有這些子彈​​點就在這裡。 幸運的是,前兩個要點告訴我們的答案是什麼。 x的地址是10,在存儲器中,存儲器中的y的地址是14。 因此,那些得到的值通過在A和B的向上頂在我們的交換功能。 所以,再一次,切換回我們的圖,我可以寫在10 和14 b中。 現在,這一點是我們進行交換。 所以再次翻轉的筆記本電腦, 我們可以看到,交換的工作方式是我第一次解引用一個,並將結果存儲在tmp。 因此,解引用運算符說,“嘿,治療的變量a的地址的內容。 無論是存儲在該地址,並加載它。“ 你加載的變量將被存儲到我們的TMP變量。 翻轉到iPad。 如果我們去解決10,我們知道地址10是輸出變x 告訴我們,因為我們的子彈點x在內存中的地址是10。 因此,我們可以去那裡,得到了它的價值,這是1,因為我們看到我們的iPad, 並加載到tmp目錄。 再次,這是不是最後的內容。 我們要走過和結束時,我們會得到我們的最終狀態的程序。 但是現在,我們的值為1,存儲在tmp。 在這裡有一個快速的問題。 [亞歷山大]是解引用運算符 - 這只是前面的變量的明星權嗎? “是的。因此,解引用運算符,當我們打開再次回到我們的筆記本電腦, 這是明星就在眼前。 從這個意義上說,它是 - 你對比乘法運算符 這需要兩件事情:解引用運算符是一元運算符。 剛剛申請到一個值,而不是一個二元操作符, 適用於兩種不同的價值觀。 所以,這就是發生在這條線。 我們裝的值為1,並把它保存到我們的臨時整型變量。 下一行,我們的內容存儲的B - 或者說,我們,b是指向到的地方,一個是指向存儲的內容。 如果我們分析這個由右至左,我們將取消引用b, 我們要解決14,我們要搶的整數,它是有, 然後我們要去的地址10, 我們要拋的結果,我們解引用的b到該空間。 翻轉回我們的iPad,在這裡我們可以使這一點更具體的, 它可能會幫助,如果我在這裡寫號上的所有地址。 因此,我們知道,我們在y地址為14,x是地址10。 當我們開始在B,我們解引用b,我們要抓住的價值2。 我們要抓住這個值,因為是住在地址14的值。 我們打算把它的變量,居住地址10, 這是正確的,相應的變量x。 所以,我們可以做一點點的覆蓋這裡 我們擺脫我們的1,而我們寫了一個2。 因此,所有的好和良好的世界,即使我們已經覆蓋x現在。 我們已經存儲在我們的TMP變量x的舊值。 因此,我們可以完成交換的下一行。 翻轉回我們的筆記本電腦。 現在,剩下的就是我們的臨時整型變量的內容 並將它們存儲到地址b被保持住在變量。 因此,我們要有效地解引用b的變量 這是在它的地址是b持有, 我們將tmp是保存它的東西的價值。 翻轉到iPad一次。 我在這裡可以刪除此值,2, 相反,我們將複製到它的。 然後,下一行執行的,當然 - 如果我們翻回的筆記本電腦 - 這是6點, 這是我們希望我們的圖完全充滿。 所以,翻轉到iPad,只是讓你可以看到已完成的圖, 你可以看到,我們在10,14在b,1 tmp中,2 x中,在y和1。 是否有任何問題嗎? 這是否更有意義,已經走過嗎? 就不是那麼有意義嗎?但願不會。好吧。 指針是一個非常棘手的問題。 與我們合作的球員之一有一個很常見的說法: “要了解指針,你必須先了解指針。” 我認為這是非常真實的。它確實需要一段時間才能使用它。 抽籤的圖片,像這樣的抽籤的內存圖是非常有幫助的, 後例如,在你走過的例子後,例如, 它會開始做一些更有意義一些更有意義和更有意義一點。 終於,有一天,你會擁有這一切完全掌握。 任何問題之前,我們進入下一個問題嗎?好的。 因此,翻轉的筆記本電腦。 我們的下一個問題是問題33號文件I / O。 這是一個有點放大。 問題33 - 是嗎? [丹尼爾]我剛做了一個簡單的問題。此星或星號, 這就是所謂的提領時,你使用一個星號之前。 它是什麼時調用之前使用的符號嗎? >>“符號前是運營商的地址。 因此,讓我們向上卷動。 哎呀。我在變焦模式,所以我真的不能滾動。 如果我們看一下這段代碼,在這裡真的很快, 再次,同樣的事情發生。 如果我們看一下這段代碼,在這裡,在這條線,我們撥打電話交換, “&”只是說“的地址在變量x的生活。” 當你的編譯器編譯你的代碼, 它實際物理標記在內存中所有的變量住的地方。 所以編譯器就可以做一次,它的編譯一切, 都知道,“哦,我把x地址10地址14,我要把Y”。 然後,它可以為你填寫這些值。 這樣你就可以 - 它可以通過本和通&Y以及。 這些人得到的地址,但他們也,當你通過它們的交換功能, 這此int *類型的信息,在這裡,告訴編譯器, “好了,我們將要解釋這個地址作為一個整型變量的地址。” 一個int作為一個地址,它是從一個字符變量的地址不同 因為一個int佔用了一個32位的機器上,佔用4個字節的空間, 而一個字符只佔用1個字節的空間。 因此,它是重要的是要知道什麼是 - 生活中,什麼類型的值 住在傳入的地址,進行了 或地址,你的工作。 這樣一來,你知道多少個字節的信息的實際加載的RAM。 然後,是的,這種反引用運算符,就像你問, 在一個特定的地址去訪問信息。 所以說,這是一個變量,在這裡,治療的內容作為地址, 去到該地址,並拉出,到寄存器加載到處理器中,負載 實際值或住在該地址的內容。 還有什麼問題嗎?這些都是很好的問題。 這是一個很多新的術語。 這也是一種時髦,看到&和*在不同的地方。 好的。 所以回到問題33,文件I / O。 這是一個發生的幾件事情,我認為這些問題。 一,這是一個相當新的課題。 據介紹很快測驗前, 然後,我認為這是種像這些字中的數學問題之一 ,他們給你很多的信息,但實際上你不使用一噸的。 這個問題的第一部分描述的是一個CSV文件是什麼。 現在,一個CSV文件,根據描述,是一個以逗號分隔值文件。 因此,這些在所有有趣的是,和你使用過他們的原因, ,因為,你們有多少人曾經使用Excel之類的東西嗎? 圖你最有可能,或將使用在一些點在你的生活中。 你會使用Excel之類的東西。 為了得到數據的Excel電子表格或做任何形式的處理, 如果你想編寫一個C程序或Python程序,Java程序, 處理的數據儲存在那裡, 把它弄出來的最常見的方式之一,是一個CSV文件中。 ,你可以打開Excel中,當你去到“另存為”對話, 你可以得到一個實際的CSV文件。 很方便的知道如何處理這些事情。 它的工作方式是,它是類似的 - 我的意思是,它本質上是模仿電子表格, 在那裡,我們在這裡看到,最左邊的一塊, 我們所有的姓氏。 因此,我們有馬蘭,那麼哈迪森,然後鮑登,MacWilliam,然後生。 所有的姓氏。然後一個逗號分隔的姓氏第一個名字。 大衛奈特,搶劫,張宇,和Zamyla。 我總是混淆:羅比和湯姆。 然後,終於,第三列是電子郵件地址。 一旦你了解了,其餘的程序是非常簡單的實現。 我們所做的,以模仿相同的結構在我們的C程序 是我們使用了一個結構。 我們將開始播放這一點以及。 我們看到了他們的第一個問題集3,當我們正在處理的字典點點。 但是,這名員工結構存儲一個姓氏,一個名字,和電子郵件。 就像我們的CSV文件存儲。 因此,這只是從一種格式轉換到另一個。 我們必須轉換,在這種情況下,工作人員成一條線的結構的, 一個以逗號分隔的行,就這樣。 這是否有意義嗎?你們都採取了有獎問答, 所以我想你已經至少有一些時間來思考這個問題。 在租用功能,這道題我們 - 我們將在這一點位變焦 - 在人員結構,人員結構,名稱為s, 並附加其內容到我們的staff.csv文件。 事實證明,這是非常簡單的使用。 我們將發揮著這些功能,今天多一點點。 但是,在這種情況下,fprintf函數是真正的關鍵。 因此,fprintf,我們可以打印,就像你們一直在使用printf的整個任期。 您可以輸出到一個文件中的行。 因此,而不是只是通常的printf調用,你給它一個格式字符串 然後你替換所有的變量與下面的參數, 與fprintf,你的第一個參數,而不是你要寫入的文件。 如果我們要看看這個設備,例如,人fprintf, 我們可以看到的printf和fprintf的區別。 我將在這裡一點點放大。 因此,用printf,我們給它一個格式字符串,然後在隨後的參數 更換或替換成我們的格式化字符串中的所有的變量。 而與fprintf,第一個參數是事實上,這稱為流文件*。 在這裡,我們的租賃, 我們已經為我們打開了我們的文件*流。 那這是什麼第一線;它打開了staff.csv的文件, 它以附加的方式打開它,並為我們做了所有剩下的是 編寫人員結構的文件。 而且,讓我們來看看,我想使用iPad? 我會使用iPad。我們有無效的 - 讓我們把這個放在桌子上,這樣我就可以寫一個好一點的 - 無效租用,它需要一個參數,稱為s的人員結構。 得到了我們的大括號,我們有我們的文件*文件, 我們有我們的FOPEN線給我們, ,我就寫點,因為它已經在百科。 然後在我們的下一行,我們要撥打電話為fprintf 和我們將要通過的文件中,我們要打印的, 然後我們的格式化字符串,其中 - 我會告訴你們告訴我它是什麼樣子。 你怎麼樣,Stella嗎?你知道的格式字符串的第一部分看起來像什麼? 斯特拉]我不知道。 >>隨意問吉米。 你知道嗎,吉米? [麥]這是最後一個嗎?我不知道。我不能完全肯定。 “好了。怎麼樣,有沒有人得到這個正確的成績? 所有權利。 原來,這裡所有我們需要做的是,我們希望我們的人員結構各部分的 作為一個字符串到我們的文件被打印出來。 我們只是使用字符串替換字符,三個不同的時間,因為我們有個姓 其次是逗號,然後一個名字後跟一個逗號, 然後最後的電子郵件地址,其次是 - 這是不 適合我的屏幕上 - 但它後面的一個換行符。 所以我要去寫它只是出現了下滑。 然後按照我們的格式化字符串, 我們只需要換人,我們使用點表示法訪問 我們所看到的問題集3。 我們可以使用s.last,s.first,s.email 在這三個值到我們的格式化字符串來代替。 所以,這是怎麼去的?有意義嗎? 是嗎?不是嗎?有可能嗎?好吧。 最後一點,我們做印刷後,我們已經打開的文件後,我們已經: 每當我們打開一個文件,我們總是要記住關閉。 否則,我們將最終的內存洩漏, 使用文件描述符。 因此,要關閉它,我們使用哪些功能?丹尼爾? [丹尼爾] FCLOSE? >> FCLOSE,準確。 因此,這個問題的最後一部分是正確地關閉文件,使用fclose函數, 它只是看起來像。 太瘋狂了。 酷。 所以這就是問題的測驗33。 I / O來了,我們肯定將有更多的文件。 我們會做多一點點在今天的演講,或在今天的部分, 因為這是怎麼回事形成這個即將到來的pset中的大部分。 讓我們繼續在這一點上測驗。是嗎? [夏洛特]為什麼FCLOSE(文件),而不是FCLOSE(staff.csv)? “啊。因為事實證明 - 這樣的問題,這是一個偉大的, 正因為如此,當我們寫FCLOSE,我們,寫FCLOSE(文件)明星變量 而不是在文件名的,staff.csv?是正確的嗎?是啊。 因此,讓我們一起來看看。如果我切換回我的筆記本電腦, 讓我們來看看fclose函數。 因此,fclose函數關閉一個流,它需要的指針,我們要關閉的流, 而不是實際文件名,我們要關閉。 這是因為在幕後,當你撥打電話的FOPEN, 當你打開一個文件時,你實際上是在分配內存來存儲信息的文件。 所以,你必須有信息的文件的文件指針, 如它是開放的,它的大小,在那裡你目前在該文件中, 這樣就可以在文件中讀取和寫入到特定的地方調用。 您最終成交的指針,而不是關閉的文件名。 是嗎? [丹尼爾]因此,為了使用租用,你會說 - 它是如何獲得用戶輸入? fprintf像在這個意義上,它會等待用戶輸入的GetString 要求用戶輸入 - 等待你輸入這三樣東西? 或者你需要使用的東西,落實租? >>呀。因此,我們不 - 的問題是,如何得到用戶輸入 為了實現租嗎?我們這裡是出租的呼叫者, 在這種人員結構,所有的數據存儲結構已經通過。 因此,fprintf是只是數據直接寫入到文件。 有沒有等待用戶輸入。 用戶是否已經輸入適當的把它在這名員工結構。 的事情,當然,將打破,如果這些指針為空的, 因此,我們在這裡向後滾動,我們期待在我們的結構。 我們有字符串,字符串的第一個字符串的郵件。 現在我們知道,所有的那些真正的,引擎蓋下,是char *變量。 這可能會或可能不會被指向空。 他們可能指向堆內存, 也許內存堆棧。 我們真的不知道,但如果其中任一指針是空的,或無效的, 一定會崩潰,我們的租賃功能。 這是什麼,這是一種超越的考試範圍。 我們不擔心這一點。 大。好吧。因此移動從測驗。 讓我們來關閉這個傢伙,我們要看看在pset中4。 所以,如果你們看在pset規範,一旦你可以訪問它的,cs50.net/quizzes, 我們要經過幾個部分問題,今天。 我向下滾動 - 部分問題開始的第三頁上的pset的規範。 第一部分要求你去觀看短重定向和管道。 這是一個很酷的短,顯示出一些新的,很酷的命令行技巧,您可以使用。 然後,我們有幾個問題要問你。 這第一個問題,關於流,輸出寫入默認情況下, 一種感動剛才只是一點點。 我們剛才談到的這fp​​rintf需要在一個文件中*流作為它的參數。 FCLOSE需要在一個文件中*流為好, 和返回值的FOPEN給你一個文件*流以及。 究其原因,我們還沒有看到那些之前我們已經處理了printf 是因為輸出有一個默認的流。 而它的默認流寫 你會發現在短期內。 所以,一定要看看它。 在今天的部分,我們要談一點關於GDB ,因為你是比較熟悉的,它更多的練習,你得到它, 你能夠更好地將追捕在自己的代碼錯誤。 這加快了極大的調試過程。 因此,通過使用printf,每次你這樣做,你必須重新編譯你的代碼, 你必須再次運行它,有時你必須左右移動printf調用, 註釋掉的代碼,它只是需要一段時間。 我們的目標是嘗試說服你,你基本上可以用GDB, 在任何時候在你的代碼中的printf什麼,你永遠不會有重新編譯它。 你從來沒有開始,並不斷猜測的printf下。 要做的第一件事是複製這條線和部分代碼的網站。 我說,“wget的http://cdn.cs50.net”這行代碼複製。 我要複製它。 我要到我的設備,縮放,所以你可以看到我在做什麼, 將其粘貼在那裡,當我按下Enter鍵,這個wget命令從字面上是一個網絡獲得。 這是怎麼回事拉下這個文件在互聯網上傳播, 它的將它保存到當前目錄中。 現在,如果我列出當前目錄中,你可以看到,我已經了這section5.zip的文件右鍵在那裡。 那傢伙處理的方式是將其解壓縮, 您可以在命令行中,就是這樣。 Section5.zip。 將它解壓縮,創建文件夾對我來說, 膨脹中的所有內容,讓他們在那裡。 所以,現在我可以進入我的第5節目錄使用cd命令。 清除屏幕採用了明確。因此,清除屏幕。 現在我有一個乾淨漂亮的終端處理。 現在,如果我列出我看到在這個目錄中的所有文件, 你看到我已經得到四個文件:buggy1,buggy2,buggy3,並buggy4,。 我也得到了相應的文件。 我們不會看的。c文件。 相反,我們要使用它們時,我們打開了GDB。 我們已經把他們周圍,使我們有機會獲得實際的源代碼,當我們使用GDB, 這部分的組成部分,但我們的目標是修改周圍的GDB 看看我們如何能夠用它來找出什麼地方出了錯這四個錯誤的程序。 所以我們僅僅是在房間裡真的很快, 我要問別人的bug的程序運行一個, ,然後我們會去通過GDB作為一個群體,我們將看看我們能做些什麼來解決這些計劃, 或者至少確定發生了什麼事情錯在他們每個人。 讓我們從這裡開始與丹尼爾。你會運行buggy1?讓我們看看會發生什麼。 丹尼爾說,有一個應用程序錯誤。 >>呀。沒錯。 所以,如果我運行buggy1,我得到了賽格故障。 在這一點上,我可以去開拓buggy1.c, 試圖找出什麼地方出了錯, 但該段故障錯誤最厭惡的事情之一 的是,它不會告訴你哪一行的程序的東西實際是錯誤的,並打破。 你有看的代碼 找出猜測檢查或printf來看看會發生什麼錯誤。 關於GDB的最酷的事情之一是,它是真的,真的很容易 要弄清楚的線在你的程序崩潰。 這是完全值得的,使用它,即使只是。 啟動GDB,我輸入GDB,然後我給它的,我想運行的可執行文件的路徑。 在這裡,我打字:GDB ./buggy1。 按下回車鍵。給了我這一切版權信息, 在這裡你會看到這條線,說:“從/ home /讀取符號 jharvard/section5/buggy1。“ 如果一切順利的話,你會看到它看起來像這樣打印出的信息。 它會讀取符號,它會說:“我從你的可執行文件,讀取符號” ,然後將這個“做”的消息在這裡。 如果你看到一些其他的變化,你看到它找不到符號 或類似的東西,這是什麼意思,你只需要編譯你的可執行文件。 當我們使用GDB編譯程序,我們必須使用特殊的-g標誌, ,這就是默認情況下,如果你編譯你的程序,通過鍵入make 或車或恢復,任何人。 但是,如果你正在編譯手動鐺,那麼你就必須去,其中包括,使用-g標誌。 在這一點上,現在,我們有我們的GDB提示, 這是很簡單的運行程序。 我們可以鍵入運行,我們就可以輸入r。 大多數GDB命令都可以縮寫。 通常只是一個或幾個字母,這是相當不錯的。 因此,薩阿德,如果你輸入r,並按下Enter鍵,會發生什麼呢? 薩阿德我SIGSEGV,分割故障,那麼這一切的官樣文章。 >>呀。 就像我們在屏幕上看到,現在,像薩阿德說, 當我們鍵入run或r並按下回車鍵,我們仍然得到同樣的賽格故障。 因此,使用GDB不解決我們的問題。 但它為我們提供了一些官樣文章,而事實證明,這種官樣文章 實際上告訴了我們它的發生。 要解析這一點,第一位的功能是,在這一切是怎麼回事了。 有__ strcmp_sse4_2,它告訴我們發生了什麼事在這個文件中的 被稱為sysdeps/i386,這一切,再次,種混亂 - 而行254。 這是一種很難解析。通常,當你看到這樣的東西, 這意味著它在一個系統庫段斷層。 因此,做的strcmp。你們已經看到的strcmp。 不是太瘋狂了,但是這是否意味著STRCMP破損或STRCMP,有一個問題嗎? 你怎麼想,亞歷山大? [亞歷山大]是 - 是254的行嗎? - 不是二進制的,但是這不是他們的天花板, 然後有每個函數的另一種語言。 254在該函數中,或者 - ? >>這是第254行。它看起來像。文件,所以它的彙編代碼可能。 但是,我想更緊迫的事情,因為我們已經得到了賽格故障, 它看起來像它來自strcmp函數, ,那麼,這是否意味著strcmp的壞了? 它不應該有希望。因此,僅僅因為你有一個分割故障 系統的功能之一,通常這意味著,你只是還沒有被稱為是正確的。 的事情弄清楚什麼是怎麼回事 當你看到這樣的瘋狂的東西,每當你看到一個段故障, 特別是如果你有一個程序,使用多只主, 使用的回溯。 我寫BT,而不是全面的回溯字縮寫回溯。 但是,夏洛特,當你鍵入BT並按下回車鍵,會發生什麼呢? 夏洛特它讓我看到兩行,0行和第1行。 >>呀。所以第0行和第1行。 這些是實際的棧幀目前在播放的時候,你的程序崩潰。 從最上面的幀,幀0開始,並持續到最底層的,這是第1幀。 我們的最高框架是strcmp的框架,。 你可以認為這是類似的問題,我們只是在做測驗的指針, 我們交換堆棧幀上的主棧幀的頂部, 和我們的互換使用,主要使用的變量的頂部上的變量。 在這裡我們的崩潰發生在我們的strcmp函數,這被稱為我們的主要功能, 回溯,不僅給我們的功能在裡面的東西沒有, 但它也告訴我們,這裡的一切,被稱為。 所以,如果我多一點點滾動的權利, 我們可以看到,是的,我們在這個的strcmp,sse4.s文件的254行。 但有人呼籲在buggy1.c,6號線。 因此,這意味著我們可以做的 - 是我們可以去看看,看看發生了什麼事 在6號線buggy1.c。 同樣,也有幾種方法可以做到這一點。一個是退出GDB 或者你的代碼在另一個窗口中打開和交叉引用。 ,其本身而言,是非常方便的,因為現在,如果你在辦公時間 和你已經有了一個段故障和TF想知道一切都被打破, 你可以說,“哦,第6行,我不知道這是怎麼回事, 但6號線是什麼導致我的計劃打破。“ 做到這一點的另一種方法是,你可以使用此命令列表中GDB。 您也可以將其縮寫為升。 因此,如果我們打升,我們在這裡嗎? 我們得到了一大堆怪異的東西。 這是實際的彙編代碼 是在strcmp_sse4_2。 這看起來有點時髦的, 的原因,我們正在這是,因為現在, GDB我們在第0幀。 所以,任何時候我們看變量,任何時候我們來看看源代碼, 我們正在尋找源代碼的堆棧幀,涉及到我們目前所在 因此,為了獲得什麼有意義的事情,我們必須 轉移到更有意義的棧幀。 在這種情況下,主要的堆棧幀將使一些更有意義, 因為這實際上是我們寫的代碼。 strcmp的代碼。 的方式,可以在幀之間移動,在這種情況下,因為我們有兩個, 我們有0和1, 你這樣做的up和down命令。 如果我把一幀, 現在,我在主堆棧幀。 我可以向下移動到的地方,我回去, 再上去,走了,再上去。 如果你這樣做你的程序在GDB,你得到一個崩潰時,你會得到回溯, 你看到它的一些文件,你不知道發生了什麼事情。 您嘗試列表,代碼並不很熟悉你, 一起來看看在您的框架,並找出你在哪裡。 你可能在錯誤的堆棧幀。 或者至少,你是不是你真的可以調試堆棧幀中。 現在,我們在適當的堆棧幀,我們在主, 現在我們可以使用list命令可以計算出該行是什麼。 你也可以看到它,它打印我們就在這裡。 但是,我們可以打列出所有相同,列表,為我們提供了這個漂亮的打印輸出 實際的源代碼,在這裡是怎麼回事。 特別是,我們可以看一下在第6行。 我們可以看到,在這裡發生了什麼事情。 它看起來像我們正在做的字符串比較 字符串“CS50石頭”和argv [1]。 他到這裡來是崩潰。 所以,大小姐,你有什麼想法什麼可能會在這裡? [大小姐]我不知道為什麼它的崩潰。 >>你不知道為什麼它的崩潰? 吉米,有什麼想法嗎? [麥]我不能完全肯定,但我們最後一次使用的字符串比較, 或strcmp的,我們有三種不同的情況下。 我們沒有一個==,我不認為,正確的,第一行。 相反,它被分離成三個,一個是== 0, 1 <0,我覺得,一個是> 0。 因此,也許這樣的事情? >>呀。所以有這個問題 我們做的比較正確? Stella嗎?有什麼想法? 斯特拉]我不知道。 >>不知道。丹尼爾?思考?好吧。 事實證明,所發生的事情在這裡是當我們運行程序時, 我們得到了賽格故障,當你第一次運行的程序,丹尼爾, 你給它任何命令行參數? [丹尼爾]第>>號在這種情況下,是什麼時,argv [1]? >>沒有任何價值。 “沒錯。 那麼,有沒有適當的字符串值。 但有一定的價值。被存儲在那裡的價值是什麼? >> A垃圾的價值呢? >>它要么是一個垃圾值,或者,在這種情況下, argv數組結束始終與null的終止。 所以實際上存儲在那裡是空的。 另一種方法解決這個問題,而不是想著它通過, 是要設法把它打印出來。 這是我說的,使用GDB是偉大的, 因為你可以打印出所有的變量,所有你想要的值 使用這種方便,花花公子p命令。 所以,如果我鍵入p,然後我輸入一個變量的值或變量的名稱, 說是argc,我看到argc是1。 如果我想打印出來的argv [0],我可以這樣做,就這樣。 就像我們所看到的,ARGV [0]是你的程序的名稱, 總是的可執行文件的名稱。 這裡你可以看到它有完整的路徑名。 我還可以打印出的argv [1],看看會發生什麼。 在這裡,我們得到了這種神秘的值。 我們拿到的這款0x0的。 記得在學期開始時,我們談到十六進制數字嗎? 或者說,在年底​​的pset 0的小問題如何表示十六進制50? 我們編寫的十六進制數在CS,只是為了不混淆自己 帶小數點的數字,是我們始終以0x前綴。 因此,這0x前綴始終只是解釋為十六進制數, 而不是字符串,而不是作為一個十進制數,而不是作為一個二進制數。 由於5-0是一個有效的十六進制數。 這是一個十進制數,50。 因此,這是我們如何消除歧義。 因此,0x0的裝置,十六進制的0,這也是十進制0,二進制0。 只是,它的值為0。 事實證明,這是空的,其實是在內存中。 NULL就是0。 在這裡,存儲元件的argv [1]為null。 因此,我們試圖比較“CS50石頭”字符串為空字符串。 因此,提領空,試圖訪問空的東西, 通常會造成某種分割故障或其他不好的事情發生。 而事實證明,STRCMP不請檢查 您是否已經通過了一個值,該值是空的。 相反,它只是前進,試圖做的事情, ,如果段故障,段故障,這是你的問題。你必須去解決它。 真的很快,我們會如何解決這個問題?夏洛特? [夏洛特,您可以檢查使用的話。 因此,如果argv [1]為null == 0,則返回1,什麼不知所云。 >>呀。所以這是一個偉大的方式來做到這一點,我們可以查看, 我們的價值傳遞到strcmp的,ARGV [1],它是空? 如果是null,那麼我們可以這樣說好了,中止。 更常見的方式做,這是使用的argc的值。 在這裡你可以看到在開始的主, 我們省略了,第一,我們通常做測試時,我們使用命令行參數, 這是我們ARGC值測試是否是我們所期望的。 在這種情況下,我們預計至少需要兩個參數, 的程序名加一個其它。 因為我們使用的第二個參數在這裡。 因此,有一些測試的結果之前,在我們的strcmp呼叫 argv是,測試是否至少2個,也做了同樣的事情。 我們可以看到,如果再次運行程序。 您可以隨時重新啟動你的程序在GDB中,這是非常好的。 您可以運行,而當你傳遞參數給你的程序, 通過他們,當你調用運行,而不是當你啟動GDB。 這樣,你可以每次都用不同的參數調用程序。 所以運行,或者,我可以輸入r,,讓我們看看會發生什麼,如果我們輸入“你好”。 它總是會問你,如果你想從一開始就再次啟動。 通常情況下,你想從一開始就再次啟動它。 而在這一點上,它會重新啟動一次,它打印出 該程序,我們正在運行,buggy1的說法您好, 並打印出這個標準了,它說,“你得到一個D,”悲傷的臉。 但是,我們不段錯誤。它說,過程正常退出。 所以,看起來還不錯。 沒有更多的賽格故障,我們做了過去, 所以看起來這的確是段故障錯誤,我們得到。 不幸的是,它告訴我們,我們需要一個D. 我們可以回去看一下代碼,看看在那裡發生了什麼事情 要弄清楚什麼是 - 為什麼它告訴我們,我們得到了D. 讓我們來看看,這裡被這個printf說,你有D. 如果我們輸入列表,你繼續輸入列表,它不斷迭代,通過你們的節目, 因此,它會告訴你你的程序的前幾行。 然後,它會告訴你在接下來的幾行字,下一個塊和下一塊。 它會繼續嘗試下去。 現在,我們會得到“線16號超出範圍。” 因為它只有15行。 如果你到了這一點,你想知道,我該怎麼辦?“你可以用help命令。 使用幫助“,然後給它一個命令的名稱。 你看到GDB為我們提供了這樣的東西。 它說,“不帶參數,列出了10個多行後或以前上市。 列表 - 列出了前十行 - “ 所以,讓我們嘗試使用列表中減去。 這列出的前10行,你可以玩弄名單一點點。 你可以做的列表,列表 - ,你甚至可以給列出一個數字,如清單8, ,它就會列出了10條線,8號線附近。 你可以看到在這裡發生了什麼事,是你已經有了一個簡單的if else。 如果您鍵入在CS50岩石,它會打印出“你會得到一個A.” 否則,它會打印出“你得到了D.” 遊民鎮。好的。是嗎? [丹尼爾]因此,當我試圖這樣做CS50不帶引號的岩石, 它說:“你得到了D.” 我需要引號來得到它的工作,這是為什麼? >>呀。事實證明,當 - 這是另一個有趣的小花絮 - 當您運行該程序,如果我們運行和輸入CS50岩石, 就像丹尼爾說他這樣做,按Enter鍵, 它說,我們得到了D. 而問題是,這是為什麼? 而事實證明,我們的終端和的GDB解析這些作為兩個單獨的參數。 因為當有一個空間,這暗示 第一個參數結束,即將開始的下一個參數。 結合的方式一分為二,或遺憾的是,成為一個參數, 使用引號。 所以現在,如果我們把它放在引號並再次運行它,我們可以得到一個A。 因此,只要回顧一下,沒有引號,的CS50和岩石被解析為兩個獨立的參數。 隨著行情中,它作為一個參數完全解析。 我們可以看到這一個斷點。 到目前為止,我們已經運行我們的程序,它的運行 直到它賽格故障或點擊錯誤 或直到它已退出所有已經完全正常。 這並不一定是最有幫助的事情,因為有時 你有一個錯誤在你的程序中,但它不是一個分割故障導致。 它不會導致程序停止或類似的東西。 GDB的方式來獲得你的程序在一個特定的點暫停 是設置一個斷點。 你可以做到這一點,一個函數名上設置一個斷點 或者您可以設置一個特定的代碼行上設置一個斷點。 我想設置斷點的函數名,因為 - 簡單易記, 如果你真的一點點去改變你的源代碼, 您的斷點將留在你的代碼在同一個地方。 而如果你使用的是行號,行號更改 因為你添加或刪除了一些代碼,然後斷點都完全搞砸了。 其中最常見的事情,我做的是設置一個斷點上的主要功能。 我經常會啟動GDB,我會鍵入b主,按下回車鍵,將設置一個斷點 的主要功能,只是說,“暫停程序盡快開始運行,” 這樣一來,當我運行我的程序,比方說,CS50岩石的兩個參數 並按下回車鍵,得到的主要功能,它就停在第一線, 前strcmp函數求值。 由於我停頓了一下,現在我可以開始擺弄周圍,看是怎麼回事 所有的不同的變量傳遞到我的程序。 在這裡,我可以打印出argc和看看會發生什麼。 請參閱argc是3,因為它有3個不同的值。 它得名的程序,它得到的第一個參數,第二個參數。 我們可以打印在ARGV [0],ARGV [1]和argv [2]。 所以,現在你也可以看到為什麼這STRCMP調用是要失敗的, 因為你看到,它並分裂成兩個獨立的參數的CS50和岩石。 在這一點上,一旦你已經打了斷點,你可以通過你的程序繼續加強 一行行,而不是重新啟動程序。 所以,如果你不想再次啟動程序,只是繼續從這裡開始, 您可以使用continue命令,並繼續將運行該程序的結束。 就像它在這裡做的。 不過,如果我重新啟動程序,CS50岩石,它打我的斷點再次, 而這一次,如果我不想去,一路過關斬將,其餘的程序, 我可以用下一個命令,這也是我縮寫與n。 而這將逐步的程序行線。 所以,你可以看的東西執行,作為變量的變化,事情得到更新。 這是相當不錯的。 其他很酷的事情,而不是一遍又一遍,再重複同樣的命令, 所以在這裡,如果你只需要敲擊回車 - 你看,我沒有在任何輸入 - 如果我只是打回車鍵,將重複前面的命令, 或以前的GDB命令,我剛剛放進去。 我可以保持按下回車鍵,它會通過我的代碼行的行不斷加強。 我會鼓勵你們去看看其他bug的程序。 我們沒有時間去通過所有這些今天在第。 源代碼是存在的,所以你可以看到這是怎麼回事 如果你真的卡幕後, 但最起碼,練習啟動GDB, 運行該程序,直到它打破了你, 回溯,搞清楚什麼樣的功能崩潰, 哪一行,打印出一些變量的值, 只是讓你的感覺,因為這將真正幫助你前進。 在這一點上,我們要退出的GDB,你退出或者只是q。 如果你的程序的運行仍然在中間,並​​沒有退出, 它總是會問你:“你確定你真的要退出嗎?” 您可以打是肯定的。 現在,我們要看看我們的下一個問題,這是貓的程序。 如果你看重定向和管道短,你會看到湯米使用這個程序 基本上所有輸出的文件打印到屏幕上。 所以,如果我遇到貓,這實際上是一個內置的程序到設備, ,如果你有Mac電腦,你可以做到這一點在您的Mac上,如果你打開終端。 而我們 - 貓,比方說,cp.c,並按下回車鍵。 這是什麼做的,如果我們向上滾動一點點,看到我們跑線, 我們跑了cat命令,它實際上只是打印出來的內容cp.c我們的屏幕上。 我們可以再次運行它,你可以把多個文件一起。 所以,你可以做貓cp.c,那麼我們也可以連接起來的cat.c的文件, 這是我們寫的程序, 它會同時打印文件備份到我們的屏幕上。 因此,如果我們向上滾動一點點,我們看到,當我們跑了這貓cp.c,cat.c, 首先它打印出來cp文件,然後在下面的,它打印出cat.c的文件就在這裡。 我們將用此來只是讓我們的腳濕。 玩簡單的打印到終端,請參閱如何工作的。 如果你們打開用gedit cat.c,按Enter鍵, 你可以看到我們寫的程序。 我們這個漂亮的鍋爐板,所以我們不必花時間打字了這一切。 我們還檢查傳入的參數 我們打印出不錯的用法消息。 這是諸如此類的事情,再次,就像我們一直在談論, 這幾乎就像肌肉記憶。 只記得繼續做同樣的東西 總是打印出某種有用的消息 讓人們知道如何運行你的程序。 隨著貓,它是非常簡單的,我們只是要通過不同的參數 被傳遞到我們的計劃,我們要打印 其內容在一個時間的一個屏幕。 為了打印文件輸出到屏幕上,我們要做的東西非常相似 我們所做的,在測驗結束時。 在測驗結束,聘用程序,我們要打開一個文件, 然後我們有打印到它。 在這種情況下,我們要打開一個文件,我們要讀取它,而不是。 然後我們要打印的,而不是到一個文件中,我們將要打印到屏幕上。 所以打印到屏幕上,你用printf之前全部完成。 所以這是不是太瘋狂了。 但是,讀取一個文件是一種奇怪的。 我們將通過那麼一點點的時間。 如果你們回去,最後一個問題,您的測驗,問題33, 我們要在這裡做的第一行,打開文件,非常類似於我們在那裡做了什麼。 因此,斯特拉,是什麼樣子,當我們打開一個文件時,該行嗎? [特拉]資本FILE *文件 - “”好了。 >> - 是平等的FOPEN。 “是啊。 在這種情況下是什麼?這是在註釋中。 >>這是在評論嗎?的argv [i]的和r? “沒錯。正確的。斯特拉是完全正確的。 這是行看起來像什麼。 我們將得到一個文件流變量,它存儲在一個FILE *,所以全部大寫字母, 將文件FILE *,這個變量的名稱。 無論我們喜歡,我們可以把它稱為。我們可以把它first_file,或file_i的,無論我們想要的。 在命令行上這個節目,然後通過的文件名。 因此,它存儲在argv [我],然後我們要打開這個文件在讀取模式。 現在,我們已經打開了文件,有什麼事情,我們總是要記住做 每當我們打開一個文件?關閉它。 所以,大小姐,我們該如何關閉一個文件? [大小姐] FCLOSE(文件)>> FCLOSE(文件)。沒錯。 大。好吧。如果我們看一下在這裡做評論, 它說,“開放的argv [i]和它的內容打印到stdout。” 標準輸出是一個奇怪的名字。 stdout是我們的一種說法 我們要打印到終端,我們希望將其打印到標準輸出流。 事實上,我們可以擺脫這個評論就在這裡。 我要複製並粘貼它,因為這是我們所做的。 在這一點上,現在我們要讀取的文件的點點滴滴。 我們已經討論了一對夫婦的讀取文件的方式。 哪些是你最喜歡這麼遠嗎? 哪種方式,你見過,你還記得,讀文件? [丹尼爾·弗里德? >>弗里德?因此,弗里德是其中之一。吉米,你知不知道什麼人嗎? [麥]第>>好。不。夏洛特?亞歷山大?還有其他人嗎?好吧。 因此,其他的是fgetc,是我們將使用一個不少。 還有fscanf,你們看到這裡的模式嗎? 他們都以f。任何一個文件。 有FREAD,fgetc函數,fscanf。這些所有的讀出功能。 為寫我們FWRITE,我們的fputc,而不是fgetc函數。 我們也fprintf,像我們看到的測驗。 由於這是一個問題,涉及從文件中讀取, 我們將使用這三個功能中的一個。 我們不打算使用這些功能。 這些功能都在標準I / O庫。 所以,如果你看一下這個程序的頂部, 你可以看到,我們已經包含標準I / O庫的頭文件。 如果我們想弄清楚,我們要使用哪一個, 我們可以隨時打開的手冊頁。 因此,我們可以輸入man的stdio 並閱讀所有的的stdio的輸入和輸出功能C. 我們已經可以看到啊,看看。提fgetc,它的一提的fputc。 所以,你可以向下一點點,看,說,fgetc函數 看它的man page。 你可以看到,它伴隨著一大堆的其他功能: fgetc函數,FGETS,getc函數,getchar函數,獲取,ungetc的函數,其輸入的字符和字符串。 因此,這是我們如何從標準輸入文件讀取的字符和字符串, 這本質上是從用戶。 這是如何做到這一點,在實際C. 因此,這是不使用的GetString和getchar功能 我們使用的從CS50庫。 我們要做的這個問題的幾種方法 所以,你可以看到兩種不同的方法做這件事。 fread函數,丹尼爾提到的和fgetc函數都不錯的辦法做到這一點。 我認為fgetc函數是一個容易一些,因為它只有你可以看到, 一個參數,FILE *,我們試圖讀取的字符, 它的返回值是一個int。 這是一個有點混亂,對嗎? 因為我們得到一個字符,那麼為什麼沒有這個返回一個字符? 為什麼這可能不會返回一個字符,你們有什麼想法? [的大小姐答案,不知所云] >>呀。所以大小姐是完全正確的。 如果它是ASCII,那麼這個整數被映射到一個實際的字符。 可能是一個ASCII字符,這是正確的。 這到底發生了什麼。 我們使用的是一個int,只是因為它有更多的比特。 這是更大的不是一個字符的字符只有8位,32位機,1個字節。 和一個int的所有4個字節的空間價值。 事實證明,fgetc函數的工作方式, 如果我們向下滾動本手冊頁一點點,在我們的簡介 向下滾動的方式。原來,他們用這種特殊的值EOF。 這是一個特殊的常量,fgetc函數的返回值 只要你打的文件,或者如果你得到一個錯誤。 事實證明,做這些比較EOF正確的, 你想有額外的大量的信息,你必須在一個int 而不是使用一個char變量。 即使fgetc函數有效地從一個文件中的字符, 你要記住,它返回的東西是你的類型為int。 這就是說,它很容易使用。 它給我們一個字符,所以我們要做的是保持要求的文件, “給我的下一個字符的下一個字符給我,給我的下一個字符。” 直到我們得到的文件結束。 這會拉一個字符的時間從我們的文件, 然後我們可以做任何我們喜歡它。 我們可以將其存儲,我們可以將其添加為一個字符串,我們可以把它打印出來。 任何。 放大退了出去,並要回到我們的cat.c的程序, 如果我們要使用fgetc函數, 我們會如何對待這下一行代碼? 我們將使用 - 弗里德會做一些事情略有不同。 而這個時候,我們只是要使用fgetc函數得到一​​個字符的時間。 為了處理一個完整的文件,我們可能會做什麼? 在一個文件中有多少個字符? 有很多。所以,你可能希望得到一個 然後得到另一個獲得,並得到另一個。 你認為我們可能要在這裡使用的是什麼樣的算法? 什麼類型的 - ? [亞歷山大]一個for循環? “沒錯。 某些類型的循環。 一個for循環實際上是很大的,在這種情況下。 像你說的,這聽起來像你想有一個循環,在整個文件, 獲得一個字符的時間。 任何建議,可能看起來像什麼? [亞歷山大,不知所云] 好了,只是英語告訴我你想要做的是什麼? [亞歷山大,不知所云] 因此,在這種情況下,這聽起來像我們只是試圖在整個文件中環。 [亞歷山大]所以,我為int的大小嗎? >>的大小 - ? 我猜大小的文件,對不對?的大小 - 我們就會把它寫這樣的。 尺寸文件的時間,我+ +。 因此,原來的方式,你使用fgetc,這是新的, 是,有沒有簡單的方法得到的文件大小 這個“大小”式的結構,你已經看到過。 當我們在使用,fgetc功能,我們要介紹的某種 新的,時髦的語法for循環,而不是只使用基本的計數器 去逐個字符,我們要拉一個字符的時候, 一次一個字符的方式,以及我們所知道的,我們是在結束 是不是當我們已經數了一定數目的字符, 但是,當我們拉出來的字符是特殊的文件結束符。 因此,我們可以做到這一點 - 我把這種路,我們要初始化 我們的第一次調用出來的文件的第一個字符。 所以這部分就在這裡,這是怎麼回事字符的文件 並把它存儲到變量ch。 我們將繼續這樣做,直到我們得到的文件, 我們做測試不等於EOF字符,特殊字符。 然後,而不是做CH + +,這將只是增加價值, 因此,如果我們看到一個出來的文件,一個大寫的A,說, CH + +會給我們B,然後我們會得到C,然後按下d。 這顯然不是我們想要的。我們想在這裡 在這最後一點是,我們​​希望得到的下一個字符的文件。 所以,我們怎麼可能得到的下一個字符的文件呢? 我們怎樣才能從文件的第一個字符? [學生]:fgetfile? >> fgetc函數,對不起,你是完全正確的。 我拼錯了就在那裡。所以,是的。 在這裡,而不是做CH + +, 我們只是調用fgetc函數(文件) 和存儲相同ch變量的結果在我們的。 [學生提問,不知所云] >>這是這些FILE *人是特殊的。 他們的工作方式是 - 當你第一次打開 - 當你第一次作出這樣的fopen的調用, 有效地作為開頭的文件指針FILE *。 然後每次調用fgetc,移動一個字符通過該文件。 所以每當你打電話,你的文件增加一個字符指針。 ,並時,fgetc再次,​​你要搬到另一個字符 另一個字符和其他字符和其他字符。 [學生提問,難以理解] >> that's的 - 是啊。 這是一種這種神奇的引擎蓋下。 你不斷遞增通過。 在這一點上,你能夠真正與字符。 因此,我們怎麼可能打印出來的畫面,現在呢? 我們可以使用相同的printf的事情,我們之前使用。 我們一直在使用一學期。 我們可以調用printf, 我們可以通過在性格就是這樣。 做到這一點的另一種方式是使用printf不必做此格式字符串,而不是 我們也可以使用其他的功能之一。 我們可以使用的fputc,可打印字符在屏幕上, 除非我們看的fputc - 讓我放大了一點點。 我們看到了什麼是好的,是需要我們讀出的字符使用fgetc函數, 但後​​來我們給它一個流打印到。 我們也可以用putchar函數,就可以直接輸出到標準輸出。 因此,有一大堆不同的選擇,我們可以使用的打印。 他們都在標準I / O庫。 每當你想打印 - 輸出,默認情況下,將打印的特殊標準輸出流, 這是標準輸出。 因此,我們就可以把它作為一種本魔法值,在這裡的stdout。 哎呀。將分號外部。 這是很多新的,時髦的信息在這裡。 很多,這是很地道的,在這個意義上,這是代碼 是這樣寫的,只是因為它是乾淨的,易於閱讀。 有很多不同的方式來做到這一點,您可以使用許多不同的功能, 但我們往往只是遵循相同的模式,一遍又一遍。 所以,不要驚訝,如果你看到這樣的代碼來了一次又一次。 好的。在這一點上,我們需要打破的一天。 感謝您的到來。感謝收看如果你在線。我們會看到你下週。 [CS50.TV]