引言
? ? ? ? 思緒很久,還是決定寫一寫指針,指針這塊內容很多,也不是那么容易說清楚,這里盡可能寫地詳細,讓大家理解指針。(未完序)
一、內存和地址
? ? ? ? 在講指針前,需要有一個對內存和地址的認識,不然后面的指針不是那么容易理解。在我們的內存中:一個字節里存儲著 0 或 1 的信息。那計算機是怎么快速找到對應的信息的呢?
? ? ? ? 回答:在內存中每個字節都有自己對應的地址,計算機通過找到地址,就能訪問地址里面的信息。
? ? ? ? 那么指針,就是一種可以存儲地址的數據類型。
二、一級指針
1.存地址:?
? ? ? ? ?重點:其實指針是很簡單的,只要會使用int ,float, double,long long 等數據類型,那么指針變量,你也一定會使用。
? ? ? ? ?先介紹一下指針變量是怎么創建的:
????????在對應數據類型的后面加上*,就可以存儲該類型的內存地址。
認識個東西 : & 取地址操作符,看下面的代碼:
int b = 10;int* a = &b;
????????這段代碼創建了一個變量b,&b,就可以取出b的地址。
int* 就是int類型的指針變量,a 里面存的就是? b 的地址
通過調試來看一下:
?
可以看到 b 的地址?
下面通過監視窗口,看一下a里面存的是什么:
發現就是b的地址。
類似地,如果要存float數據類型的地址,就要創建float類型的指針變量:
float c = 1.2;float* d = &c;
d中存儲的就是 c 的地址。
其他的數據類型一樣,包括自定義類型的數據。
可以自己多試試。
2.解讀地址對應的內容:?
要用到一個東西:解引用操作符:*
使用起來也特別簡單:
比如上面代碼中,a中存儲的是b的地址,*a,就可以找到b對應位置的內容了
來看代碼:?
#include<stdio.h>int main() {int b = 10;int* a = &b;int c = *a; //*a 等價于 bprintf("%d\n", c);*a = 20; //改變a地址里面的內容,就是把b的內容給改了printf("%d\n", b);return 0; }
運行結果:
????????是不是特別簡單,認為指針難,是因為你不理解每個符號的內容,這里給拆開來講,相信你一定明白了
3.指針變量的大小
int在內存中占4個字節,float在內存中占4個字節,double在內存中占8個字節,和int,float,double等類型一樣,指針類型在內存中也是占有字節的。
????????那指針類型在內存中占多少個字節呢?
先給出結論,下面來看代碼證明。
? 32位平臺下地址是32個bit位,指針變量大小是4個字節
? 64位平臺下地址是64個bit位,指針變量大小是8個字節
? 注意指針變量的大小和類型是無關的,只要指針類型的變量,在相同的平臺下,大小都是相同的。(和CPU里面的線路有一定的關系)
sizeof操作符同樣可以返回指針類型在內存中占多少個字節。?
#include<stdio.h>
int main()
{printf("%zd\n", sizeof(char*));printf("%zd\n", sizeof(short*));printf("%zd\n", sizeof(int*));printf("%zd\n", sizeof(double*));return 0;
}
?在不同平臺下運行這段代碼:
在32位平臺下:
運行結果:?
在64位平臺下:?
?運行結果:
?三、指針變量類型的意義
你是不是會有這么個疑問:
????????指針變量的大小和類型無關,只要是指針變量,在同一個平臺下,大小都是一樣的,為什么還要有各種各樣的指針類型呢?
其實指針類型是有特殊意義的,通過兩中方法來理解一下。
1.指針的解引用
下面看兩段代碼:?
//代碼1 #include <stdio.h> int main() {int n = 0x11223344;int* pi = &n;*pi = 0;return 0; }
//代碼2 #include <stdio.h> int main() {int n = 0x11223344;char* pc = (char*)&n;*pc = 0;return 0; }
代碼二中給int*類型,強制轉換成了char*類型。最后都解引用后賦值0
通過調試,來看一下兩段代碼在內存中的存儲。
代碼一:
代碼二:
????????相信聰明的你一定發現了不同,代碼1會將n的4個字節全部改為0,但是代碼2只是將n的第一個字節改為0。
得出結論:指針的類型決定了,對指針解引用的時候有多大的權限(一次能操作幾個字節)。 比如: char* 的指針解引用就只能訪問?個字節,而 int* 的指針的解引用就能訪問四個字節。?
?2.指針+ -整數
有了上面的結論,這個就很容易理解了
來看代碼:
#include <stdio.h>
int main()
{int n = 10;char* pc = (char*)&n;int* pi = &n;printf("%p\n", &n);printf("%p\n", pc);printf("%p\n", pc + 1);printf("%p\n", pi);printf("%p\n", pi + 1);return 0;
}
來看運行結果:?
????????char* 類型的指針變量+1跳過1個字節, int* 類型的指針變量+1跳過了4個字節。 這就是指針變量的類型差異帶來的變化。指針+1,其實跳過1個指針指向的元素。指針可以+1,也可以-1。?
結論:指針的類型決定了指針向前或者向后走一步有多大(距離)。
四、void* 指針?
????????在指針類型中有?種特殊的類型是 void * 類型的,可以理解為無具體類型的指針(或者叫泛型指針),這種類型的指針可以用來接受任意類型地址。但是也有局限性, void* 類型的指針不能直接進行指針的+-整數和解引用的運算。
?來看代碼:
#include <stdio.h>
int main()
{int a = 10;int* pa = &a;char* pc = &a;return 0;
}
這段代碼在編譯的時候肯定是會報警告的。(因為類型不兼容)
#include <stdio.h>
int main()
{int a = 10;void* pa = &a;void* pc = &a;//*pa = 10; 這樣寫是錯誤的//*pc = 0;return 0;
}
void* 類型的指針可以接收不同類型的地址,但是無法直接進行指針運算。
????????一般 void* 類型的指針是使用在函數參數的部分,用來接收不同類型數據的地址,這樣的設計可以實現泛型編程的效果。
五、指針運算?
1.指針+-整數
? ? ? ? 上面的一個代碼已經可以看出這個功能了,這里再通過一個案例來理解一下(也算是一個小的練習)
????????
通過地址來訪問一個數組。
#include<stdio.h>
int main()
{int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };int* p = &arr[0]; //取出首元素的地址int i = 0;int sz = sizeof(arr)/sizeof(arr[0]);for (int i = 0; i < sz; i++){printf("%d ", *(p + i));}return 0;
}
運行結果:
?從這個案例中可以看出:*(p + i) 等價于 p[i]。(其實就是這樣)
這也可以說明數組名是就是數組首元素的地址。
不過有來個特例需要記一下:
1. &arr,對數組名取地址,得到的是整個數組的地址,而不是首元素的地址。
2. sizeof(arr),這里面的arr也是整個數組的地址,而不是首元素的地址。
把上面代碼改一下來證明一下:
#include<stdio.h>
int main()
{int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };int* p = &arr[0]; //取出首元素的地址int i = 0;int sz = sizeof(arr)/sizeof(arr[0]);for (int i = 0; i < sz; i++){//printf("%d ", *(p + i));printf("%d ", p[i]);}return 0;
}
只改了一個地方,就是輸出位置。
運行結果:
是不是又增加了新知識,嘿嘿(●ˇ?ˇ●)?
?2.指針-指針
? ? ? ? 后面位置的指針減前面位置的指針,可以計算出兩個指針之間字節個數。
來看參考代碼:
#include <stdio.h>
int my_strlen(char* s)
{char* p = s;while (*p != '\0')p++;return p - s;
}
int main()
{printf("%d\n", my_strlen("abc"));return 0;
}
運行結果:?
3.指針的關系運算
指針之間也是可以比較大小的
來看代碼:
#include <stdio.h>
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int* p = &arr[0];int sz = sizeof(arr) / sizeof(arr[0]);while (p < arr + sz) //指針的???較{printf("%d ", *p);p++;}return 0;
}
運行結果:?
是不是又被震驚到了,哇嗚,竟然還可以這么寫。