點擊直接查看粉絲福利:點我領取粉絲福利
點擊直接開始正文:點我跳過目錄
文章目錄
- C語言指標淺談
- 1. 普通指標
- 1.1 指標的定義
- 1.2 指標實體理解
- 1.3 頭腦風暴
- 1.4 頭腦風暴(二)
- 1.5 二級指標
- 1.7 指標常量
- 1.8 常量指標
- 2. 陣列指標/指標陣列
- 2.1 陣列的理解
- 2.2 陣列強制型別和下標
- 2.3 陣列指標
- 2.4 指標陣列
- 3. 函式指標
- 3.1 函式
- 3.2 指向函式的指標
立馬開始學習
C語言指標淺談
最近在看一本關于C語言的書,普通語法沒什么,就是指標這個地方確實有點難,現在寫一點關于我對指標的理解,當然肯定會有理解不到位的地方,希望多多擔待,
1. 默認你有其他語言基礎比如Java,Python,C++等語言基礎
2. 默認你對計算機有所了解
3. 默認你想學習,而不是當個熱鬧看
那么到底什么才是指標呢?
指標就是一串代表記憶體地址的數字,通常使用十六進制來表示,
正因為我們可以直接對記憶體地址進行操作,所以我們才說C語言功能強大,
說起指標,以我目前所學,我認為暫時可以分為以下幾種型別:
- 普通指標——指向一個變數的指標
- 陣列指標——能對陣列進行操作的指標
- 函式指標——指向函式的指標
pass:為什么陣列指標不說是指向陣列的指標,
這個原因會在陣列指標的地方,對陣列進行分析,讓你了解陣列的形成,這里就不多做贅述了
1. 普通指標
什么是普通指標,普通指標就是指向基本資料型別的指標,比如int 、float等,
1.1 指標的定義
我認為實戰是最好的理解方式,所以會有代碼以及注釋詳細理解,不過在你看代碼之前,你應該知道這些東西:
- 如何定義一個指標
- 如何給指標賦值
- 給指標賦值后,怎么使用原變數的值
就和定義一個普通變數一樣:型別 *變數名
指標變數接收的是變數的記憶體地址<br> 在C語言中,通過符號**&**來取出變數的記憶體地址
賦值也是同樣的
1.2 指標實體理解
那么你知道了這些知識后,就看代碼:
#include<stdio.h>
int main()
{
int num = 10;
// 創建一個int型別的變數,并賦值為10
int* pnum;
// 創建一個int型別的指標,你還能這樣寫 int *pnum
pnum = #
// &num是num在記憶體空間的記憶體地址
// 這句代碼是將num的記憶體地址賦值給pnum
printf("num的值為:%d\n&num的值為:%p\n*pnum的值為:%d\npnum的值為:%p",num,&num,*pnum,pnum);
// 將各個值都列印出來看看效果
return 0;
}
運行結果是:
num的值為:10
&num的值為:0xff8effe0
*pnum的值為:10
pnum的值為:0xff8effe0
1.3 頭腦風暴
看了這段代碼,是不是對指標有了更深刻的了解了呢?
如果你想學好,就暫停你的進度,思考一下:
- pnum是什么,他開辟的記憶體空間是多大
- *pnum是什么,有什么用
- &pnum是什么,他的作用是什么
思考之后來看看吧~~
那么我們看著代碼和運行結果可以總結出以下內容:
接下來,你就想想你,你的身份證號,你的身份證,國家資訊系統
- num是一個變數,這個變數可以對10進行操作
- 將10當成你,num是你的名字
- 宣告一個變數后,記憶體空間會為變數開辟一個記憶體空間以及記憶體地址
- 而你出生后也會有一個身份證號
- &num是變數的記憶體地址,這里&num雖然是記憶體地址,但是不說&num是num的指標,因為指標是一個變數,俗稱指標變數
- num在記憶體的記憶體地址相當于你的身份證號
- 你的身份證號只是一串陣列,抽象存在
- pnun是一個指標變數,他的值是是&num,也就是一個普通變數的記憶體地址
- 把它當成你的身份證
- 你的身份賬號在上面,就可以通過身份證號(指標)進行操作(買票,辦卡等)
- *pnum是通過記憶體地址獲取到該記憶體地址存盤的值
- 就是通過你的身份證在國家系統找到了你
- 也可以對你進行操作(比如征信,車票等)
1.4 頭腦風暴(二)
試想,你如果宣告一個變數,并將該指標變數的記憶體地址給該指標,也就是讓指標變數存盤的是指標的記憶體空間,會有什么事情發生?
思考:我們的指標變數是一個存盤記憶體地址的指標,但他同樣也還是一個變數,所以也會在記憶體中有自己的記憶體地址,而剛好指標存盤的就是變數!!等等等等,一拍即合,我們就把指標的記憶體賦給指標,看看會發生什么!
上代碼:
#include <stdio.h>
int main(int argc, char const *argv[])
{
int * p;
p = &p;
printf("p=%p\n&p=%p\n*p=%p",p,&p,*p);
return 0;
}
編譯結果:
p=0xfff75af4
&p=0xfff75afc
*p=0x0
哦豁,結果清晰可見,我們也因此產生了一些疑惑,為什么會出現兩個記憶體地址呢?
來看看昨晚我與大佬的對話吧~~

