三個一工程比較有意思的題彙總

三個一工程比較有意思的題彙總

Near far 近遠指針。*(char far *)

*(int  *)0x2000=0xf; 
*(char *)0x2000=’a’; 

((char *)*(int far *)0x02000000)
malloc20)是開闢20個字節的內存空間。

以下地址,本意是指內存地址。不然實在要說的話,那什麼東西肯定都有地址嘍,只要它依賴了硬件,我們看得見摸得着檢測得到,沒有地址纔有鬼嘍,現在我是隻比較一下最常用法的不同。
1>.變量有名字嗎?變量有地址嗎?
變量有變量名;變量有地址。
2>.寄存器有名字嗎?寄存器有地址嗎?
寄存器有寄存器名;寄存器沒有地址(此處切記與arm單片機等區分開,現在談論一下單純的C語言)。
3>.內存有名字嗎?內存有地址嗎?
內存沒有名字;內存有內存地址。
4>.端口有名字嗎?端口有地址嗎?
就目前看到的,好像常用的就是地址;名字?有嗎?存疑。

1>.用C語言編程可以不用變量嗎?
可以,綜合研究二得出其中一種額外選擇:可以使用寄存器替換變量的使用。
2>.函數名、變量,這些區別於彙編的新生名詞,在C語言反彙編爲彙編語言的時候又是具體以怎樣的熟悉的形式展開的呢?
相對於函數,很容易就想到了子程序,包括子程序的標號、調用、返回等都與函數感覺有某種近親關係一樣。變量的話,存在的形式,還不確定就是唯一的形式。
3>. 用debug怎麼查找函數的偏移地址?
參考語句“printf(“%x\n”,main);”,最好能舉一反三。

1>.c0s.obj文件的作用
c0s.obj文件的作用:在程序開始運行,進行相關初始化,再調用main函數,返回後進行相關的資源釋放,環境恢復等工作,再將程序返回。
2>.可以不用main函數編程嗎?
可以。只不過需要修改相應的原來在程序開始運行時,負責進行相關初始化操作、調用main函數、返回後進行相關的資源釋放、環境恢復等工作的文件。在本次研究中用到的是c0s.obj文件,同樣的道理,以後的話要學會舉一反三。

1>.頭文件的作用和生成的四個階段?
1.預處理(include這個庫尋找定義頭文件的內容插入源程序中生成.i文件)
2.編譯(將.i文件翻譯成生成.s後綴的彙編語言程序)
3.彙編 (將.s文件翻譯成機器語言,生成可重定位的目標程序,打包生成.o文件)
4.連接(將各種代碼和數據整合成一個單一可執行文件)

3>.有關於程序的的編譯過程?

雖然最終都會編譯成機器碼,但存在有的程序不必編譯成彙編語言,c語言必須轉變成彙編語言,因爲C語言是彙編編譯。之後會講到“彙編器”相關知識。

4>.Main與Main()的區別?
在用printf()打印main時,打印出的是main()的偏移地址;而在打印main()時,得到的數是main()的返回值。但是這個返回值的選取又涉及到之前函數將返回值保存在哪裏的問題,由於原本編寫的main()返回值保存在ax寄存器中當我們再編寫一個函數,並且該函數並沒有在作用過程中修改ax寄存器的值。我們得到的該函數的返回值仍然是上一次保存在ax寄存器中的值。

5>.Sp、bp問題?
作用一:與SP聯合使用,SP不能輕易更改,所以賦值給BP,用BP來實現我們的要求;
作用二:棧幀,相當於一個鏈表,bp,sp代表一個棧幀的序列,爲了方便調用其他函數,返回之後恢復調用前狀態。

而在這裏,再回頭看一下在這一過程中指點迷津、至關重要的那條語句“printf(“%x\n”,main);”格式如此,是否可以理解爲這裏的函數名簡直是在當作變量用,但是區別呢?聯繫呢?函數名、變量,這些區別於彙編的新生名詞,在C語言反彙編爲彙編語言的時候又是具體以怎樣的熟悉的形式展開的呢?
相對於函數,很容易就想到了子程序,包括子程序的標號、調用、返回等都與函數感覺有某種近親關係一樣。

