【C語言進階技巧】指針掌握之道:解密指針的奇妙世界(第一部))

??博客主頁: 小鎮(zhèn)敲碼人
?? 歡迎關(guān)注:??點贊 ????留言 ??收藏
??回來4天了,加油?。。??????
??當你的能力匹配不上你的夢想,當你需要實現(xiàn)的目標匹配不上你的圈子的時候,你就會出現(xiàn)錯位。當我們的能力還匹配不上我們的夢想時,我們就需要沉淀學習。??????
1. 字符指針
字符指針,就是儲存字符變量地址的指針,這是一種指針的類型。
- 通常我們字符指針有如下兩種用途
- 通過字符指針間接改變字符的值
int main()
{
char ch = 'c';
// ch = 'a';//直接通過變量賦值更改ch的值
char *p = &ch;//將字符ch的地址存入p中
//*p = 'a';//通過對p里面的地址解引用找到ch,間接的改變ch的值
}
- 借助字符指針對字符串進行儲存和打印
int main()
{
const char* ptr = "abcdef";
printf("%s\n",ptr);
return 0;
}
- const修飾字符串表示
"abcdef"
不可修改,為常量字符串 -
字符指針變量
ptr
只儲存了字符'a'
的地址。 (可類比字符數(shù)組理解,實際上字符串是字符數(shù)組的一種形式,在C語言中,字符串是由字符數(shù)組表示的,以空字符(‘\0’)作為字符串的結(jié)束標志,又因為數(shù)組名表示首元素地址,所以字符指針也只儲存了首元素的地址)
2. 指針數(shù)組
??????指針數(shù)組是由指針組成的數(shù)組。在C和C++等編程語言中,指針是一個變量,用于存儲內(nèi)存地址。指針數(shù)組是一個數(shù)組,其每個元素都是指針類型。
????int *arr1[10];
????整形指針數(shù)組
????char *arr2[10];
??一級字符指針的數(shù)組
????char **arr3[10];
?二級字符指針的數(shù)組
2.1 整形指針數(shù)組
int main()
{
int a = 0;
int b = 1;
int c = 2;
int* arr[] = { &a,&b,&c };
for (int i = 0; i < 3; i++)
{
printf("%d ", *arr[i]);
}
}
2.2 用指針數(shù)組模擬二維數(shù)組
int main()
{
int arr1[] = { 1,2,3,4,5 };//arr1-int*
int arr2[] = { 2,3,4,5,6 };//arr2-int*
int arr3[] = { 3,4,5,6,7 };//arr3-int*
//指針數(shù)組
int* p[] = { arr1,arr2,arr3 };
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 5; j++)
{
//這兩種方式打印都可以,兩者擇一
printf("%d ", *(*(p + i) + j));
printf("%d ",p[i][j]);
}
printf("\n");
}
}
3. 數(shù)組指針
我們可以通過類比來理解
整型指針:
int * a;
能夠指向整形數(shù)據(jù)的指針
浮點型指針:float* b;
能夠指向浮點型數(shù)據(jù)的指針。
那么數(shù)組指針應該是:能夠指向數(shù)組的指針。
3.1 數(shù)組指針的表示方法
數(shù)組的類型表示是什么?通過簡單的類比可以知道。
int a = 3;
類型為除變量名以外的部分int
就是整形的類型表示形式。char b = 'a';
類型為除變量名以外的部分char
就是字符類型的的表示形式。int arr[10] = {0};
類型為除變量名以外的部分int [10]
就是這個數(shù)組的變量類型表示形式。- 而數(shù)組指針的類型是指針,指針的類型是數(shù)組,通過前面的學習我們知道,變量類型+變量名為一個變量,所以一個數(shù)組指針變量就是,指向數(shù)組的指針變量,
int [10] *p = &arr;
,看起來似乎是這樣,但是通過下面符號的優(yōu)先級我們可以知道[]
的優(yōu)先級要比*
號高,所以我們要給*p
加上括號,讓其成為指針,int [10] (*p) = &arr
,那這樣是否正確呢?還是不對,放進vs編譯器會報錯,數(shù)組的類型比較特殊,應該是這樣int (*p) [10] = &arr;
才正確。
3.2 深度剖析&數(shù)組名和數(shù)組名
對于下面的數(shù)組
int arr[10];
-
arr
與&arr
分別是什么呢?
對于arr,我們知道它是一個數(shù)組名,數(shù)組名表示首元素的地址,
那&arr是什么呢,我們來看下面一段代碼:
int main()
{
int arr[10] = { 0 };
printf("arr = %p\n", arr);
printf("arr+1 = %p\n", arr+1);
printf("&arr[0] = %p\n", &arr[0]);
printf("&arr[0]+1 = %p\n", &arr[0]+1);
printf("&arr = %p\n", &arr);
printf("&arr+1 = %p\n", &arr+1);
return 0;
}
運行結(jié)果如下:
可以看到數(shù)組名和&數(shù)組名打印的地址和首元素打印的地址是一樣的,這是不是意味著它們是一樣的呢?很明顯通過代碼我們也可以發(fā)現(xiàn),它們并不是相同的,因為&arr+1
打印的地址與arr+1
打印的地址相差了40個字節(jié),而arr+1
打印的地址和首元素&arr+1
的仍然一樣,進一步說明,arr就表示首元素地址。
- 實際上:&arr表示的是整個數(shù)組的地址,它的類型是int (*)[10],是一種數(shù)組指針類型。
???????????? ??而arr代表的是數(shù)組首元素的地址,它的類型是int *。
???????????????數(shù)組的地址加1,跳過整個數(shù)組的大小,所以&arr+1與&arr的差值就是 10 ? 4 = 40 10*4 =40 10?4=40個字節(jié)。
3.3 數(shù)組指針的使用
3.3.1 在同一函數(shù)內(nèi)直接將數(shù)組的地址賦給數(shù)組指針
#include<stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//int[10] *p = &arr;err
int (*p)[10] = &arr;
for (int i = 0; i < 10; i++)
{
//兩種打印方式擇一
printf("%d ",*((*p)+i));
printf("%d ", (*p)[i]);
}
//int* p2 = &arr;//err
return 0;
}
- 可以看到,這樣做簡直就是多此一舉,因為想打印一維數(shù)組,我們直接打印就行了,使用數(shù)組指針,反倒是比較麻煩。
3.3.2 數(shù)組指針在二維數(shù)組傳參上的應用
3.3.2.1 二維數(shù)組傳參使用二維數(shù)組
#include<stdio.h>
void Print(int arr[][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("\n");
}
}
int main()
{
int arr[3][5] = { 1,2,3,4,5,2,3,4,5,6,3,4,5,6,7 };
Print(arr, 3, 5);
return 0;
}
3.3.2.2 二維數(shù)組傳參使用數(shù)組指針
#include<stdio.h>
Print(int(*p)[5], int r, int c)
{
for (int i = 0; i < r; i++)
{
for (int j = 0; j < c; j++)
{
//兩種打印方式擇一
printf("%d ", *(*(p + i) + j));
printf("%d ", p[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 };
//arr表示數(shù)組名,數(shù)組名表示首元素的地址,也就是那一行的地址
Print(arr, 3, 5);
return 0;
}
- 因為二維數(shù)組的數(shù)組名代表首元素的地址,首元素地址就是那一行的地址,就等價于一維數(shù)組的地址(&一維數(shù)組數(shù)組名),故又稱二維數(shù)組為一維數(shù)組的數(shù)組,所以可以用數(shù)組指針來接收。
4. 數(shù)組傳參和指針傳參
4.1 一維數(shù)組傳參
- 有以下主要兩種方式
- 傳遞數(shù)組名,用一級指針的方式去接收,通過地址訪問
- 傳遞數(shù)組的副本
#include<stdio.h>
void test1(int arr[])
{}
void test1(int arr[10])
{}
void test1(int* arr)
{}
void test2(int* arr[20])
{}
void test2(int** arr)
{}
int main()
{
int arr1[10] = { 0 };
int* arr2[20] = { 20 };
test1(arr1);
test2(arr2);
return 0;
}
4.2 二維數(shù)組傳參
- 正確的方法已經(jīng)在“3.3.2 數(shù)組指針在二維數(shù)組傳參上的應用“版塊給出,下面就幾種錯誤的傳參方法做一下闡述。
#include<stdio.h>
void test(int arr[3][5]) ?
{}
void test(int arr[][])//err,二維數(shù)組傳參,如果是傳遞數(shù)組的副本,需要給出一行有多少數(shù)字,因為二維數(shù)組其實是一維數(shù)組的擴展,所以需要提供一行有多少元素,來正確進行內(nèi)存訪問
{}
void test (int arr[][5]) ?
{}
void test(int *arr)//err,傳的是一行的地址,指針的類型是數(shù)組,int(*)[5]。
{}
void test(int *arr[5])//err,傳的是第一行的地址,用指針數(shù)組接收不對,因為傳的是一行的地址。
{}
void test(int (*arr)[5]) ?
{}
void test(int **arr)//err,傳的是數(shù)組的地址(第一行的地址),不是一級指針的地址,不能用二級指針接收。
{}
int main()
{
int arr[3][5] = { 0 };
test(&arr);
return 0;
}
- 注意:傳二維數(shù)組的副本必須給定一行有多少個數(shù),在C語言中,二維數(shù)組在內(nèi)存中以連續(xù)的塊存儲,可以看作是一維數(shù)組的擴展。當傳遞二維數(shù)組作為參數(shù)時,需要提供一行有多少個元素的信息,以便在函數(shù)內(nèi)部正確地進行內(nèi)存訪問。
二維數(shù)組在內(nèi)存中按行主序(row-major order)存儲。也就是說,二維數(shù)組的每一行依次存儲在內(nèi)存中,并且相鄰的元素在內(nèi)存中也是相鄰的。通過提供一行有多少個元素的信息,可以根據(jù)內(nèi)存布局準確地計算出每個元素的地址,從而進行正確的訪問。 - 二維數(shù)組不能用二級指針來接收,因為二級指針是指向一級指針的,很明顯,二維數(shù)組的數(shù)組名的類型是第一行即數(shù)組的地址應該用這個
int (*)[5]
即用數(shù)組指針接收。 - 二維數(shù)組也不能用指針數(shù)組接收,類型不匹配。
4.3 一級指針傳參
當函數(shù)形參為一級指針時,實參可以是同類型一維數(shù)組數(shù)組名、同類型變量的地址、同類型指針。
- 請看如下代碼加深理解:
#incldue<stdio.h>
void print(int* p, int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%d ", *(p + i));
printf("%d ", p[i]);
}
}
int main()
{
int arr[5] = { 0,1,2,3,4 };
int sz = sizeof(arr) / sizeof(arr[0]);
print(arr, sz);
}
#include<stdio.h>
void test(char *p)
{}
int main()
{
char ch = 'a';
char *str = "abcdef";
char* ptr = &ch;
test(str);
test(&ch);
test(ptr);
return 0;
}
4.4 二級指針傳參
當函數(shù)形參為二級指針時,實參可以是一級指針的地址、二級指針。
- 請看下面代碼,加深理解:
#incldue<stdio.h>
void test(int **ptr)
{
printf("num = %d\n",**ptr);
}
int main()
{
int a = 3;
int* p = &a;
int **pp = &p;
test(pp);
test(&p);
return 0;
}
5. 函數(shù)指針
函數(shù)指針和數(shù)組指針、字符指針一樣都是指針,只不過指針所指向的對象的類型不同,其中函數(shù)指針與數(shù)組指針最為相似。
5.1 函數(shù)的地址
我們先看下面代碼,函數(shù)名的地址,類比數(shù)組的地址
int Add(int a, int b)
{
return a + b;
}
int main()
{
printf("%p\n", &Add);
printf("%p\n", Add);
return 0;
}
運行的結(jié)果:
????????可以看見輸出的是一段地址,這兩個地址都是函數(shù)Add的地址。既然函數(shù)的地址我們知道了,就可以用指針去保存它,我們只需要知道指針指向的對象的類型就行了。
5.2 函數(shù)指針的表示方法
- *號代表變量是一個指針變量,函數(shù)指針所指向的類型就是函數(shù)聲明去掉變量名和分號后保留的部分,包括返回值和參數(shù)的類型,與數(shù)組指針相似,函數(shù)指針的(*變量名)也要放在函數(shù)名的位置,而數(shù)組指針是放在數(shù)組名的位置。
請看如下代碼:
void test()
{
printf("i love programming!\n");
}
//除函數(shù)名以外的部分,就是的類型
void (*pf)();//函數(shù)指針,指向一個返回值為void,無參數(shù)的函數(shù)。
void* pf();//表示返回值類型為void*,無參數(shù)的函數(shù)。。
#incldue<stdio.h>
int Add(int a, int b)
{
return a + b;
}
int main()
{
int arr[10] = { 0 };
int(*p)[10] = &arr;//數(shù)組指針
//int (*)[10]是數(shù)組指針類型
int(*pf) (int, int) = &Add;
//int (*)(int,int)是函數(shù)指針類型
return 0;
}
再給出一組代碼幫助理解:
#include<stdio.h>
int Add(int x, int y)
{
return x + y;
}
int main()
{
//&Add和Add都可以
int (*pf)(int, int) = &Add;
int (*pf)(int, int) = Add;
int r = Add(3, 5);
printf("%d\n", r);
int m = pf(3, 5);
printf("%d\n", m);
return 0;
}
運行結(jié)果如下:
- 函數(shù)指針與數(shù)組指針有相似性,可以對比著去理解。
5.3 兩行有趣的代碼
5.3.1 將0強制轉(zhuǎn)換為函數(shù)指針類型然后調(diào)用
void (*p)() -p是函數(shù)指針
void (*)() -是函數(shù)指針類型
(* ( ( void(*)() )0 ) )();
//(((void(*)())0)是將0強制轉(zhuǎn)換為函數(shù)至真
//(*(函數(shù)指針))是將函數(shù)指針解引用
//然后不傳參數(shù)調(diào)用(*(函數(shù)指針))();
5.3.2 數(shù)組指針、函數(shù)指針等特殊變量類型typedef的使用
#include<stdio.h>
typedef int* ptr_t;//一級指針別名
typedef unsigned int uint;//無符號整形的別名
typedef int(*parr_t)[10];//數(shù)組指針的別名
typedef int (*pf_t)(int, int);//函數(shù)指針的別名
int main()
{
ptr_t p1;
uint u2;
parr_t p2;
pf_t p3;
}
- 函數(shù)指針與數(shù)組指針別名的創(chuàng)建與其變量的創(chuàng)建類似,可類比理解。
5.3.3 函數(shù)指針類型的函數(shù)的聲明,并使用別名typedef化簡
請看如下代碼:
void(* signal ( int, void(*)(int) ) )(int);
解析:
??????????我們可以觀察到signal
函數(shù)的返回類型和其中一個參數(shù)是相同的都是函數(shù)指針類型,下面我們利用別名typedef
來使這段代碼看起來更加易懂:
typedef void (*pf_t)(int);
pf_t signal(int,pf_t);
??????????CSDN上的別名沒有高亮,但VS2019里面是有高亮的,定義函數(shù)后,你會發(fā)現(xiàn)編譯器是不會報錯,說明這樣是可行的。文章來源:http://www.zghlxwxcb.cn/news/detail-535089.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-535089.html
到了這里,關(guān)于【C的葵花寶典進階篇】之指針進階(一)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!