)
目錄1. const修飾指針變量1. const修飾變量2. const修飾指針變量2. 野指針1. 野指針成因2. 如何規(guī)避野指針3. assert斷言4. 指針的使用和傳址調(diào)用1. strlen的模擬實現(xiàn)2. 傳值調(diào)用和傳址調(diào)用前言本文主要講解C語言中指針相關(guān)的進階知識包括const修飾指針變量、野指針的成因與規(guī)避、assert斷言的用法以及傳值調(diào)用與傳址調(diào)用的區(qū)別。通過本文的學習你將能更安全、規(guī)范地使用指針。1.const修飾指針變量1.const修飾變量首先我先去創(chuàng)建一個變量a并修改它最后輸出a現(xiàn)在我不想讓這個a被修改那么就要使用一種方式去限制它的修改這時候我學習到了const可以去發(fā)揮這種作用先介紹const的基本概念const表示常屬性的意思就是不能被改變的意思限制變量的修改知道它的作用之后我就要去使用它了改進代碼如下此時系統(tǒng)爆出了警告表示我已經(jīng)限制了a的修改在這里a本質(zhì)上還是變量只是在語法層面上限制了a的修改這里a被稱為常變量是最合適的。拓展在之后要學的C里const修飾的就不是變量直接就是常量了。但是現(xiàn)在又換了另一種思路說我不直接修改你的a我通過指針變量得到a的地址再通過地址去找到那塊空間去修改那塊空間的存放的數(shù)據(jù)這個方法好像有點道理我嘗試去改進代碼運行一下發(fā)現(xiàn)修改成功了這就有點像這么一個例子如下圖我有一間房子我每次回家都要從門進去每次出門都要鎖門但是有一天我忘帶鑰匙了此時門打不開那我為了進入家里我被迫翻越窗戶進到家中才達到我回家的目的這個場景是不是有點像上面的情況了。但是我最初的目的是想要a不被修改而上面是拿到地址才能去修改a的如何讓p拿到a的地址也不能成功去修改a呢這就引出了我接下來要講的知識繼續(xù)往下看2.const修飾指針變量我的解決辦法就是用const修飾指針變量限制指針變量p指向的對象a被修改但是在實現(xiàn)這個代碼之前還是得先來講一下這個const修飾指針變量的基本用法及意義吧。const修飾指針變量一般可以放在星號的左邊也可以放在星號的右邊如下那么const的位置不同表達的含義又有什么不同呢首先說明一下pa和pa的區(qū)別如下圖**const在星號的左邊限制的是p也就是p指向的對象不能被修改但是指針變量p的指向可以被修改**const在星號的右邊限制的是p也就是指針變量p的指向不能被修改但是p指向的對象可以被修改這時候我又要舉個例子幫助大家理解記憶了吃涼皮的例子首先創(chuàng)建2個變量名為男孩a和男孩b分別有10塊和100塊再創(chuàng)建一個指針變量p名為女孩p,取地址a賦給p,說明男孩a和女孩p是男女朋友當有一天女孩想吃涼皮了她想要男孩請他吃涼皮但是男孩不想要花錢所以在星號的左邊加上const限制他兜里的10塊被修改此時女孩說你不請我吃涼皮但是我有權(quán)利換男朋友啊男孩a明顯慌了他告訴女孩“我可以請你吃涼皮但是你不能換男朋友此時在星號的右邊加上了const限制女孩不能換男朋友他兜里的10塊可以被修改這個故事很好的說明了const在星號的不同位置上所起到的作用提醒一嘴如果既不愿意給你花錢也不愿意讓你換男朋友你大抵是遇上渣男了。2.野指針學習完const,接下來我們要去講一類指針叫做野指針。先來了解一下它的概念吧概念野指針就是指針指向的位置是不可知的隨機的、不正確的、沒有明確限制的說白了就是指針指向的空間不屬于當前的程序。1.野指針成因1.指針未初始化原因指針變量pa未初始化時pa屬于局部變量局部變量不初始化它里邊存放的是1個隨機值指針變量pa沒有明確指向的是內(nèi)存的哪一塊空間。2.指針的越界訪問原因p存放首元素的地址我們故意讓它循環(huán)11次p在前10次循環(huán)時p都是一個合法的指針但當加到10時p10所指向的空間已經(jīng)不屬于當前程序向內(nèi)存申請的空間了此時指針變成了野指針。3.指針指向的空間釋放原因雖然說test函數(shù)返回讓p接受到了n的地址但是函數(shù)的調(diào)用結(jié)束了局部變量n已經(jīng)被銷毀了。n已經(jīng)把那塊空間還回去了然后你想要通過接受到的n的地址去找到np所指向的那塊內(nèi)存空間已經(jīng)不屬于當前程序了即使你找到了那塊地址也只能獲得1個隨機值。2.如何規(guī)避野指針1.指針初始化情況一明確知道指針變量要初始化為什么值。inta10;int*paa;情況二不知道指針變量應(yīng)該給什么值可以給指針賦值為NULLint*pNULL;關(guān)于NULL的解釋NULL表示空指針的意思空指針就是沒有指向任何有效的空間暫時不要使用它。NULL 作為C語言中定義的一個標識符常量值是00也是地址這個地址是無法使用的讀寫該地址會報錯。舉個形象的例子大家就明白了將野指針看作是一條野狗野狗沒拴繩很危險此時NULL相當于一條狗繩將野狗暫時拴起來也就是指針變量防止變成野指針。2.小心指針越界當前程序向內(nèi)存申請了哪些空間只能允許指針訪問哪些空間不能超出范圍訪問不屬于你的空間不訪問只訪問自己的空間。3.指針變量不再使用時及時置為NULL指針使用之前檢查有效性如下圖4.避免返回局部變量的地址返回棧空間的地址不返回局部變量的地址就可以了與返回局部變量的區(qū)別函數(shù)返回局部變量時將局部變量的數(shù)據(jù)暫時存放在寄存器內(nèi)函數(shù)調(diào)用結(jié)束局部變量被銷毀主函數(shù)內(nèi)的變量通過關(guān)聯(lián)寄存器得到局部變量的數(shù)據(jù)。3.assert斷言我們前面不是通過if條件去判斷指針變量是否為空指針嗎而在這里其實還有1種更好的方法去判斷就是assert斷言。assert.h頭文件定義了宏assert(),用于在運行時確保程序符合指定條件如果不符合就報錯終止運行。這個宏常常被稱為**“斷言”**。assert()宏在括號里可以接受一個你想判斷的表達式作為參數(shù)若表達式為真即返回值為非0assert不會產(chǎn)生任何作用程序繼續(xù)運行若表達式為假即返回值為0assert發(fā)生報錯控制臺會顯示一條錯誤的信息包含沒有通過的表達式以及包含這個表達式的文件名和行號如下圖所示當然使用assert()也要包含對應(yīng)的頭文件#include assert.h如果使用assert()后確認程序沒有問題我們可以把它給關(guān)掉如果不關(guān)掉程序始終會默認執(zhí)行assert()這條語句增加程序運行的時間我們在頭文件前面定義一個宏NDEBUG,就可以關(guān)掉斷言如果下次還想使用直接將#define NDEBUG注釋或移除還有一點要注意的就是在VS這樣的集成開發(fā)環(huán)境下我們可以在Debug中使用在Release版本直接就是把assert()優(yōu)化掉了就是不起作用了。4.指針的使用和傳址調(diào)用1.strlen的模擬實現(xiàn)在上節(jié)課我們已經(jīng)對strlen函數(shù)通過一個函數(shù)實現(xiàn)了功能的模擬今天我們在重新地去梳理一下當我們學過const和assert斷言后我們可以把代碼去變得更加安全一些2.傳值調(diào)用和傳址調(diào)用學習指針的目的是為了用指針解決問題那么有什么問題非指針不可呢例如寫一個函數(shù)交換2個整型變量的值你的第一反應(yīng)可能會想創(chuàng)建一個臨時變量去幫助這2個整型變量的交換我們根據(jù)這個思路寫出下面的代碼運行結(jié)果我們發(fā)現(xiàn)a和b的值未發(fā)生交換這是為什么呢我們不妨調(diào)試一下調(diào)試過程中我們可以看到當函數(shù)將實參a、b傳遞給形參時形參x、y單獨向內(nèi)存申請了空間這也導(dǎo)致了它們內(nèi)存空間不一樣在交換過程中形參的改變對實參沒有影響交換x、y不影響a、b的值自然就達不到我們想要的結(jié)果了。這種方式叫做**“傳值調(diào)用”**。結(jié)論實參傳遞給形參的時候形參會單獨創(chuàng)建一份臨時空間來接收實參對形參的修改不影響實參。所以Swap1是失敗的了。那么怎么樣將實參和形參的改變聯(lián)系起來呢我們想讓函數(shù)內(nèi)部操作的參數(shù)就是主函數(shù)中的參數(shù)那這時候我們就要用到指針了通過main函數(shù)對swap函數(shù)的調(diào)用將a、b的地址傳遞給swap函數(shù)swap函數(shù)在間接使用該地址從而實現(xiàn)對a、b的交換。改進代碼如下這種方式叫做**“傳址調(diào)用”**可以讓函數(shù)和主調(diào)函數(shù)之間建立真正的聯(lián)系在函數(shù)內(nèi)部可以修改主調(diào)函數(shù)中的變量。所以未來函數(shù)中只是需要主調(diào)函數(shù)中的變量值來實現(xiàn)計算就可以采用傳值調(diào)用。如果函數(shù)內(nèi)部要修改主調(diào)函數(shù)中的變量的值就需要傳址調(diào)用??偨Y(jié)本文圍繞C語言指針的進階使用展開首先介紹了const修飾指針變量的兩種形式及其區(qū)別接著分析了野指針的成因并給出了初始化、防越界、置NULL等規(guī)避方法然后介紹了assert斷言這一更安全的指針檢查方式最后通過交換變量的例子清晰區(qū)分了傳值調(diào)用與傳址調(diào)用的適用場景。掌握這些內(nèi)容能幫助你在實際編程中寫出更健壯、更安全的指針代碼。