1>.既然大家都是變量都是用來存儲的,爲啥放的位置還不一樣呢?或者全局變量爲啥不用棧?局部變量爲啥不選數據段?

這個肯定的,首先全局變量,就是要讓它從定義起就可以在程序中任意位置使用,如果選擇棧了,棧用完就pop了,其他地方還咋用呢?這就是全局變量不選棧;
局部變量呢?如果所有的數據都直接放在數據段中,局部變量也變成了直到整個程序運行完畢纔會得到釋放的話,那麼如果我們定義的變量越來越多。。。。。。一方面增大了內存的開銷,另一方面,這個“局部變量”哪裏都可以修改,函數的獨立性不好保證。
2>.程序1中,全局變量n,是由“unsigned int n”這條語句定義,還是由main函數中的“n=0”這條語句定義?
應該是由前者定義的,函數外定義的變量,不管有沒有加static,沒有初始化的話,系統默認初始化爲0。如果在n=0語句之前打印n,是能夠打印出它的值的。

搜到的網上資料:
當啓動連接程序時,它會尋找“未定義的外部函數”,也就是說,它將在每一個庫文件中查找源代碼文件中未定義的函數。當它找到一個未定義的外部函數後,它會引入包含該函數定義的目標代碼(obj)。不幸的是,如果這個函數是在一個包含其它函數定義的源文件中被編譯的話,那麼這些函數也會被包含進來,你的可執行代碼中將包含一些不需要的代碼。因此,將庫函數放到各自的源文件中是很重要的——否則會浪費寶貴的程序空間。有些編譯程序包含特殊的“精明的”連接程序,這些連接程序能查出不需要的函數並去掉它們,從而使這些函數不再進入你的程序。

一、內存對齊:節省時間,以空間換時間。看資料自己推演。vc下可以寫代碼規定內存對齊。(希望大家的研究要有深度)

同樣的觀察彙編代碼可以發現(a+n)等價於(&a[0]+n),即a與&a[0]是等價的。這是因爲a[0]是數組的第一項,所以它的地址就是數組a的地址。
所以數組名就是一個指針,[]運算是在指針的值上加上一個偏移量,使其指向下一個存儲單元的數據。

結合之前學習的彙編,計算機中最多的就是數字了。這些數字,在c語言中從不同的角度看,具有不同的使命。數據、指令、地址。但是三者之間又是相互聯繫的。啊呀,一拍腦門,這不又與計算機的組成中三大總線數據總線、控制總線、地址總線一一對應起來了嗎?可以,這很指針。

是否可以自己編寫一個功能與sizeof關鍵字相似的函數呢?如果打算編寫,自己會從什麼角度出發?
Sizeof是求字節數,字節也就是內存中一種絕對的單元長度,如果打個比方,我覺得他就像尺子中的釐米單位,當然比他更小的還有毫米,對應的,位可以填充。同樣sizeof好像就是一把丈量的尺子一樣,他是一把短小精悍的工具,釐米——字節、毫米——位等都存在於內存中,但並不意味着內存更貼近尺子,當然是與sizeof在此處相較而言。內存是個容器,是字節們、位們的容器,是他們的世界,而我們的世界,又是我們的容器。我如果想測量一段字節多長,我會怎麼測呢?我想了想我在現實生活中的使用尺子的時候一般是這樣做的:
第一種:我會將尺子的0刻度對齊欲測量字節段的首或尾端,然後查看對應的尾或首端讀出的“尺子刻度”是多少釐米,也就是多少字節,是多少毫米,也就是多少位。。。。。。
第二種:有時候尺子的0刻度可能斷掉了,被墨跡玷污了。。。。。。這時候我會隨便找個刻度,當然現實生活中一般選取整釐米處,內存選取整字節處。然後記下這個刻度值,將尺子的這個刻度對齊欲測量字節段的首或尾端,然後查看對應的尾或首端讀出的“尺子刻度”,然後兩值相減取絕對值。也就是多少釐米,也就是多少字節,是多少毫米,也就是多少位。。。。。。
編寫程序的話,會選上面兩種。但是還有兩個問題。
1>.sizeof是關鍵字,不是函數;
2>.sizeof能實現對結構體中出現內存對齊情況的正確計算。

