為什么需要這木書
市面上已經有了許多優秀的講述C語言的書籍,為什么我們還需要這一本呢?我在大學里教授C語言編程已有1個年頭,但至今尚未發現 本書是按照我所喜歡的方式來講述指標的,許弟節籍用一章的篇幅專門講述指標,而且往往出現在全書的后半部分,但是, 僅僅描述指標的語法、并用一些簡單的例子展示其用法是遠遠不夠的,我在授課時,很早 便開始講授指標,而且在以后的授課程序中也經常討論指標,我描述它們在各種不同的上 卜’文環境中的有效用法,展示使用指標的編程慣用法(programming idiom)=,我還討論了 ,些 相關的課題如編程效率和程式可維護性之間的權衡,指標是本書的線索所在,融會貫通于 全持之中,
指標為什么如此重要?我的信念是:正是指標使C威力無窮.有嗖任務用其他語言也可 以實作,但C能夠更有效地實作;有些任務無法用其他語言實作,如直接訪問硬體,但C卻 可以,要想成為一名優秀的C程式員,對指標有一個深入而完整的理解是先決條件,
然面,指標雖然很強大,與之相伴的風險卻也不小,跟指甲輪相比,鏈鋸可以更快地切 割木材,但鏈鋸更容易使你受傷,而且傷害常常來得極快,后果也非常嚴重.指標就像鏈鋸 一樣,如果使用得當,它們可以簡化演算法的實作,并使其更富效率;如果使用不當,它們就 會引起錯誤,導致細微而令人困惑的癥狀,并且極難發現原因,對指標只是略知一二使放手 使用是件非常危險的事,如果那樣的話,它紿你帶來的總是痛苦而不是歡樂,本書提供了你 所需要的深入而完整的關于指標的知識,足以使你避開指標可能帶來的痛苦,
有時,在Web上,我會進行無數次有關C或C ++等語言的指標的討論,關于使用指標是否值得甚至是明智的爭論不斷, 這個問題似乎一直持續到今天, 因此,希望澄清一些事情,我決定創建這篇文章, 我不會對這個問題過于熟練,也不會虔誠地要求每個人 new必須具有相應的 delete或每個人 malloc必須具有相應的 free,
本文適用于C和C ++,您除了處理原始指標外別無選擇, 其他語言(例如Java,C#等)將所有的伏都教魔術隱藏在幕后, 它也適合那些不使用ASM,C或C ++撰寫代碼但被主題迷惑的初學者,
我將嘗試涵蓋所有情況,而不會太深入技術細節, 如果您希望對指標有非常深入的技術知識,那么從Wikipedia到眾多博客都可以找到很多資訊, cplusplus dot com網站上有一篇很棒的關于指標的文章,因此在這里我不再重復, 鑒于您正在使用C或C ++進行開發,因此我將在以下場景中進行介紹:
- 什么時候不使用指標就不可能實作某些東西
- 使用指標的優點
- 通過值,參考或指標傳遞變數
- 懸空指標
什么是指標?
指標是一個整數變數保持在所述特定的寬度(一個地址 float, double, int, struct, class,等等)值被存盤在計算機存盤器中, 因此,指標是計算機在金屬層上本機可以理解的“事實”物件, 指標始終是寬度為8、16、32、64、128等位的無符號整數, 這在很大程度上取決于CPU的主暫存器寬度, 它也由作業系統運行時位對齊指示, 完全有可能在64位CPU上運行16位OS,但反之亦然, 但是,在16位OS中,即使CPU為64位寬,您也將被限制在16位地址空間中,
旁記:由于64位暫存器的寬度足以滿足上述16位OS的需要,因此可以撰寫一個特殊的分段記憶體管理器,該管理器可以窺視超過64Kb的限制,但前提是物理暫存器必須足夠寬(哦,舊的16位Windows) ), 16位記憶體段和偏移量的主題在今天已經過時了, 在當前的討論中,我僅針對平面記憶體,
指標的寬度與該指標可以容納的可尋址記憶體直接相關,并且其寬度的乘冪為2減1,
通過查看此表,您可以看到過去和將來,
如果您要在sifi小說領域中醒來,首先要檢查的是它們使用的計算機上的可尋址指標存盤器,以了解您要處理的內容 ,

