一:数组名取地址方式不同相关内存分析
在平常,对于一个数组,虽然我们取地址的方式不同,但是有可能会得到相同的地址,那么我们是不是就可以随便拿出一个方式来用呢,这肯定是不行的。
基于此,我们首先需要分析的是下面的一段简单代码,给出判断。下面的代码是对 arr[9] 的三种不同的形式进行取地址分析(为了方便观察,下面代码是在32位平台上跑的程序):
#include<stdio.h>
int main()
{
int arr[9] = { 1,2,3,4,5,6,7,8,9 };
//第一种: &arr[0]的地址本质
printf("&arr[0] = %p\n", &arr[0]);
printf("&arr[0]+1 = %p\n", &arr[0]+1);
//第二种: arr的地址本质
printf("arr = %p\n", arr);
printf("arr+1 = %p\n", arr+1);
//第三种:&arr的地址本质
printf("&arr = %p\n", &arr);
printf("&arr+1 = %p\n", &arr+1);
return 0;
}
对于结果的分析,我们可以看出,1、3、5的地址结果相同,它们分别对应着:
&arr[0] ,arr , &arr 。
是不是这三个的内部含义一样呢,可以看下面的分析:
对于这个分析结果,可以清楚的知道,当各自的数组名+1时,有的增加了一个int的字节(4个字节),有的增加了一个数组arr[9]的字节(36个字节)。
1.对于&arr[0]和 arr,取出的是首元素的地址,&arr[0]+1 和 arr+1,是首元素地址+1,取出的地址当然是arr[1]的地址啦。
2.对于 &arr,它们的地址和首元素的地址一样,但是,它们的地址是整个数组的地址,&arr+1都是增加整个数组的字节大小(代码中是9个int的大小),所有地址要加36
二:指针数组(存储地址的数组)
很简单,我们知道整型指针存储的是整型的地址,类型是int*,字符指针存储的是字符的地址,类型是char *,那么我们当然也有数组指针,数组指针存储的是一个数组的地址,如下图:
用法一:int *str[4]是整型类型的数组指针,每个元素的类型是int *,可以存储4个整型变量的地址。
#include<stdio.h>
int main()
{
//整型指针
int a = 3;
int* p_int = &a;
//字符指针
char c = 'a';
char* p_char = &c;
//数组指针
int x1 = 1, x2 = 2, x3 = 3, x4 = 4;
int *str[4] = { &x1,&x2,&x3,&x4 };
}
用法二:int *arr[3]是整型类型的指针数组(就是存储地址的数组),类型是 int *,可以存储3个整型变量的数组的地址。
三:数组指针(存储数组的地址)
在下图中,如果我们想存储二维数组的地址,那么我们怎么来写呢,用int *a[3]来表示显然不可以,因为int *p[3]是一个指针数组,不能存储一个数组的地址.
其实,二维数组本质也是一个数组,如下图:
我们用int (*p)[3]来存储 int a[2][3]的地址,下图中,可以将a数组的行数作为一个一维数组来存储,我们一般用int *p来存储一维数组的首地址,而每一个首地址又是一个数组,这时,用(*p)[3]来表示存储的这个数组地址,每个元素是int类型,所以用 int (*p)[3]来存储int a[2][3] 的地址,类型为int (*)[3] .
当行数+1时,int (*p+1)[3] 表示下一行的数组地址。
#include<stdio.h>
int main()
{
int a[2][3] = { 1,2,3,4,5,6 };
int(*p)[3] = a;
printf("%d ", p[0][0]);
printf("%d ", p[0][1]);
printf("%d ", p[0][2]);
printf("%d ", p[1][0]);
printf("%d ", p[1][1]);
printf("%d ", p[1][2]);
printf("\n=========================\n");
printf("%d ", *((*p) + 0) );
printf("%d ", *((*p) + 1) );
printf("%d ", *((*p) + 2) );
printf("%d ", *(*(p+1) + 0) );
printf("%d ", *(*(p+1) + 1) );
printf("%d ", *(*(p+1) + 2) );
}
从这里可以看出,两种表示方式的结果相同,p[1][1]和*(*(p+1)+1)表示的方式相同(这里的[]可以理解为是一个 * , * 是解引用操作符),这里第一种是最好理解的,但是在计算机中,全部都是转换成第二种方式来继续操作的,在做题的时候可以相互借鉴使用.
四:函数指针(存储函数的地址)
有时候,我们会遇到存储函数的地址的问题,(比如:回调函数,qsort函数也需要传函数的地址),我们首先得存储相对用函数得地址,方法如下(以加法函数Add为列子):