一般的大型工程都會在生成可執行文件的同時讓鏈接器生成一個map文件,從而大致查看一下可執行文件中符號的內存佈局以及從哪裏引入可執行文件。這個通常對於小型工程是作用不大,因爲代碼就那麼多,隨便grep一下就知道符號定義位置了。但是對於一些大型工程或者涉及了比較多的第三方庫、或者涉及了比較多的功能模塊的時候,就需要知道這些符號是在哪裏定義,或者說如果一個符號引用了但是沒有知道函數定義,此時也需要找到這個符號是哪個模塊引入的,爲什麼需要,所以需要一些通用的(形式化)的方法來搜索這些符號,而map文件就是一個比較好的切入點。

1.針對靜態庫、動態庫佔用內存資源問題。對比兩者的實現。
靜態動態的關鍵區別(學長的):加載的時間不同。
還有什麼其他異同:針對靜態庫和動態庫中存放的文件。靜態庫是多個*.obj文件的集合,有的同學查出資料說動態庫是可執行程序文件的集合。
動態庫、靜態庫都是模塊。

動態庫是調用的時候加載,因爲如果一運行就加載,那動態庫和靜態庫還有什麼分別呢?
庫: 指由標準常用函數編譯而成的文件,旨在提高常用函數的可重用性,減輕開發人員負擔。常用的sdtio.h,math.h等。庫便是C函數庫的冰山一角。
1.靜態庫:指編譯鏈接階段將整個庫複製到可執行文件
1.1優點:靜態鏈接的程序不依賴外界庫支持,具有良好的可移植性。
1.2缺點: 每次庫更新都需要重新編譯程序,即使更新很小或只是局部。
1.3缺點:每個靜態鏈接的程序都有一份庫文件,存儲時增加了硬盤空間消耗,運行時則增加了內存消耗。
2.動態庫:指直到運行時纔將庫鏈接到可執行程序
1.1優點: 動態鏈接方式的程序不需要包含庫(編輯鏈接時節省時間),佔用的空間小很多。
1.2優點: 運行時系統內存只需提供一個共享庫給所有程序動態鏈接,內存消耗減少。
1.3缺點: 需要系統中動態庫支持纔可運行,可能有動態庫不兼容問題

1.(學長的)指令“ c = *c + 1;”c爲int類型,這條指令讀寫了幾次內存?
1>.讀c的內容;
2>.讀*c的內容;
*3>.讀c的內容;
4>.寫*c。
這裏注意一下:*c在算過一次地址後要再算一遍。

1.(學長的)指令“c = &ch;”c爲int*類型,ch爲int類型,這條指令讀寫了幾次內存?
ch的地址並沒有存放在內存中,所以訪問&ch並不需要訪問內存)寫一次。
相關彙編代碼:
Mov ax,offset ch
Mov [c],ax

3.(學長的)Push [bx]
運行這一句,訪問了讀指令、算地址、取一次數據、入棧4次指令。

