C语言进阶:指针的进阶(2)

编辑: admin 分类: c#语言 发布时间: 2021-12-12 来源:互联网
目录
  • 数组指针
    • 数组指针的定义
    • &数组名和数组名
    • 数组指针的使用
    • 反面用例
    • 正面用例
    • Example
    • 类型辨别方法
  • 总结

    数组指针

    由前面的例子,不难得出,数组指针是指向数组的指针,是指针而非数组。

    数组指针的定义

    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]是数组指针的类型。

    &数组名和数组名

    之前介绍过不止一遍,所以这次只说重点。

    指针类型决定了指针±整数的步长。

    //首元素地址+1
    printf("%p\n", arr);//0073FCA4
    printf("%p\n", arr + 1);//0073FCA8
    //整个数组地址+1
    printf("%p\n", &arr);//0073FCA4
    printf("%p\n", &arr + 1);//0073FCCC
    

    1.首元素地址就是整型指针+1,自然只能向后访问4shou个字节

    2.整个数组地址+1,即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;
    }
    
    • 把二维数组想象成一个拥有三个元素的一维数组(每个元素也为一维数组),即一维数组的一维数组。
    • 由于其每个元素是有5个元素的一维数组,数组指针定义为int(*p)[5],指向首行这个“一维数组”。(传参穿的是数组名)
    • 第一层循环用于“跳行”,即每次跳过5个元素。第二层循环遍历每行“一维数组”。

    在这里插入图片描述

    用二维数组和数组指针接收的都是首行地址。
    数组指针的类型int(*)[5],和二维数组首元素地址的类型相同。

    故可得,二维数组首元素地址和数组指针是等价的,即数组指针pa就是数组名

    二维数组首元素为其首行,相当于一个一维数组,该一维数组的地址类型为int(*)[5]。且实参为二维数组名,降级为指向首行的指针,所以它是数组指针,类型为int(*)[5]。

    数组指针指向二维数组,才是使用数组指针的正确示范。

    Example

    下列示例分别是什么?

    //1.
    int arr[5];
    //2.
    int *pa1[5];
    //3.
    int (*pa2)[10];
    //4.
    int (*pa3[10])[5];
    

    1.整型数组

    2.存放整型指针的数组

    *靠左靠右无所谓,pa1先和[]结合为数组,剩下int*为数组元素类型。

    3.指向整型数组的指针

    (*pa2),*先和pa2结合为指针,剩下int[10],指向的是元素个数为10的整型数组。

    4.存放数组指针的数组

    pa3先和[10]结合为数组,剩下int(*)[5]是指向数组的指针为数组的元素。所以是个元素个数为10的数组指针数组。

    逆向思考,有整型数组arr[5]和指向该数组的类型为int(*)[5]的数组指针,还有数组指针数组pa3[10]用于存放该数组指针。

    在这里插入图片描述

    类型辨别方法

    1.若名称先和[]结合为数组,只去掉数组名就是数组类型,去掉[n]和数组名便是其元素的类型。

    2.若名称先和*结合为指针,只去掉指针名就是指针类型,去掉*和指针名便是指向的变量的类型。

    总结

    本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注海外IDC网的更多内容!

    【原URL 台湾大带宽服务器http://www.558idc.com/tw.html复制请保留原URL】