當今的64位處理器中確實存在128位暫存器,通常用于SIMD(單指令多資料)操作碼, 長話短說–它們允許并排加載四個32位整數到同一個暫存器中,并在一個CPU周期(赫茲)中執行多個指令,例如MulDiv等, 值得一看:1 EB等于一百萬TB,
好, 那么什么是指向指標的指標?
指向指標的指標是一個整數變數,它保存指標的地址, 它通常用作函式的回傳值, 它也用于可能出現懸掛指標的危險的地方,
// Calling a function that returns a pointer
//
void* ptr = nullptr;
// declared as void SomeFuncReturnsPtr(void** p) { *p = value; }
SomeFuncReturnsPtr(&ptr);
// ptr is no longer null
ptr->DoStuff();
懸空指標
作為直接指標(又稱為指標副本)傳遞給不同函式的指標面臨著以下危險:如果該函式洗掉了該指標,則該指標在另一個函式中的另一個副本不會無效, 它仍然保留已擦除記憶體的地址,因此成為懸空指標,因為它的狀態似乎是有效的(不是 null), 此類指標結果的任何用法都是未定義的運行時行為, 在非托管環境中,這是一個真正的危險,因為它在運行時很難找到,因此最好在編碼階段需要面對,
以下代碼防御性地處理了指標懸空的可能性:
// Disastrous main
void main()
{
A* ptr = new A;
NukeA(ptr);
// ptr now is a dangling pointer
assert(ptr == nullptr); // Kaboom. Still points to wiped memory
delete ptr; // Kaboom
}
// Safe main
void main()
{
A* ptr = new A;
NukeSafelyA (&ptr);
// ptr now is null
assert(ptr == nullptr);
}
void NukeA(A* p)
{
p->DoThis();
p->DoThat();
delete p; // Kaboom! A dangling pointer is born
p = nullptr;
}
void NukeSafelyA(A** p)
{
(*p)->DoThis();
(*p)->DoThat();
delete *p; // Nice
*p = nullptr;
}
實際上,如果您使用指標來動態分配記憶體,則永遠不要將指標傳遞給另一個函式,尤其是該函式可以或可能洗掉它時, 僅通過指標將其傳遞給指標,然后 了該指標并 中將 則您的指標將變為 null如果實際上洗掉 在另一個函式 其無效, 這是一個非常簡單的示例,但確實演示了何時可以發生,
也許干巴巴的文字看起來有寫枯燥,如果單看文字不是很容易消化的話,可以進群【1106675687】來跟大家一起交流學習,群里也有許多視頻資料和技術大牛,配合文章一起理解應該會讓你有不錯的識訓,
推薦一個不錯的C/C++ 初學者課程地址,這個跟以往所見到的只會空談理論的有所不同,這個課程是從六個可以寫在簡歷上的企業級專案入手帶領大家學習c/c++,正在學習的朋友可以了解一下,
指標從哪里開始?
計算機金屬根本不在乎甚至不知道變數是什么或變數是什么, 它所了解的只是CPU暫存器和記憶體地址-指標, 話雖這么說,編譯器通過變數宣告創建了一種錯覺,使CPU可以將其與內部暫存器以及從中加載該值的記憶體地址相關聯,
區域變數和指標之間的差異
所有區域變數都被宣告并駐留在函式堆疊框架中, 指向動態分配記憶體的指標也位于堆疊幀上,但它指向程式的全域堆記憶體, 在不受管理的全域堆中,程式有責任對其進行管理, 因此,任何泄漏的記憶體最終都會導致資源匱乏,并使您的程式遲早崩潰,
那么什么時候沒有指標就不可能實作?
我可以列舉幾種這樣的情況,但是讓我在進一步嘗試之前先解決一下, 每個程式都有多個功能堆疊框架和一個全域堆, 全域堆基本上是OS管理的虛擬記憶體, 堆疊是LIFO(后進先出)受限大小的資料結構, 可以想象,到相鄰堆疊的任何溢位都將有效擦除已保存的資料并使程式崩潰,
但是,有那么多神奇的智能指標!
記住這一點, 沒有任何代碼看起來和行為像由不知道指標實際是什么或代表什么的人撰寫的智能指標所騎的代碼一樣糟糕, 在嘗試使用智能指標沉迷程式之前,請閱讀以下所有情況, 另外,回傳并重新閱讀“懸空指標”部分, 當通過資產而不是通過腦細胞處理原始指標到智能指標的連接/分離時,智能指標會創建此類底層而臭名昭著,
什么是呼叫堆疊和堆疊溢位
堆疊是LIFO資料結構, 實際上,編譯程式也稱為“堆疊計算機”, 每個行程和執行緒都有自己的堆疊, 每個堆疊都細分為呼叫堆疊, 呼叫堆疊的數量與程式中函式的數量完全匹配, 呼叫堆疊是堆疊中的較小塊, 呼叫堆疊大小有一個限制,通常為1Mb, 在UNIX系統上,它是一個環境變數(我相信), Visual C ++編譯器允許您使用 來更改呼叫堆疊的大小 /F標志 ,
堆疊溢位最好通過以下 偽代碼來表征 _chkstk()函式 :
;***
;_chkstk - check stack upon procedure entry
;
;Purpose:
; Provide stack checking on procedure entry. Method is to simply probe
; each page of memory required for the stack in descending order. This
; causes the necessary pages of memory to be allocated via the guard
; page scheme, if possible. In the event of failure, the OS raises the
; _XCPT_UNABLE_TO_GROW_STACK exception.
;
; NOTE: Currently, the (EAX < _PAGESIZE_) code path falls through
; to the "lastpage" label of the (EAX >= _PAGESIZE_) code path. This
; is small; a minor speed optimization would be to special case
; this up top. This would avoid the painful save/restore of
; ecx and would shorten the code path by 4-6 instructions.
;
;Entry:
; EAX = size of local frame
;
;Exit:
; ESP = new stackframe, if successful
;
;Uses:
; EAX
;
;Exceptions:
; _XCPT_GUARD_PAGE_VIOLATION - May be raised on a page probe. NEVER TRAP
; THIS!!!! It is used by the OS to grow the
; stack on demand.
; _XCPT_UNABLE_TO_GROW_STACK - The stack cannot be grown. More precisely,
; the attempt by the OS memory manager to
; allocate another guard page in response
; to a _XCPT_GUARD_PAGE_VIOLATION has
; failed.
;
;*******************************************************************************
由于堆疊大小有限,因此會監視溢位到相鄰堆疊上的內容,如果發生這種情況,則會引發例外, A _PAGESIZE_在32位作業系統上為4Kb,在64位作業系統上為8Kb,因此,如果任何變數大小大于頁面大小,則會對其進行檢查,但不一定會導致堆疊溢位,Alloca()函式在運行時從本地堆疊中拉出記憶體,
由于篇幅有限,需要的高清原資料的可以點擊這里,看到會通過哦


轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/255862.html
標籤:其他
上一篇:c語言整蠱朋友小程式