對於強類型語言c語言中,當然不止是c語言,這些語言中都有很多的描述性語言,但是有時候當一件擁有大量陌生名詞的學習事物充斥在初學者身邊時,很多時候,其實是排斥的。而且有很多概念如果拿以前已經學習吸收的知識組裝成新知識的理解框架之後,相比於直接拋出這個陌生的概念,學習效果可能也會很不錯。“=”右邊的表達式中,放在變量、數字之前的,目前來看大部分看作“強制類型轉換”來理解,特例如果有可以舉出來,這樣對於大家理解指針函數、函數指針、指向指針的指針等較複雜概念與一般化的(int)、(char)等大家熟悉的強制類型轉化性質進行統一和類比,更容易消化吸收。“b =(int) a”、“ b =(int ) a”、“ b =(int *) a”等等,甚至將“b =& a”,也就是“b =(&)a”,也歸入“強制轉換類型”,好像也說的通。還有“b =* a”,也就是“b =()a”,從這個角度,我自己覺得也是。然後如果p是一個指向字符的指針,“b = p[3]”,這個呢?好像有點不像了,可是之前說的等價式:“b = (p + 3)”,也就是“b = ()(p + 3)”,這樣好像和前面我認爲的同樣屬於強制類型轉換應用的“b =()a”有點像呀,只不過這裏(p+3),抽象化,兩者都是指針,屬性不變呀,所以,這種也算嗎?嗯,有點不確定。學長,您說呢?還有其他的什麼情況可以歸到這裏呢?還需要再想想,從長計議,好好總結。

1.打印char類型,要以無符號形式打印16進制。因爲char類型是1個字節。打印%x是壓入2個字節,這時要進行擴展,如果是以有符號形式打印的話,則擴展後爲ff80,(假設打印80h),這樣就不合適,因爲前面的bit位都變成了1,而用無符號的話,擴展出來就成了0x0080,這樣就很合適。

這樣,也就涉及到快速數數的方面。我們對計算機存取數值的靈敏感知,其實沒有必要非得看什麼原碼、反碼、補碼。想出這些只顧着理論性強的繁瑣詞彙,並想要學生們非得記住的,一定一定不是一個好主意,而很多學校老師們就很是這樣,不僅喜歡教,還喜歡這樣考,真的受不了。其實數一直是連續的,至少,是線性排列的。就好像最經典的一維座標軸:


——————————————————————————————————>


對於無符號數,比如無符號整型,數值範圍是(0~255)同樣模仿一維座標軸的形式列出來:


————————————————————————————————–> 增大
0 1 2 …… 127 128 129 …… 253 254 255
00h 01h 02h …… 7fh 80h 81h …… Fdh feh ffh 補碼


其中從始至終,箭頭方向,就是數值單調遞增的方向。

對於有符號數,比如有符號整型,數值範圍是(-128~127)同樣模仿一維座標軸的形式列出來:


——————————————>|———————————————————>增大
0 1 2 …… 127 | -128 -127 …… -3 -2 -1
00h 01h 02h …… 7fh | 80h 81h …… Fdh feh ffh 補碼


其中從始至終,箭頭方向,就是數值遞增的方向,但是並不是單調遞增,分爲了兩大部分:“0~127”和“-128~-1”。接着中間這個劃分兩部分,也就是劃分“正負”的界限,就更熟悉了,此處的“127”和“-128”之間,也就是正好二分處。

接着考慮進來之前所說的數是線性排列的概念,思考學長提出的擴展成有符號四字節之後,“-128”又是怎樣表示的,數值範圍是(-32768~32767)同樣模仿一維座標軸:


——————————————>|——————————————————>增大
0 1 …… 32767 | -32768 -32767 …… -2 -1
0000h 0001h …… 7fffh | 8000h 80011h …… fffeh ffffh 補碼


其中,同樣-32768 到 -1也是線性增大的,而在之前的有符號整型,-128距離 -1是80h 到ffh 的距離,這樣推算下來,-128擴展之後的表示,就是ff80h。

而在這個過程中,補碼是一直線性連續的,這樣就比較直觀,好記憶理解了。

在不同的系統中,
NULL並非總是和0等同,NULL僅僅代表空值,也就是指向一個不被使用的地址,在大多數系統中,都將0作爲不被使用的地址,所以就有了類似這樣的定義

#define NULL 0

但並非總是如此,也有些系統不將0地址作爲NULL,而是用其他的地址,所以說,千萬別將NULL和0等價起來,特別是在一些跨平臺的代碼中,這更是將給你帶來災難。

結果表明用函數指針p和用變量b強轉,都實現了調用函數的效果。

