網站首頁 編程語言 正文
前提
指針,是C語言中的一個重要概念及其特點,也是掌握C語言比較困難的部分。指針也就是內存地址,指針變量是用來存放內存地址的變量,在同一CPU構架下,不同類型的指針變量所占用的存儲單元長度是相同的,而存放數據的變量因數據的類型不同,所占用的存儲空間長度也不同。有了指針以后,不僅可以對數據本身,也可以對存儲數據的變量地址進行操作
指針描述了數據在內存中的位置,標示了一個占據存儲空間的實體,在這一段空間起始位置的相對距離值。在 C/C++語言中,指針一般被認為是指針變量,指針變量的內容存儲的是其指向的對象的首地址,指向的對象可以是變量(指針變量也是變量),數組,函數等占據存儲空間的實體。
一.指針基礎
1.1 變量指針
//首先我們聲明一個變量 int a = 10; //聲明一個指針并指向該變量的地址 int* b = &a; printf("%d\n", *b);
運行結果:10
1.2 數據指針
//首先我們聲明一個數組變量 int c[10] = { 0,1,2,3,4,5,6,7,8,9 }; printf("c的地址:%p\n", c); printf("c[0]的地址:%p\n", &c[0]);
運行結果:兩個一樣
Why? 數組本質為內存空間上連續的空間,而作為數組的地址,其實為首元素的地址,而對于數組,數組名稱其實就是個指針(重點)
然后我們創建數組指針
int* d=c printf("d[9]的值為:%d\n",*(d+9)); //另一種寫法printf("d[9]的值為:%d\n",d[9]);
運行結果:9
1.3 指針的本質
通過1.0和1.1的指示我們突然發現,整數的指針和整數數組的指針居然是同個類型!
事實上確實是一個東西,指針為指向變量地址的類型,所以對于指針來說是不需要類型的,但是為了程序規范性,增加了類型說法
下面使用無類型指針輸出a的值(10)
void* p = &a; printf("void指針的值:%d\n", *(int*)p); //對比b指針變量 printf("這是b的值:%p\n", b); printf("這是p的值:%p\n", p);
上述結果可以完全表明,指針變量儲存的是地址,而且是單一變量的地址
接下來可以解釋數組和數為啥指針就是同一個類型了
回到1.2所述 單個數占了1個空間(這里不提占用內存),而數組占了n個空間,而指針指向的都是首地址,而對于單個數來說
首地址就是數所在的地址,而對于數組來說,首地址就是頭元素所在的地址
1.2解釋了數組地址就是數組名本身,所以數組指針就是數組本身了,所以有下面這種寫法
printf("直接使用數組名獲取索引3的元素:%d\n", c[3]); printf("使用指向c的指針獲取索引3的元素:%d\n", d[3]);
但是數組和指針數組還是有一定的區別
1.4 指針數組
/如果我們把指針看作一個變量,那么類比整數類型,我們一定可以聲明一個指向指針的指針
int g = 50; int* h = &g; int** i = &h; //輸出一下 printf("h的值:%p\n", h); printf("i的值:%p\n", *i); //實驗證明這是可行的,那我們嘗試做一個數組 int j[5] = { 9,8,7,6,5 }; int* k[5] = { &j[0],&j[1],&j[2],&j[3],&j[4] }; int** l = k; printf("k[1]的值:%p,對應的整數的值:%d\n", k[1], *k[1]); printf("l獲取k1的值:%p,對應的整數的值:%d\n", *(l + 1), **(l + 1));
這就是指針數組,把指針當作一個變量看,指針也可以做數組
1.5 指針的移動
對于數組指針獲取元素,上面展示了可以通過"變量名[索引]"的方式獲取,當然也可以通過移動指針位置來獲取
int* m = (int*)malloc(10 * sizeof(int)); for (int i = 0; i < 10; ++i) *(m++) = i;
指針移動到了分配內存的最后一塊
“變量名[索引]“的方式表明了是以當前指針為數組頭,偏移索引個單位為方法獲取元素的方式,索引獲取第8個元素
錯誤寫法 printf(”%d”,m[8]);
正確寫法
printf("%d\n", *(m - 2));
所以釋放的時候要釋放頭指針,所以要回到頭指針
for (int i = 0; i < 10; ++i) (--m); free(m);
所以實際操作的情況下盡量不要去移動指針
int* n = (int*)malloc(10 * sizeof(int)); for (int i = 0; i < 10; ++i) *(n + i) = i; printf("%d\n", *(n + 8)); free(n);
1.5 Scanf函數的解釋
scanf函數為獲取變量地址函數
首先我們看一下scanf函數的定義
scanf函數有一個格式符參數和一個可變參數
可變參數類型為指針!!!!
char o;
第一種直接寫法
scanf("%c", &o);
printf(“輸出的字符:%c\n”, o);
同理
用指針獲取
char* q = &o; scanf("%c", q); printf("通過指針獲取輸出的字符:%c\n", *q);
上面我們說了,指針可以代表數組,而c語言對字符串的定義就是字符數組
char r[] = { "Hello" }; scanf("%s", r); printf("字符串的值:%s\n", r);
這樣我們就可以解釋為什么對于獲取字符串的時候,不用寫&的原因了,因為字符數組本來就是指針!!
二.指針的進階玩法
2.1 二維指針
由名字可得,二維指針就是指針的指針,指針可以代表數組,自然二維指針就可以代表二維數組
int u[4][4] = { {1,2,3,4},{2,3,4,5},{3,4,5,6},{4,5,6,7} }; int** v = u;
二維數組在內存空間上也是線性排列的,就如下順序
1 2 3 4 2 3 4 5 3 4 5 6 4 5 6 7
所以二維數組獲取元素也可以靠指針移動
printf("u[1][2]的值:%d\n", *(v + 6));
也可以動態內存分配實現
int** w = (int**)malloc(4 * sizeof(int)); //分配行內存 for (int i = 0; i < 4; ++i) w[i] = (int*)malloc(4 * sizeof(int)); //分配列內存 for (int i = 0; i < 4; ++i) for (int j = 0; j < 4; ++j) w[i][j] = (j + 1) + i; printf("w[1][2]的值:%d\n", w[1][2]); free(w); //記得釋放
2.2 結構體指針
先聲明一個結構體
struct people { char* name; int age; }SPeople; //方便表示替換掉標識符 typedef struct people People; People dr; dr.name = "aaa"; dr.age = 18; printf("dr的名字是:%s,年齡是:%d\n", wusihao.name, wusihao.age); People* lp = &wusihao; //注意屬性獲取的符號"->" lp->name = "sss"; lp->age = 19; printf("lp的名字是:%s,年齡是:%d\n", lp->name, lp->age);
當然也可以動態內存分配使用
People* lps = (People*)malloc(1 * sizeof(People)); lps->name = "xxx"; lps->age = 20; printf("lps的名字是:%s,年齡是:%d\n", lps->name, lps->age);
結語
指針定義后,在不用時最好指向NULL,比如
int* s = &a; printf("%p\n", s); s = NULL;
因為就算釋放掉內存,但是指針指向的內存地址依舊可用(獲取處于沉睡狀態),所以最好不要去動它,不然很容易導致內存泄露
動態聲明的指針一定要釋放,free函數釋放
動態分配內存最好要檢查是否成功分配到
int* t = (int*)malloc(10 * sizeof(int)); if (t == NULL) { //分配錯誤 system("pause"); return 0; } else { //你的代碼 system("pause"); free(t); }
原文鏈接:https://blog.csdn.net/qq_39935495/article/details/123031315
相關推薦
- 2023-04-07 C#?async/await任務超時處理的實現_C#教程
- 2022-06-08 FreeRTOS實時操作系統的任務創建和刪除_操作系統
- 2022-12-08 C語言如何實現成績等級判別_C 語言
- 2023-02-10 docker實現跨宿主機的容器之間網絡互聯_docker
- 2022-04-23 git如何提交本地倉庫并同步碼云倉庫
- 2023-10-27 np.array()函數的使用方法_python
- 2022-09-08 Python如何將list中的string轉換為int_python
- 2022-12-27 React生命周期函數圖解介紹_React
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支