但是當時還是很疑惑為什么會出現了兩個記憶體地址~~
第二天找到了原因!
昨天是使用手機敲得C代碼,因為那時候還在火車上,沒法拿電腦
今天使用了電腦,編譯器是gcc,編輯器是vs code
重新編譯了一下
運行結果如下:
p=0061FECC
&p=0061FECC
*p=0061FECC
欸,這就很舒服了,記憶體地址是一樣的,所以雖然安卓有C語言的編譯器,但還是使用電腦吧,
1.5 二級指標
今天使用了電腦,但是我們的代碼卻是不妥的,因為這里涉及到了一個二級指標
我們也收到了一條警告
assignment to 'int *' from incompatible pointer type 'int **'
翻譯:從不兼容的指標型別'int **'賦值給'int *'
參考大佬的話,p是一級指標,&p是二級指標,那么問題來了,什么是一級指標,什么是二級指標
一級指標就是普通變數的指標
二級指標就是指標變數的指標,也就是指標的指標
就像這樣:
#include <stdio.h>
int main(int argc, char const *argv[])
{
int test = 10; // 普通變數
int * p = &test; // 指向普通變數的指標,也就是一級指標
int ** pp = &p; // 指向指標的指標,也就是二級變數
printf("test的記憶體地址是%p\np的記憶體地址是%p\npp的記憶體地址是%p", &test, &p, &pp);
return 0;
}
運行結果:
test的記憶體地址是0061FECC
p的記憶體地址是0061FEC8
pp的記憶體地址是0061FEC4
你就可以這么理解:幾級指標,就嵌套了幾個地址
1.7 指標常量
他和常量指標長得很像,但是他倆卻差了很多,首先,指標常量是指標常量,而常量指標是常量指標,
與指標變數相區別,就和常量與變數的區別一樣,常量是不可改變的,指標常量也是不可改變的,但是指標常量指向的普通變數卻不是不可改變的,
也就是說,指標常量是一個常量,而我們在定義普通常量時通常是使用的const或者#define,而定義指標常量是使用
#include <stdio.h>
int main(int argc, char const *argv[])
{
/* code */
int num = 10; // 普通變數
int *const p = # // 指標常量
// 錯誤使用
int num2 = 11;
p = &num2; // 因為是常量,無法再進行賦值
return 0;
}
這個指標是常量,無法再進行賦值運算,
1.8 常量指標
這是一個指標,只不過他指向的是一個常量,
我們無法通過指標操作常量,但是可以對指標重新賦值,
#include <stdio.h>
int main(int argc, char const *argv[])
{
const int num = 10; // 常量
const int *p = # // 常量指標
// 錯誤使用
*p = 20;
// 正確使用
int num2 = 20;
p = &num2;
return 0;
}
2. 陣列指標/指標陣列
陣列指標是:
指向陣列的指標,它本質上還是一個指標,類比普通指標
指標陣列是:
一個存放指標的陣列,本質上是陣列,就如經常說的字符陣列,整型陣列一樣
2.1 陣列的理解
陣列本質上只是編譯器在記憶體空間上開辟的一連串的記憶體
而代表陣列的變數其實只是這一連串記憶體空間的第一個元素的記憶體地址,
所以當你給編譯器看一個陣列時,他并不是像人一樣能看到這個陣列的全貌,他只能看到這個陣列的第一個元素,并且知道這個元素的記憶體地址
看看這串代碼:
#include <stdio.h>
int main(int argc, char const *argv[])
{
int a[] = {1, 2, 3, 4}; // 一個陣列
printf("a的記憶體地址%p\na[0]的記憶體地址%p", &a, &a[0]);
return 0;
}
他的運行結果為:
a的記憶體地址0061FEC0
a[0]的記憶體地址0061FEC0
相信你對陣列有了更深的了解,
2.2 陣列強制型別和下標
那么為什么定義陣列需要強制型別呢?
拿int型別來說,int型別占用4個位元組
在人們眼中的元素位置的+1
相當于編譯器眼里的+4(4是型別占用的位元組數)
所以才能精準的拿到某個元素
陣列下標是怎么定義的呢?為什么下標從0開始
陣列的下標也是這么來的,通過對記憶體地址的相加減來獲取
因為編譯器只記得陣列第一個元素的記憶體地址
而下標就是讓第一個元素的記憶體+i(i是下標)
通過下標獲取元素的程序可以類比為:
- arr[1] => *(&arr +1)
先讓記憶體地址加下標,再通過指標獲取到元素
2.3 陣列指標
陣列指標就是指向陣列第一個元素的指標,相信認真看了2.1和2.2的你能夠很快理解
定義一個陣列指標
int a[] = {1, 3, 5, 7}; // 一個陣列
int (*p)[4] = &a; // 定義一個指標,指向陣列的頭元素
通過指標訪問第二個陣列元素:
printf("訪問陣列的第二個元素:%d", *(*p+1));
完整代碼:
#include <stdio.h>
int main(int argc, char const *argv[])
{
int a[] = {1, 3, 5, 7}; // 一個陣列
int (*p)[4] = &a; // 定義一個指標,指向陣列的頭元素
printf("a的記憶體地址%p\na[0]的記憶體地址%p\n", &a, &a[0]);
printf("訪問陣列的第二個元素:%d", *(*p+1));
return 0;
}
運行結果:
a的記憶體地址0061FEBC
a[0]的記憶體地址0061FEBC
訪問陣列的第二個元素:3
2.4 指標陣列
指標陣列,顧名思義,他是個陣列,就如經常說的字符陣列,整型陣列一樣,只不過指標陣列的定義方法和存盤物件也有億點點不一樣,
定義一個指標陣列(以整型為例)
int *pArr[10]; // 定義一個指標陣列
要注意與陣列指標的定義區別開
陣列指標的定義:
int (*arrP)[10];
一定要注意這個括號,這涉及到了*符號的運算優先級,一但寫錯,就是不同的兩個東西了,
簡單使用:
#include <stdio.h>
int main(int argc, char const *argv[])
{
int *arr[10]; // 定義一個指標陣列
int arrSize = 10; // 指標陣列的長度
for (int i = 0; i < arrSize; i++)
{
arr[i] = &i; // 將臨時地址放在指標陣列里
printf("陣列的元素:%p\n陣列元素所指向的元素%d\n", *arr[i]);
/* code */
}
/* code */
return 0;
}
輸出結果:
陣列的元素:0061FEA0
陣列元素所指向的元素0
陣列的元素:0061FEA0
陣列元素所指向的元素1
陣列的元素:0061FEA0
陣列元素所指向的元素2
陣列的元素:0061FEA0
陣列元素所指向的元素3
陣列的元素:0061FEA0
陣列元素所指向的元素4
陣列的元素:0061FEA0
陣列元素所指向的元素5
陣列的元素:0061FEA0
陣列元素所指向的元素6
陣列的元素:0061FEA0
陣列元素所指向的元素7
陣列的元素:0061FEA0
陣列元素所指向的元素8
陣列的元素:0061FEA0
陣列元素所指向的元素9
因為i是臨時變數,所以在每次回圈之后都會銷毀,下次使用再次開辟,所以記憶體地址是一樣的,
3. 函式指標
在我們定義函式的時候,編譯器也會在記憶體空間給函式開辟一個記憶體,而該記憶體的首地址就是函式的記憶體地址,而函式指標就是指向該記憶體地址的,
3.1 函式
眾所周知,C語言是面向程序的語言,或者稱函式式編程,
而在C語言中,函式也確實起了很大的作用,在C語言的學習中,你見過最多的可能就是main函式,同時也是你第一個見得函式,
我們來看看這個main函式
int main(){return 0;}
我們把他濃縮成一行,比較好瞅
- int是回傳型別,每個函式都要有這個,不回傳東西的函式的回傳值型別為void
- main是函式名,固定的,無法多載
- 括號里面是引數串列,一般是默認沒有,也可以傳遞
void或者int argc, char const *argv[]- {}大括號里面是函式的具體實作代碼,比如說
printf("Hello World!");- return 是函式結束的關鍵字,回傳值為0表示程式正確運行,為其他表示有其他例外
切記main函式不要
void main(){},這個真的很重要
3.2 指向函式的指標
見名知意,這個東西也是一個指標,只不過他指向的是一個函式,準確來說是函式在記憶體空間中開辟空間的頭地址,
定義也是有億點點麻煩,不過卻也不是不好理解,
定義:
int (*funP)(int num1, int num2); // 定義一個函式,有兩個整型引數
因為運算子優先級的存在,所以我們需要對變數名與*進行首先運算
使用:
#include <stdio.h>
/*
定義一個兩數求和函式
回傳兩個數的和的結果
*/
int sum(int num1, int num2)
{
int ans = num1 + num2;
return ans;
}
int main(int argc, char const *argv[])
{
int (*funP)(int num1, int num2); // 定義一個函式,有兩個整型引數
funP = sum; // 將函式sum的地址給funP
int ans = funP(1, 2); // 使用指標使用函式
printf("%d", ans);
return 0;
}
粉絲福利:
添加下方博主微信,領取指標思維導圖,C語言思維導圖
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/303098.html
標籤:其他
上一篇:[八大排序]0基礎C語言實作八大排序,詳解快排,歸并,希爾
下一篇:詳解scanf()輸入的一些問題