這裏最令我感興趣的就是“a = ( (int ()(char,char) )b )(3,4);”這一句,因爲他又讓我想起了自己之前那段邏輯不清的表述,這裏也是可以歸類於其中的另一種“強制類型轉換”,儘管書上沒有教過我們這個道理,但它的確是存在的,這個程序恰恰驗證了。“(int ()(char,char) )b”這裏就是將整型變量b強制類型轉換爲“返回值爲int,參數爲char、char的函數的函數指針變量類型”。而之所以可以實現f()函數的功能,是因爲在這之前b的值已經通過“b =(int)f”賦值爲f()函數的首地址了。

看到這裏,感覺之前自己認爲的“(&)a”,這裏a是一個普通變量,假設是整型吧,可以有另一種自己覺得更好的解釋,本來a的地址與變量a以及變量a的值就是一體的,但是我們平時默認的就是正面的角色-》a的值,而(&)a自然就是反面角色了。但是正反從來都是一體的。看過不少武俠書籍之中,總會提到一種常見的機關門,按下機關,門就會翻轉,主人公就能抵達暗室了。在這裏,a的值,以及(&)a的值,就像是這扇會翻轉的門的正反兩面,而他們從來都不是分割的,因爲他們都在a這扇門上。嘿嘿,感覺好像是在“翻牌子”;那麼“*p”呢?其實我更喜歡“->”的標記來描述。這裏p是一個普通的指針變量,假設是整型指針吧,它又有什麼自己的解釋呢?它更像個穿越門,穿越空間,不穿越時間。到了鏈表中就更好說了,通過p可以穿越到哪裏,制定目的地的主人,就是我們了。多個穿越門可以到達同一個地方,一個穿越門可以修改到達的目的地,穿越了之後可以再次穿越。。。。。。我去 ,怎麼有點哈利波特的感覺了?哈哈。希望以後現實中也可以有這種超級黑科技。應該,可以實現吧?

1.sizeof和strlen的區別

Sizeof是預處理時候得出的,strlen是庫函數,運行的時候根據“\0”進行計算

粗淺的理解:
Continue

#include <stdio.h>

void printWelcome(int len)
{
    printf("welcome -- %d\n", len);
}


void printGoodbye(int len)
{
    printf("goodbye -- %d\n", len);
}

void callback(int times, void (* print)(int))
{
    int i;
    for (i = 0; i < times; ++i)
    {
        print(i);
    }
    printf("\nwhat am i doing!\n\n");
}
void main(void)
{
    callback(3, printWelcome);
    callback(3, printGoodbye);
    printWelcome(3);
    printGoodbye(3);
}

這裏,callback()是可以調用兩個回調函數( printWelcome()、printGoodbye() )的函數,這兩個回調函數“類型”是一樣的。Callback()這裏就像普通的整型變量一樣用“這兩個回調函數的類型”定義了一個形參變量給自己,另外還有一個變量,是在main()函數中可以提供的。因爲打印時候的i本質上是受傳入的參數times影響和控制的。
所以這個有什麼用呢?網上對這個講的真的是五花八門,看着都能睡着了。我就覺得他讓我最感興趣的就倆點兒:
1.把函數像變量來使用,通過使用函數的地址、函數指針等;
2.在最外面的函數,這裏就是main(),通過在main()函數裏面進行操作,也能很輕鬆按照我們想要的方式控制和影響最裏面的函數的變化,從而實現最終結果變量化的功能。

表面上看,中間函數callback()可以通過函數指針形參來選擇回調函數,最外層函數又可以通過times傳參影響callback函數,但是這個times參數還會對回調函數產生影響,而且其實最終目的就是爲了影響最內層的回調函數。
我該不會越講越亂了吧。

變量:

