日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

栈顶指针到底指向哪_被称为“程序员试金石”的指针真的没有那么难!不信的话你来看看

發布時間:2024/2/28 编程问答 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 栈顶指针到底指向哪_被称为“程序员试金石”的指针真的没有那么难!不信的话你来看看 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

很多朋友放棄C語言都是因為指針,說什么指針的*號很討厭啦、分不清址與值啦,當然了,最煩的還是鏈表結點,本來鏈表操作就讓人煩了,再加上指針這個東西真是讓初學的朋友苦不堪言,最后放棄了C語言轉投其他語言的懷抱,但是只要理解指針的本質,那么掌握指針只是時間問題,這篇文章就跟大家一起來看看C語言的指針到底是何方神圣。文末放了一張c/c++入門學習大綱,有需要的朋友可以看一下。

有什么不理解的地方歡迎進群973961276來跟大伙一下交流,或者直接到課堂上面對面請教c/c++ 項目實戰,閑話少說,我們開啟正文。


一、指針到底是什么?

計算機中所有的數據都必須放在內存中,不同類型的數據占用的字節數不一樣,例如 int 占用 4 個字節,char 占用 1 個字節。為了正確地訪問這些數據,必須為每個字節都編上號碼,就像門牌號、身份證號一樣,每個字節的編號是唯一的,根據編號可以準確地找到某個字節。
下圖是 4G 內存中每個字節的編號(以十六進制表示):

?
我們將內存中字節的編號稱為地址(Address)或指針(Pointer)。地址從 0 開始依次增加,對于 32 位環境,程序能夠使用的內存為 4GB,最小的地址為 0,最大的地址為 0XFFFFFFFF。
下面的代碼演示了如何輸出一個地址:

#include <stdio.h> int main(){ int a = 100; char str[20] = "c.biancheng.net"; printf("%#X, %#Xn", &a, str); return 0; }

運行結果:

0X28FF3C, 0X28FF10

%#X表示以十六進制形式輸出,并附帶前綴0X。a 是一個變量,用來存放整數,需要在前面加&來獲得它的地址;str 本身就表示字符串的首地址,不需要加&。

C語言中有一個控制符%p,專門用來以十六進制形式輸出地址,不過 %p 的輸出格式并不統一,有的編譯器帶0x前綴,有的不帶,所以此處我們并沒有采用。

一切都是地址

C語言用變量來存儲數據,用函數來定義一段可以重復使用的代碼,它們最終都要放到內存中才能供 CPU 使用。
數據和代碼都以二進制的形式存儲在內存中,計算機無法從格式上區分某塊內存到底存儲的是數據還是代碼。當程序被加載到內存后,操作系統會給不同的內存塊指定不同的權限,擁有讀取和執行權限的內存塊就是代碼,而擁有讀取和寫入權限(也可能只有讀取權限)的內存塊就是數據。
CPU 只能通過地址來取得內存中的代碼和數據,程序在執行過程中會告知 CPU 要執行的代碼以及要讀寫的數據的地址。如果程序不小心出錯,或者開發者有意為之,在 CPU 要寫入數據時給它一個代碼區域的地址,就會發生內存訪問錯誤。這種內存訪問錯誤會被硬件和操作系統攔截,強制程序崩潰,程序員沒有挽救的機會。
CPU 訪問內存時需要的是地址,而不是變量名和函數名!變量名和函數名只是地址的一種助記符,當源文件被編譯和鏈接成可執行程序后,它們都會被替換成地址。編譯和鏈接過程的一項重要任務就是找到這些名稱所對應的地址。
假設變量 a、b、c 在內存中的地址分別是 0X1000、0X2000、0X3000,那么加法運算c = a + b;將會被轉換成類似下面的形式:

0X3000 = (0X1000) + (0X2000);

( )表示取值操作,整個表達式的意思是,取出地址 0X1000 和 0X2000 上的值,將它們相加,把相加的結果賦值給地址為 0X3000 的內存
變量名和函數名為我們提供了方便,讓我們在編寫代碼的過程中可以使用易于閱讀和理解的英文字符串,不用直接面對二進制地址,那場景簡直讓人崩潰。
需要注意的是,雖然變量名、函數名、字符串名和數組名在本質上是一樣的,它們都是地址的助記符,但在編寫代碼的過程中,我們認為變量名表示的是數據本身,而函數名、字符串名和數組名表示的是代碼塊或數據塊的首地址。

