[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]