變量是一種存放數據方式,與常量不同,它的內容是可以改變的。它可以分爲全局變量和局部變量,它們的本質區別是存儲的位置不同,全局變量是在內存中存儲的,而局部變量是在棧段中存儲的,這個差別導致了它們的一系列區別:全局變量存儲的內存空間是沒有內存對齊的情況的,而局部變量有;全局變量作爲參數傳遞是直接用地址調用,而局部變量是入棧的方式;全局變量的生命週期是整個程序,而局部變量的生命週期是當前函數;全局變量的段地址在ds寄存器裏,局部變量的段地址在ss寄存器裏;全局變量定義是自動清零的,而局部變量定義時在棧中的空間還是原來的數據。比較特別的靜態局部變量的存儲位置和生命週期都和全局變量一樣,只是靜態局部變量只能在定義的函數中使用。

比較重要的變量類型有char、int、long、double和結構體,它們分別佔的大小爲1字節、2字節、4字節、8字節,結構體的大小是結構體中數據項之和。結構體也存在內存對齊的情況,結構體中各數據項存儲位置是相鄰的。結構體作爲參數傳遞和返回比一般變量要複雜,一般變量都是直接入棧,而結構體必須創建一個臨時變量,用塊搬移函數將結構體的各數據項複製到臨時變量裏,在子函數裏再將臨時變量的值搬移到棧段裏面,返回的原理也是相同的。

數組:

數組是利用一段連續的內存空間存放一系列相同類型的數據。一維數組是存儲的數據按照線性的順序來排列,二維數組是存儲的數據按照類似圍棋棋盤的順序來排列,多維數組是存儲的數據以多維的形式存儲,我們可以通過當前一組不斷向下查找到某一個元素。數組也可以根據存放的元素類型不同來分類:整型數組的元素是int型數據、指針型數組的元素是指針型數據、結構體數組的元素是結構體數據。數組中的元素是連續存放的。數組名相當於數組的首地址,也是數組第一個元素的地址,它的使用和指針有相似之處,如果p是一個指針,那麼p[n]等同於*(p+n),即跳到下一個元素就相當於在當前地址上加上數組元素的類型大小。數組還有函數指針數組,存放的元素是函數指針,指向函數的指針,函數指針數組可以將要運行的程序以數據的形式寫入並對函數進行調用。

函數:

函數是一段指令的集合。函數名相當於一個函數指針,存儲函數的入口地址,程序由這個入口地址跳轉到當前函數。函數的參數是局部變量,在調用該函數的函數中將參數壓入棧中,在子函數裏用bp寄存器找到參數的地址進行調用。函數可以有返回值,void函數沒有返回值,函數的返回值一般是存儲在寄存器中,如果返回值爲結構體,則將結構體的內容傳遞到一個臨時變量裏。函數是一段數據,它同樣存儲在內存空間裏,這樣我們可以以數據的形式將一個函數寫到內存中執行。

指針:

指針存儲的數據是一個地址,我們可以通過“*”來取得指針存儲的這個地址處的內容,通過“&”來取得一個內存空間的地址賦給指針。指針加減一個數並不是以它的值加減一個數字,而是加減它所指向的存儲空間的數據類型的大小,即如果它指向的是int型數據,那麼加1就是在當前地址上加上2個字節。我們可以將一個地址賦給一個整形變量,但我們不能對一個整形變量使用“*”取得它所存儲的地址處的值,因爲它不是一個指針,同時如果一個指針是一級指針,即定義成*p,那麼只能用“*”對它取一次值,如果一個指針是二級指針,可以用“*”對它取兩次值,總之,一個指針是幾級指針,就可以對它取幾次值。對於指針的使用我們一定要注意它和其他的變量的類型匹配問題,近指針佔2個字節,存儲偏移地址,遠指針佔4個字節,存儲段地址加偏移地址。雖然指針的值的大小是固定的,但是指針指向的值的大小和指針的定義有關。指針在地址和內容之間建立了一條聯繫,這種聯繫是c語言最重要的基礎,我們可以用它來實現多種數據結構。指針可以指向任意的數據類型,結構體指針指向的是一個結構體,它可以以->符號調用結構體的數據項。函數指針是指向一個函數入口的指針,當定義一個函數指針時要指明函數的類型和參數類型和個數,通過函數指針可以調用指定位置的函數。