關于程序內存、編譯鏈接、可執行文件的結構以及如何找到名稱對應的地址我們后面會講。

光看文字無法完全理解的朋友可以看這個視頻c/c++《指針詳解》,以便更好的理解


二、指針變量的定義和使用

數據在內存中的地址也稱為指針,如果一個變量存儲了一份數據的指針,我們就稱它為指針變量。
在C語言中,允許用一個變量來存放指針,這種變量稱為指針變量。指針變量的值就是某份數據的地址,這樣的一份數據可以是數組、字符串、函數,也可以是另外的一個普通變量或指針變量。
現在假設有一個 char 類型的變量 c,它存儲了字符 'K'(ASCII碼為十進制數 75),并占用了地址為 0X11A 的內存(地址通常用十六進制表示)。另外有一個指針變量 p,它的值為 0X11A,正好等于變量 c 的地址,這種情況我們就稱 p 指向了 c,或者說 p 是指向變量 c 的指針。

?

定義指針變量

定義指針變量與定義普通變量非常類似,不過要在變量名前面加星號*,格式為:

datatype *name;

或者

datatype *name = value;

*表示這是一個指針變量,datatype表示該指針變量所指向的數據的類型 。例如:

int *p1;

p1 是一個指向 int 類型數據的指針變量,至于 p1 究竟指向哪一份數據,應該由賦予它的值決定。再如:

int a = 100; int *p_a = &a;

在定義指針變量 p_a 的同時對它進行初始化,并將變量 a 的地址賦予它,此時 p_a 就指向了 a。值得注意的是,p_a 需要的一個地址,a 前面必須要加取地址符&,否則是不對的。
和普通變量一樣,指針變量也可以被多次寫入,只要你想,隨時都能夠改變指針變量的值,請看下面的代碼:

//定義普通變量 float a = 99.5, b = 10.6; char c = '@', d = '#'; //定義指針變量 float *p1 = &a; char *p2 = &c; //修改指針變量的值 p1 = &b; p2 = &d;

*是一個特殊符號,表明一個變量是指針變量,定義 p1、p2 時必須帶*。而給 p1、p2 賦值時,因為已經知道了它是一個指針變量,就沒必要多此一舉再帶上*,后邊可以像使用普通變量一樣來使用指針變量。也就是說,定義指針變量時必須帶*,給指針變量賦值時不能帶*。
假設變量 a、b、c、d 的地址分別為 0X1000、0X1004、0X2000、0X2004,下面的示意圖很好地反映了 p1、p2 指向的變化:

?

