文章目錄
本文涉及到的代碼是C寫的,但是在C++上一樣適用
0.背景知識–堆和棧的理解
堆和棧的學習:https://blog.csdn.net/pt666/article/details/70876410(這個是基礎)
個人理解:(可能有不準確的地方,歡迎指正)
1.當我們在程序中初始化一個變量的時候,首先在棧內存裏面建立一個叫age的變量,存儲下來
2.系統然後在堆內存中,開闢一塊空間,存儲具體的值19,例如內存地址爲:0fxx02
3.然後棧內存變量age的值即爲ofxx02這個地址值
4.程序如果想調用age的時候,直接先找個變量的地址,然後再去這個地址找對應的值
1.C中對變量賦值的理解
#include <stdio.h>
#include<string.h>
#include<stdlib.h>
int main(void)
{
int age = 20;
printf("age = %d\n", age);
printf("age的地址:%p\n", &age);
age = 30;
printf("age = %d\n", age);
printf("age的地址:%p\n", &age);
}
// 如上的結果爲:
age = 20
age的地址:006FFD04 #
age = 30
age的地址:006FFD04
以上代碼解析:
-
1.最初的時候,age存儲的情況
-
2.後續程序對變量重新賦值,相當於把之前存20值的內存地址006FFD04,重新存儲爲30.(隱藏的含義是內存地址不變,值改變了!)
2.指針和內存地址的理解
指針,是C語言中的一個重要概念及其特點,也是掌握C語言比較困難的部分。指針也就是內存地址,指針變量是用來存放內存地址的變量,不同類型的指針變量所佔用的存儲單元長度是相同的,而存放數據的變量因數據的類型不同,所佔用的存儲空間長度也不同.
- &用來取一個變量的地址
- * 用來取一個地址(指針)的值
1.case1:獲取變量的地址和指針的值
#include <stdio.h>
int main(void)
{
int age = 19;
int* p_age = &age;
printf("age 的地址爲: %p\n", &age);
printf("* p_age 指針的值:%d\n", *p_age);
}
age 的地址爲: 008FFE98
* p_age 指針的值:19
2.case2:對指針的值重新賦值
#include <stdio.h>
int main(void)
{
int age = 19;
int* p_age = &age;
printf("age 的地址爲: %p\n", &age);
printf("* p_age 指針的值:%d\n", *p_age);
*p_age = 20;
printf("age 的地址爲: %p\n", &age);
printf("* p_age 指針的值:%d\n", *p_age);
printf("age的值爲:%d\n", age);
}
age 的地址爲: 009FF898
* p_age 指針的值:19
age 的地址爲: 009FF898
* p_age 指針的值:20
age的值爲:20
難理解的是,爲什麼第二次打印*p_age是20:
1.因爲p_age指針已經指向了age變量的內存地址,即int p_age = &age
2.所以 p_age相當於是堆內存的地址,*p_age = 20 ,就是上面講到的賦值的原理過程。
3.case3:改變指針的指向
#include <stdio.h>
int main(void)
{
int age = 19;
int* p = &age;
printf("age 的地址爲: %p, age的值爲: % d\n", &age, age);
printf("* p 指針的值:%d, p的值爲:%p\n", *p, p);
int height = 185;
p = &height; //改變了指針的指向,對應的p存儲的地址也就改變了
printf("===========================\n");
printf("age 的地址爲: %p, age的值爲: % d\n", &age, age);
printf("* p指針的值:%d, p的值爲:%p\n", *p, p);
printf("height 的地址爲: %p, height的值爲: % d\n", &height, height);
}
age 的地址爲: 004FFC3C, age的值爲: 19
* p 指針的值:19, p的值爲:004FFC3C
===========================
age 的地址爲: 004FFC3C, age的值爲: 19
* p指針的值:185, p的值爲:004FFC24
height 的地址爲: 004FFC24, height的值爲: 185
原理如下:
1.最終的時候,指針指向了變量age,*p的值是age的值,p對應age的內存地址
2.後來,指針指向改變了,改爲指向height變量,*p的值是height的值,p對應的也就變爲height的內存地址了
3.三種參數傳遞方式:
參考:https://blog.csdn.net/cocohufei/article/details/6143476
1.按值傳遞參數(不推薦)
按值傳遞的過程爲:首先計算出實參表達式的值,接着給對應的形參變量分配一個存儲空間,該空間的大小等於該形參類型的,然後把以求出的實參表達式的值一一存入到形參變量分配的存儲空間中,成爲形參變量的初值,供被調用函數執行時使用。這種傳遞是把實參表達式的值傳送給對應的形參變量,故稱這種傳遞方式爲“按值傳遞”。(我讀了好幾遍,是不是感覺有點拗口,其實就是我上面說的賦值的過程)
#include <stdio.h>
void swap(int i, int n)
{
int t;
t = i;
i = n;
n = t;
printf("%d %d\n", i, n);
printf("%p %p\n", &i, &n);
}
int main()
{
int i = 20, n = 30;
printf("%p %p\n", &i, &n);
printf("===================\n");
swap(i, n);
printf("===================\n");
printf("%d %d\n", i, n);
return 0;
}
// 結果如下:
006FF958 006FF94C
===================
30 20
006FF874 006FF878
===================
20 30
- 調用swap函數本身沒有對原始實參進行操作,只有原始實參對應的地址或對應的值變動了,纔算對原始實參操作了。
- 雖然在swap函數中形參i, n發生了變化,但對swap函數執行完回收了形參i,n,main函數的實參i,n仍爲調用前的值
2.按地址傳遞參數(感覺很NB,但不實用,特殊場合除外)
#include <stdio.h>
void swap(int*, int*); # 定義的2個變量是,需要傳遞的爲指針,即地址的值
int main()
{
int a = 20, b = 30;
printf("a = %d ,b=%d \n", a, b);
swap(&a, &b);
printf("a = %d ,b=%d \n", a, b);
system("pause");
return 0;
}
void swap(int* x, int* y)
{
int t = *x;
*x = *y;
*y = t;
}
a = 20 ,b=30
a = 30 ,b=20
3.引用傳遞參數(C++專享,C不支持)
有關這個,請移步我的另一篇博文:
https://blog.csdn.net/chenmozhe22/article/details/106116120
4.多級指針的原理
1.一級指針圖示:
- 在堆內存中保存數據的地址爲00B8A000
- 指針b在棧內存的地址爲00B8A004,但指針變量b會保存堆內存的地址00B8A000,方便指針找到堆內存
- *b的過程是先找到指針變量b對應的堆內存地址,然後再算出來這個地址對應的值,即爲10
2.二級指針的圖示:
- 二級指針c,因爲指向了一級指針b,根據一級指針的原理,C指針(指針變量c存放具體的地址值),應該存放的是b指針的內存地址,即爲00B8A004
- 二級指針**c指向一級指針b,按照一級指針的原理,b變量存放的是其指向內存的地址,**c指向b,所以c(c指針變量才能存儲值,c指針本身只有指向關係)指針存放的是b指針的內存地址 (很重要!很重要!很重要!)
- 棧內存上存放的都是內存地址,因爲**c指向b,b指向a(堆內存存放的是具體的數值),所以b取值的話,直接找到堆內存,**c取值的話,先找到b,最終還是找到a變量。
- 無論是二級指針還是多級指針,都按照二部分劃分,指針類型(int * 或int ** 或int ***)和指針變量 (p),指針變量賦值保存被指向的內存地址,指針類型負責規定是一級指針、二級指針、或三級指針
#include <stdio.h>
int main() {
int a = 10;
int* b = &a;
int** c = &b;
printf("a的值:%d\n", a); // a的實際值
printf("a的地址:%p\n", &a); // a的地址值
printf("==================\n");
printf("b的地址:%p\n", b); //一級指針b指向變量a,那b保存的值爲a的地址值
printf("&b的地址:%p\n", &b); //一級指針b,對應的&b是指針b的真實地址,
printf("*b的值:%d\n", *b); //指針b指向變量的真實的值
printf("==================\n");
printf("c的地址:%p\n", c); // 二級指針的指針變量的值爲:被指向指針的真實地址,即b的真實地址&b
printf("&c的地址:%p\n", &c); // 二級指針c,本身的地址&c
printf("*c的地址:%p\n", *c); // 二級指針c,所指向內存空間所保存的真實的值,即b指針保存的真實值,而b指針真實保存的是a的地址,即&a
printf("**c的值:%d\n", **c); // 二級指針,最終指向棧內存的值
return 0;
}
// 如上結果爲:
a的值:10
a的地址:00A3FB94
==================
b的地址:00A3FB94
&b的地址:00A3FB88
*b的值:10
==================
c的地址:00A3FB88
&c的地址:00A3FB7C
*c的地址:00A3FB94
**c的值:10
學習文章:https://www.cnblogs.com/lulipro/p/7460206.html#commentform