1、重新總結針對12的關於共性和個性的問題

研究12中b.c程序的共性和個性的思考可以分成幾個部分。
第一部分:通過函數指針數組func[n]中選擇n的值來實現調用不同的函數,其中func[]是共性,通過變量n來調用不同的add(),sub()等函數,參數不同,這是個性;
第二部分:幾個個性函數的接收參數都是兩個,這是共性,但是對於兩個參數的操作是不同的,這是個性;
第三部分:字符串code中的“+-*/”等都是可以增減變化的,而且各自的意義不同,這是個性,但是函數中對於code[n]數組元素的遍歷及判斷選擇操作並不會因爲code中元素內容的改變而更改,這是共性。

1.對數組和指針一定要有關鍵的區分點。

計算機爲數組分配存儲空間,但沒有爲數組變量分配空間;
計算機爲指針是分配了空間的。

由於數組變量沒有分配空間,而且它其實在編譯的時候就被替換爲具體的地址,所以它也無法像一個正經的指針那樣指向任何地方。

但是應用方面我覺得最經典的就是,對於字符串常量了:
如果“char* f = ”hello”;”之後像“f[1] = f[0];”這樣想要改變值的情況時,就會出現錯誤;
如果 “char f1[] = ”hello”;”,之後再改變,就安然無恙。
因爲這裏位於棧區的f想要改變位於常量區字符串常量,而位於棧區f1想要改變的是位於棧區的字符串常量的副本。

map文件的作用是,我們可以根據程序產生錯誤地址在map文件中找到相應的函數、變量地址。

比如程序有“除0錯誤”,在 Debug 方式下編譯的話,運行時肯定會產生“非法操作”。好,讓我們運行它,果然,“非法操作”對話框出現了,這時我們點擊“詳細信息”按鈕,記錄下產生崩潰的地址。比如:0x0040104a

再看看它的 MAP 文件:

仔細瀏覽 Rva+Base 這列,會發現第一個比崩潰地址 0x0040104a 大的函數地址是 0x00401070 ,所以說在 0x00401070 這個地址之前的那個入口就是產生崩潰的函數,也就是這行:

0001:00000020 ?Crash@@YAXXZ 00401020 f CrashDemo.obj

發生崩潰的函數就是 ?Crash@@YAXXZ ,也就是 Crash() 這個子函數。

不僅如此,根據*.map文件還可以找到具體的行號。不過得先生成代碼行信息,

2.爲什麼要使用一個頭結點?

這裏我覺得就是一個統一的問題,比較一下按位計數法,最常見的10進制計數法,0的作用是什麼呢?舉個數字“2503”,0在這裏的作用是什麼?對的,脫口而出,就是“沒有”,0在這裏就是表示沒有,但是,可不可以沒有“沒有”呢?那不就是“253”了嗎?明顯不對了。所以這裏的0表示“沒有”但是不可或缺,也就是起了“佔位”的作用。同樣的,樂譜中的休止符也是可以由此聯想的。頭結點就像0一樣,它有具體的空間,它佔了一個位。而在數學中10的冪次從0…5…100…依次增長,這裏爲什麼 有0呢?因爲還有…-100…-5…0
的次冪,如果我們把10的0次只作爲1來特殊化處理,就好像人爲地建立了一個隔斷,所以0還有統一標準的作用。頭結點同樣如此,統一標準,就像是程序員的一枚小細作,程序員控制頭結點,頭結點像領頭羊一樣帶領着屁股後面的一串結點。

3.void *memcpy( void *dest, const void *src, size_t count);

Memcpy函數的原型如上,即從指針src指向的空間拷貝count個字節到指針dest指向的空間裏。

4.對void* 指針強制類型轉換時的體會

就像是用基本類型(系統地默認模板)或自定義數據類型(自定義模板)對一塊已經密密麻麻擺滿的數據的文本進行了相應的數據類型的格式化排版,這也就解決了相應的打印輸出問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章