需要強調的是,p1、p2 的類型分別是float*和char*,而不是float和char,它們是完全不同的數據類型,讀者要引起注意。
指針變量也可以連續定義,例如:

  • int *a, *b, *c; //a、b、c 的類型都是 int*
  • 注意每個變量前面都要帶*。如果寫成下面的形式,那么只有 a 是指針變量,b、c 都是類型為 int 的普通變量:

  • int *a, b, c;
  • 通過指針變量取得數據

    指針變量存儲了數據的地址,通過指針變量能夠獲得該地址上的數據,格式為:

    *pointer;

    這里的*稱為指針運算符,用來取得某個地址上的數據,請看下面的例子:

    #include <stdio.h> int main(){ int a = 15; int *p = &a; printf("%d, %dn", a, *p); //兩種方式都可以輸出a的值 return 0; }

    運行結果:
    15, 15
    假設 a 的地址是 0X1000,p 指向 a 后,p 本身的值也會變為 0X1000,*p 表示獲取地址 0X1000 上的數據,也即變量 a 的值。從運行結果看,*p 和 a 是等價的。
    上節我們說過,CPU 讀寫數據必須要知道數據在內存中的地址,普通變量和指針變量都是地址的助記符,雖然通過 *p 和 a 獲取到的數據一樣,但它們的運行過程稍有不同:a 只需要一次運算就能夠取得數據,而 *p 要經過兩次運算,多了一層“間接”。
    假設變量 a、p 的地址分別為 0X1000、0XF0A0,它們的指向關系如下圖所示:

    ?

    程序被編譯和鏈接后,a、p 被替換成相應的地址。使用 *p 的話,要先通過地址 0XF0A0 取得變量 p 本身的值,這個值是變量 a 的地址,然后再通過這個值取得變量 a 的數據,前后共有兩次運算;而使用 a 的話,可以通過地址 0X1000 直接取得它的數據,只需要一步運算。
    也就是說,使用指針是間接獲取數據,使用變量名是直接獲取數據,前者比后者的代價要高。
    指針除了可以獲取內存上的數據,也可以修改內存上的數據,例如:

    #include <stdio.h> int main(){ int a = 15, b = 99, c = 222; int *p = &a; //定義指針變量 *p = b; //通過指針變量修改內存上的數據 c = *p; //通過指針變量獲取內存上的數據 printf("%d, %d, %d, %dn", a, b, c, *p); return 0; }

    運行結果:
    99, 99, 99, 99
    *p 代表的是 a 中的數據,它等價于 a,可以將另外的一份數據賦值給它,也可以將它賦值給另外的一個變量。*在不同的場景下有不同的作用:*可以用在指針變量的定義中,表明這是一個指針變量,以和普通變量區分開;使用指針變量時在前面加*表示獲取指針指向的數據,或者說表示的是指針指向的數據本身。
    也就是說,定義指針變量時的*和使用指針變量時的*意義完全不同。以下面的語句為例:

    int *p = &a; *p = 100;

    第1行代碼中*用來指明 p 是一個指針變量,第2行代碼中*用來獲取指針指向的數據。
    需要注意的是,給指針變量本身賦值時不能加*。修改上面的語句:

    int *p; p = &a; *p = 100;

    第2行代碼中的 p 前面就不能加*。
    指針變量也可以出現在普通變量能出現的任何表達式中,例如:

    int x, y, *px = &x, *py = &y; y = *px + 5; //表示把x的內容加5并賦給y,*px+5相當于(*px)+5 y = ++*px; //px的內容加上1之后賦給y,++*px相當于++(*px) y = *px++; //相當于y=(*px)++ py = px; //把一個指針的值賦給另一個指針


    【示例】通過指針交換兩個變量的值。

    #include <stdio.h> int main(){ int a = 100, b = 999, temp; int *pa = &a, *pb = &b; printf("a=%d, b=%dn", a, b); /*****開始交換*****/ temp = *pa; //將a的值先保存起來 *pa = *pb; //將b的值交給a *pb = temp; //再將保存起來的a的值交給b /*****結束交換*****/ printf("a=%d, b=%dn", a, b); return 0; }

    運行結果:
    a=100, b=999
    a=999, b=100
    從運行結果可以看出,a、b 的值已經發生了交換。需要注意的是臨時變量 temp,它的作用特別重要,因為執行*pa = *pb;語句后 a 的值會被 b 的值覆蓋,如果不先將 a 的值保存起來以后就找不到了。

    關于 * 和 & 的謎題

    假設有一個 int 類型的變量 a,pa 是指向它的指針,那么*&a和&*pa分別是什么意思呢?*&a可以理解為*(&a),&a表示取變量 a 的地址(等價于 pa),*(&a)表示取這個地址上的數據(等價于 *pa),繞來繞去,又回到了原點,*&a仍然等價于 a。&*pa可以理解為&(*pa),*pa表示取得 pa 指向的數據(等價于 a),&(*pa)表示數據的地址(等價于 &a),所以&*pa等價于 pa。

    對星號*的總結

    在我們目前所學到的語法中,星號*主要有三種用途:

    • 表示乘法,例如int a = 3, b = 5, c; c = a * b;,這是最容易理解的。
    • 表示定義一個指針變量,以和普通變量區分開,例如int a = 100; int *p = &a;。
    • 表示獲取指針指向的數據,是一種間接操作,例如int a, b, *p = &a; *p = 100; b = *p;

    這里推薦一本書以便更好的理解《C和指針》,有需要的朋友可以評論區留言或者進群獲取。

    三、C語言指針變量的運算(加法、減法和比較運算)

    指針變量保存的是地址,而地址本質上是一個整數,所以指針變量可以進行部分運算,例如加法、減法、比較等,請看下面的代碼:

    #include <stdio.h> int main(){ int a = 10, *pa = &a, *paa = &a; double b = 99.9, *pb = &b; char c = '@', *pc = &c; //最初的值 printf("&a=%#X, &b=%#X, &c=%#Xn", &a, &b, &c); printf("pa=%#X, pb=%#X, pc=%#Xn", pa, pb, pc); //加法運算 pa++; pb++; pc++; printf("pa=%#X, pb=%#X, pc=%#Xn", pa, pb, pc); //減法運算 pa -= 2; pb -= 2; pc -= 2; printf("pa=%#X, pb=%#X, pc=%#Xn", pa, pb, pc); //比較運算 if(pa == paa){ printf("%dn", *paa); }else{ printf("%dn", *pa); } return 0; }

    運行結果:

    &a=0X28FF44, &b=0X28FF30, &c=0X28FF2B pa=0X28FF44, pb=0X28FF30, pc=0X28FF2B pa=0X28FF48, pb=0X28FF38, pc=0X28FF2C pa=0X28FF40, pb=0X28FF28, pc=0X28FF2A 2686784

    從運算結果可以看出:pa、pb、pc 每次加 1,它們的地址分別增加 4、8、1,正好是 int、double、char 類型的長度;減 2 時,地址分別減少 8、16、2,正好是 int、double、char 類型長度的 2 倍。
    這很奇怪,指針變量加減運算的結果跟數據類型的長度有關,而不是簡單地加 1 或減 1,這是為什么呢?
    以 a 和 pa 為例,a 的類型為 int,占用 4 個字節,pa 是指向 a 的指針,如下圖所示:

    ?
    剛開始的時候,pa 指向 a 的開頭,通過 *pa 讀取數據時,從 pa 指向的位置向后移動 4 個字節,把這 4 個字節的內容作為要獲取的數據,這 4 個字節也正好是變量 a 占用的內存。
    如果pa++;使得地址加 1 的話,就會變成如下圖所示的指向關系:

    ?
    這個時候 pa 指向整數 a 的中間,*pa 使用的是紅色虛線畫出的 4 個字節,其中前 3 個是變量 a 的,后面 1 個是其它數據的,把它們“攪和”在一起顯然沒有實際的意義,取得的數據也會非常怪異。
    如果pa++;使得地址加 4 的話,正好能夠完全跳過整數 a,指向它后面的內存,如下圖所示:

    ?
    我們知道,數組中的所有元素在內存中是連續排列的,如果一個指針指向了數組中的某個元素,那么加 1 就表示指向下一個元素,減 1 就表示指向上一個元素,這樣指針的加減運算就具有了現實的意義,我們將在《C語言數組指針》一節中深入探討。
    不過C語言并沒有規定變量的存儲方式,如果連續定義多個變量,它們有可能是挨著的,也有可能是分散的,這取決于變量的類型、編譯器的實現以及具體的編譯模式,所以對于指向普通變量的指針,我們往往不進行加減運算,雖然編譯器并不會報錯,但這樣做沒有意義,因為不知道它后面指向的是什么數據。
    下面的例子是一個反面教材,警告讀者不要嘗試通過指針獲取下一個變量的地址:

    #include <stdio.h> int main(){ int a = 1, b = 2, c = 3; int *p = &c; int i; for(i=0; i<8; i++){ printf("%d, ", *(p+i) ); } return 0; }

    在 VS2010 Debug 模式下的運行結果為:

    3, -858993460, -858993460, 2, -858993460, -858993460, 1, -858993460,

    可以發現,變量 a、b、c 并不挨著,它們中間還參雜了別的輔助數據。
    指針變量除了可以參與加減運算,還可以參與比較運算。當對指針變量進行比較運算時,比較的是指針變量本身的值,也就是數據的地址。如果地址相等,那么兩個指針就指向同一份數據,否則就指向不同的數據。
    上面的代碼(第一個例子)在比較 pa 和 paa 的值時,pa 已經指向了 a 的上一份數據,所以它們不相等。而 a 的上一份數據又不知道是什么,所以會導致 printf() 輸出一個沒有意義的數,這正好印證了上面的觀點,不要對指向普通變量的指針進行加減運算。
    另外需要說明的是,不能對指針變量進行乘法、除法、取余等其他運算,除了會發生語法錯誤,也沒有實際的含義。


    就先寫這么多吧,如果有人看的話后面再更新,最后給大家發個福利,這是一本理解指針的電子書?《C和指針》

    因為很多朋友學習c/c++都不得門而入,所以最后再給大家貼個學習大綱?

    ?

    好了,這篇文章就到這里了,如果這篇文章沒有解決到你的問題,可以進群來咨詢老師和領取優質視頻跟電子書資料哦,覺得寫的還不錯的話請關注點贊收藏三連!

    ?

    總結

    以上是生活随笔為你收集整理的栈顶指针到底指向哪_被称为“程序员试金石”的指针真的没有那么难!不信的话你来看看的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 国产精品成人一区二区三区 | 亚洲精品成a人 | 十八禁一区二区三区 | 色91在线 | 亚洲一区中文字幕在线 | 精品+无码+在线观看 | 午夜a级片 | 亚洲色域网 | 天天躁狠狠躁 | 天天天天 | 亚洲自拍网站 | 中文字幕校园春色 | 在线观看欧美成人 | 国产又粗又猛又爽又黄 | 调教丰满的已婚少妇在线观看 | 日本高清视频网站 | 欧美视频一区二区三区四区在线观看 | 久热免费 | 国产91在线视频观看 | 男人的影院 | 男女69视频 | 日本极品少妇 | 一区二区三区中文视频 | 国产123区在线观看 91国产一区二区 | 亚洲一区二区蜜桃 | www.狠狠| 在线91视频 | 日韩欧美综合在线 | 日韩电影第一页 | 色欲狠狠躁天天躁无码中文字幕 | 无码少妇精品一区二区免费动态 | 99精品国产99久久久久久97 | 日韩大片免费观看视频播放 | free性欧美hd精品4k | 欧美日韩在线观看一区二区三区 | 免费在线观看黄色网址 | 国产伦理久久精品久久久久 | 亚洲欧洲自拍 | 欧美日韩中文字幕在线 | 欧亚免费视频 | 亚洲男女激情 | av中文字幕第一页 | 黄色在线免费视频 | 又嫩又硬又黄又爽的视频 | 日本黄色录象 | 超碰c | 777黄色| 国产三级播放 | www.久久成人 | 91插插插视频 | 亚洲天堂av一区二区三区 | 精品人妻无码专区在线 | 热久久网站 | 中文字幕一区二区三区久久久 | 黄色a级免费 | 亚洲天堂av免费在线观看 | 各种含道具高h调教1v1男男 | 久久精品亚洲无码 | 午夜精品视频在线观看 | 午夜激情在线播放 | 亚洲第一免费视频 | 中文字幕2区 | 好吊色一区二区 | 国产欧美久久一区二区三区 | 国产日韩欧美亚洲 | 久久精品无码专区 | 欧美精品小视频 | 日韩欧美大片 | 91豆花视频 | 老熟妇一区二区三区啪啪 | 91久久精品美女高潮 | 国产一区二区不卡在线 | free国产hd露脸性开放 | av无码一区二区三区 | 国产人妻人伦精品1国产盗摄 | 逼逼av网站 | 国产91精品露脸国语对白 | 苍井空浴缸大战猛男120分钟 | 国产女人和拘做受视频免费 | 日本视频不卡 | 91精品久久久久久久久久久 | 国产清纯白嫩初高中在线观看性色 | 公交顶臀绿裙妇女配视频 | 日韩免费电影一区 | 91综合色 | 宿舍女女闺蜜调教羞辱 | 欧美日韩亚洲国产一区 | 国产成人资源 | 欧美11一13sex性hd | 亚洲在线观看视频 | 99热在线这里只有精品 | 国产精品久久久久电影 | 久久久久伊人 | 国产伦精品一区二区三区 | 日本电影大尺度免费观看 | 99热热热热 | 亚洲va久久久噜噜噜久久天堂 | 91av免费观看 | 蜜桃传媒一区二区亚洲av |