摘要:本章節在此基礎上,對語言階段指針進行更深層次的研究。數組指針的類型由數組類型決定,先找出數組的類型去掉名就是類型。相當于數組指針所指向數組的數組名。數組指針指向整個數組,將其看作二維數組并解引用得到一行的首元素,從而遍歷訪問。
我們在初階時就已經接觸過指針,了解了指針的相關內容,有:
- 指針定義:指針變量,用于存放地址。地址唯一對應一塊內存空間。
- 指針大小:固定32位平臺下占4個字節,64位8個字節。
- 指針類型:類型決定指針±整數的步長及指針解引用時訪問的大小。
- 指針運算:指針解引用,指針±整數,指針-指針,指針關系運算。
本章節在此基礎上,對C語言階段指針進行更深層次的研究。
字符指針,存入字符的地址,類型為char *
char ch = "w";const char* pch = &ch;
這種很容易理解,就是指針解引用訪問字符變量。
char* pc = "hello";printf("%s/n", pc);
這種是把字符串
"hello"
放進指針嘛?其實不然,類似于數組名,該指針存的是常量字符串
"hello"
的首字符的地址。通過對指針解引用訪問首字符地址,從而找到整個字符串。
char* pc = "hello";printf("%c/n", *(pc + 1));//eprintf("%s/n", pc);//helloprintf("%s/n", pc + 1);//ello
字符串本質上還是在空間上連續存放,所以指針±整數同樣有訪問的效果。由此也可以看出
%s
的用法,把地址給%s
會將其后的內容看作字符串并打印直到/0
。(所以我猜測%s的s是string的意思)
例題
char str1[] = "hello bit";char str2[] = "hello bit";char* str3 = "hello bit";char* str4 = "hello bit";if (str1 == str2) printf("str1 = str2/n");//1else printf("str1 != str2/n");//2if (str3 == str4) printf("str3 = str4/n");//3else printf("str3 != str4/n");//4
str1(3)==str2(4)
,比較的是二者其實位置地址是否相同。(地址才是真正判斷二者是否相同的要素)
答案是2和3。因為1和2是用字符串初始化數組,3和4是指針指向常量字符串。
- str1和str2是普通的數組,是在內存上開辟了兩塊空間不過存放了一樣的數據。
- str3和str4指向常量字符串,存放在內存的常量區,是不可被修改且具有唯一性即常量區只存放一個。所以str3和str4指向的都是同一個字符串。
常量區的存儲特點:存放在常量區的數據不可被修改,正因為不可修改所以存一份就夠了。后期如果需要,使用的是同一數據。(數據還是同一個數據,只是用不同的指針維護)
總結
int arr[10];//整型數組char ch[5];//字符數組float f[20];//浮點型數組
可見,元素類型也就是數組的“類型”。
char* pch[5];int* parr[10];float* pf[20];
指針數組就是存放指針的數組。
int arr[10];int* arr[10];
整型數組的數組名arr
,即首元素地址,是一級指針。
指針數組的數組名parr
,也是首元素地址,不過其首元素為int*
類型變量,所以parr
就是二級指針。
int arr1[] = { 1,2,3,4,5 };int arr2[] = { 2,3,4,5,6 };int arr3[] = { 3,4,5,6,7 };int* parr[] = { arr1,arr2,arr3 };for (int i = 0; i < 3; i++) { for (int j = 0; j < 5; j++) { //1. printf("%d ", parr[i][j]); //2. printf("%d ", *(*(parr + i) + j)); } printf("/n");}//答案1 2 3 4 52 3 4 5 63 4 5 6 7ps:parr[i] <==> *(parr+i) *(parr[i]+j) <==> *(*(parr+i)+j) <==> (*parr+i)[j] <==> parr[i][j]
通過指針數組訪問整型數組的每個元素。
parr[i][j]
和*(*(parr+i)+j)
本質上是等價的。
const char* pch[] = { "abcde", "bcdef", "cdefg" };for (int i = 0; i < 3; i++) { //1. printf("%s", pch[i]); //2. printf("%s", *(pch + i)); for (int j = 0; j < 5; j++) { //3. printf("%c", pch[i][j]); //4. printf("%c", *(*(pch + i) + j)); } printf("/n");}
打印字符串使用
%s
更簡單,若要使用%c
,就是得到每個字符串的起始地址,分別向后訪問。
從這里也可以看出數組和指針的關系,我愿稱之為*
和[]
的愛恨情仇!
?
由前面的例子,不難得出,數組指針是指向數組的指針,是指針而非數組。
char ch = "w";char* pch = &ch;//字符地址存放在字符指針中int a = 10;int* pint = &a;//整型地址存放在整型指針中float f = 0.0;float* pf = &f;//浮點型地址存放在浮點型指針中
什么變量的地址存放在什么指針中。指針指向變量的類型,決定了指針的類型。顧名思義,數組指針指向的是數組。
遞推可得,數組的地址存放在數組指針中。且數組指針的類型為數組的類型再加個*
。
下面那種定義方法是對的呢?
int arr[10] = { 0 };//1.int* pa = arr;//2.&arr;//整個數組的地址int* parr = &arr;//3.int* parr[10] = &arr;//4.int(*parr)[10] = &arr;
- 取出的是首元素的地址,而非整個數組的地址
- 整型指針應存放整型變量的地址,數組的地址無法存入整型指針中。
[]
的優先級比*
高,故parr
先與[]
結合成數組名,所以parr
是個指針數組。
數組指針的類型由數組類型決定,先找出數組的類型int[10]
(去掉名就是類型)。且不能讓[]
先與parr
結合,所以用()
先將parr
和*
結合,即成int(*parr)[10]
。
C語言規定
[]
必須再最后面,所以不可寫成int[10](*parr)
。
int* parr[10];//指針數組int(*parr)[10];//數組指針
我們前面強調過,去掉名字就是類型。所以int[10]
是整型數組的類型,int*[10]
是指針數組的類型,int(*)[10]
是數組指針的類型。
之前介紹過不止一遍,所以這次只說重點。
指針類型決定了指針±整數的步長。
//首元素地址+1printf("%p/n", arr);//0073FCA4printf("%p/n", arr + 1);//0073FCA8//整個數組地址+1printf("%p/n", &arr);//0073FCA4printf("%p/n", &arr + 1);//0073FCCC
int(*)[10]
型指針+1,向后訪問了 i n t × 10 int×10 int×10即40個字節。
sizeof(arr)
也代表整個數組,現在去理解為什么sizeof
里數組名代表的是整個數組呢?數組這種結構保存了數組的大小,sizeof
求所占空間的長度,那自然要嚴謹一些了。
遍歷數組,使用數組或是指針作形參接收就行了。且所謂的用數組接收僅是理解層面,本質上都是指針。
void Print1(int arr[], int sz) { for (int i = 0; i < sz; i++) { //printf("%d ", arr[i]); printf("%d ", *(arr + i)); }}void Print2(int* arr, int sz) { for (int i = 0; i < sz; i++) { printf("%d ", arr[i]); //printf("%d ", *(arr + i)); }}int main() { int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; int sz = sizeof(arr) / sizeof(arr[0]); Print1(arr, sz); Print2(arr, sz); return 0;}
數組作實參,用數組或指針接收即可。數組指針使用對了很好用,但如果隨便用可能會很別扭。下面先介紹強行使用數組指針的用法。
//錯誤示范void Print3(int(*pa)[10], int sz) { for (int i = 0; i < sz; i++) { //printf("%d ", pa[i]); printf("%d ", *(pa + i)); }}
將整個數組地址傳過去,則用數組指針接收,然后呢,直接對
pa
解引用嗎?
結果顯然是錯誤的,從結果中也可以看出打印的是十進制下的地址,+1跳過40個字節。
這里筆者在學習的時候產生了個疑問,傳過去數組的地址,為什么解一層引用后還是地址呢?
&arr
解引用*
后相當于找到首元素的地址,可以理解為&
和*
相互抵消只剩下arr
不就是首元素的地址嘛~
void Print4(int(*pa)[10], int sz) { for (int i = 0; i < sz; i++) { printf("%d ", *(*(pa)+j)); }}
倘若我們把一維數組看作是二維數組第一行。由于二維數組在內存中是連續存放的,我們只打印二維數組的第一行,便可以避免上面的錯誤。
style=“zoom:80%;” />
*(pa)
相當于數組指針所指向數組的數組名。數組指針指向整個數組,將其看作二維數組并解引用得到一行的首元素,從而遍歷訪問。
從上面的例子也可以看出,用數組指針訪問二維數組時,效果便不錯。
//二維數組傳參,用二維數組接收void Print1(int arr[3][5], int r, int c) { for (int i = 0; i < r; i++) { for (int j = 0; j < c; j++) { //printf("%d ", arr[i][j]); printf("%d ", *(*(arr + i) + j)); } printf("/n"); }}
上面的例子,是正常二維數組傳參,二維數組接收的情況。下面我們用數組指針接收。
//二維數組傳參,用數組指針接收void Print2(int(*pa)[5], int r, int c) { for (int i = 0; i < r; i++) { for (int j = 0; j < c; j++) { //1. printf("%d ", pa[i][j]); //2. printf("%d ", *(*(pa + i) + j)); } printf("/n"); }}int main(){ int arr[3][5] = { 1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7 }; Print2(arr, 3, 5);//二維數組首元素是首行 return 0;}
int(*p)[5]
,指向首行這個“一維數組”。(傳參穿的是數組名)
- 用二維數組和數組指針接收的都是首行地址。
- 數組指針的類型
int(*)[5]
,和二維數組首元素地址的類型相同。
故可得,二維數組首元素地址和數組指針是等價的,即數組指針pa
就是數組名。
二維數組首元素為其首行,相當于一個一維數組,該一維數組的地址類型為int(*)[5]
。且實參為二維數組名,降級為指向首行的指針,所以它是數組指針,類型為int(*)[5]
。
數組指針指向二維數組,才是使用數組指針的正確示范。
下列示例分別是什么?
//1.int arr[5];//2.int *pa1[5];//3.int (*pa2)[10];//4.int (*pa3[10])[5];
*
靠左靠右無所謂,pa1
先和[]
結合為數組,剩下int*
為數組元素類型。
(*pa2)
,*
先和pa2
結合為指針,剩下int[10]
,指向的是元素個數為10的整型數組。
pa3
先和[10]
結合為數組,剩下int(*)[5]
是指向數組的指針為數組的元素。所以是個元素個數為10的數組指針數組。逆向思考,有整型數組
arr[5]
和指向該數組的類型為int(*)[5]
的數組指針,還有數組指針數組pa3[10]
用于存放該數組指針。
[]
結合為數組,只去掉數組名就是數組類型,去掉[n]
和數組名便是其元素的類型。*
結合為指針,只去掉指針名就是指針類型,去掉*
和指針名便是指向的變量的類型。?
實踐之中不免會碰到數組和指針作函數參數而如何設計形參的問題。
一維數組傳參,下列接收方式是否可行呢?
//1.void test(int arr[]) {}//2.void test(int arr[10]) {}//3.void test(int* arr) {}int main(){ int arr[10] = { 0 }; test(arr); return 0;}
數組傳參數組接收,可行但其實都會降級優化成指針,編譯器不會真正創建一個數組。
由于形參數組形同虛設,所以數組大小無意義,任意大小或無。(有歧義)
數組傳參本質就是首元素地址,首元素類型為int
,所以指針的類型為int*
。
所以可以看出
[]
和*()
是等價的。我愿稱之為*
和[]
的愛恨情仇!(‐^▽^‐)
//1.void test2(int* arr[2]){}//2.void test2(int** arr) {}int main(){ int* arr2[10] = { 0 }; test2(arr2); return 0;}
指針數組,每個元素類型為int*
,故用二級指針接收數組名。
一維數組傳參,數組和指針接收。
//1.void test(int arr[3][5]) {}//2.void test(int arr[][]){}//3.void test(int arr[][5]){}int main() { int arr[3][5] = { 0 }; test(arr)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/118796.html
摘要:故使用無具體類型,又稱通用類型,即可以接收任意類型的指針,但是無法進行指針運算解引用,整數等。求指針所占字節而不是解引用訪問權限大小。數組就是整個數組的大小,數組元素則是數組元素的大小,指針大小都為。 ...
摘要:函數的返回值為指針就按照字面意思,指針函數的定義顧名思義,指針函數即返回指針的函數。 目錄 前言指針與函數函數的返回值為指針作為函數參數的指針指針函數可以改變變量...
摘要:釋放不完全導致內存泄漏。既然把柔性數組放在動態內存管理一章,可見二者有必然的聯系。包含柔性數組的結構用進行動態內存分配,且分配的內存應大于結構大小,以滿足柔性數組的預期。使用含柔性數組的結構體,需配合以等動態內存分配函數。 ...
摘要:三文讀透指針上篇本文將繼續介紹有關函數指針的相關內容。在大型工程里,函數指針應用還是挺普遍的。首先看閱讀下面兩段有趣的代碼出自語言陷阱與缺陷看看他們是什么意思代碼代碼函數指針數組函數指針數組,即存放函數指針的數組。 ...
閱讀 1284·2021-10-11 10:57
閱讀 2057·2021-09-02 15:15
閱讀 1615·2019-08-30 15:56
閱讀 1207·2019-08-30 15:55
閱讀 1166·2019-08-30 15:44
閱讀 992·2019-08-29 12:20
閱讀 1338·2019-08-29 11:12
閱讀 1076·2019-08-28 18:29