数组与指针
一、指针与数组的比较
指针 | 数组 |
保存数据的地址,任何存入指针变量P的数据都会被当作地址来处理。p本身的地址由编译器另外来储存,储存在哪里我们并不知道。 | 保存数据,数组名a代表的是数组首元素的首地址,而不是数组的首地址。&a才是数组的首地址,a本身的地址由编译器另外储存,储存在哪里,我们并不知道。 |
间接访问数据。首先取得指针p的内容,把它作为地址,然后从地址提取数据或向这个地址写入数据。指针可以以指针的形式访问*(p+i),也可以以下标的形式访问p[i]。但本质都是先取p的内容然后加上i*sizeof(类型)个byte作为数据的真正地址。 | 直接访问数据。数组名a是整个数组的名字,数组内每个元素并没有名字。只能通过“具名+匿名”形式来访问某个元素,不能把数组当作一个整体来进行读写操作,数组可以以指针的形式访问*(a+i),也可以以下标的形式访问a[i],但本质都是a所代表的数组首元素地址加上i*sizeof(类型)个byte作为数据的真正地址。 |
通常用于动态数据结构 | 通常用于储存固定数目且数据类型相同的元素。 |
相关函数为malloc和free | 隐式分配和删除 |
通常指向匿名数据(当然也可以指向具名数据) | 自身即为数组名 |
二、指针应用与动态内存管理(malloc和free的应用)
假如我们定义了一个数组,如果我们想要销毁这块空间,只有等到程序结束才能做到。但是如果我们想要动态的管理它(想让它有就有,想让它没有就没有),这时候我们就可以用malloc和free,列如:int *p = (int *)malloc(10 * sizeof( int));开辟了10个整型的大小空间,如果我们不想要它,可以用free(p),这样就可以了。当然内存也可能开辟失败。下面我写个简单的代码,大家看看。
程序代码:
#include<stdio.h>
int main()
{
int *p = (int *)malloc(10 * sizeof( int ));//开辟了10个整型大小的空间
if (p == NULL )//开辟失败
{
printf( "没有开辟成功!\n" );
return 1;
}
for (int i = 0; i < 10; i++)
{
p[i] = i;//初始化
}
free(p);//如果不想要这块空间,就把这句代码加上
system( "pause" );
return 0;
}
三、数组指针和指针数组
这个不好解释,我给大家举个例子吧
int *p1[10]//指针数组
int (*p2)[10]//数组指针
指针数组每个元素都是指针,我这里的这个例子,它的每个元素都是int*类型的。
指向数组的一个指针就叫数组指针,我这里这个例子,它每个元素类型是int。数组指针就是用来存放数组的地址。
切记:数组指针只是一个指针,它在内存空间里只有4个字节。
下图为指针数组与数组指针区别:
四、指针的偏移
下面我们看一个程序:
#include<stdio.h>
int main()
{
int a[4] = { 1, 2, 3, 4 };
int *p1 = (int *)(&a + 1);
int *p2 = (int *)((int)a + 1);
printf( "%x %x\n" , p1[-1], *p2);//p1[-1]=*(p1-1)
system( "pause" );
return 0;
}
这个程序的输出结果:
这是为什么哪?
在分析这个程序之前,我先给大家介绍一下,大端和小端。
大段就是低位放在高地址出,高位放在低地址处。
小端就是低位放在低地址处,高位放在高地址处。
有的编译器是大段存储,有的编译器是小端存储,我用的vs2013就是小端存储。
现在我们来分析上面那个代码:
(&a+1)向后偏移了一个数组的大小,(int*)类型是四个字节的大小,所以p[-1]就是偏移了四个字节。(int)a就是一个整型,((int)a+1)就相当与加了一个数字1,偏移一个字节。如图所示:
我这个编译器是按小端排列的,所以输出就是上面那个结果。
如果你不知道你编译器是大端存储还是小端存储,你可以编译一个程序检验一下。
程序代码:
#include<stdio.h>
int main()
{
int i = 1;
if (*(char *)&i == 1)
{
printf( "小端\n" );
}
else
{
printf( "大端\n" );
}
system( "pause" );
return 0;
}
当然我们也可以用函数的方式:
#include<stdio.h>
int check()
{
int i = 1;
if (*(char *)&i == 1)
{
return 1;
}
else
{
return 0;
}
}
int main()
{
int ret = check();
if (ret == 1)
{
printf( "小端\n" );
}
else
{
printf( "大端\n" );
}
system( "pause" );
return 0;
}
当然还可以用联合体来做:
#include<stdio.h>
union UN
{
int i;
char c;
};
int main()
{
union UN un = { 0 };
un.i = 1;
if (un.c == 1)
{
printf( "小端\n" );
}
else
{
printf( "大端\n" );
}
system( "pause" );
return 0;
}
也可以用函数:
#include<stdio.h>
int check()
{
union UN
{
int i;
char c;
} un;
un.i = 1;
if (un.c == 1)
{
return 1;//返回1表示小端
}
else
{
return 0;//返回0表示大端
}
}
int main()
{
int ret = check();
if (ret == 1)
{
printf( "小端\n" );
}
else
{
printf( "大端\n" );
}
system( "pause" );
return 0;
}
五、二维数组
下面我们看一个程序:
#include<stdio.h>
int main(int argc, char *argv [])
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
int *p;
p = a[0];
printf( "%d\n" , p[0]);
system( "pause" );
return 0;
}
先看一下它的输出结果:
为什么输出的不是0,而是1哪?
那是因为它用的是小括号,它显示的是小括号里面逗号的下一个数,也就是最后一个数,如果这个数组所有元素输出来,应该是1,3,5,0,0,0。大家可以用两个for循环试一下。如果你想要把这个数组初始化成0,1,2,3,4,5的话,应该把上面的小括号改成大括号。
下面我们在看一段程序:
#include<stdio.h>
int main()
{
int a[5][5] = { 0 };
int (*p)[4];
p = a;
printf( "%d\n" , &p[4][2] - &a[4][2]);
system( "pause" );
return 0;
}
它的输出结果:
P是一个数组指针,它访问的是【4】这么大,二a这个数组每一行是5,下面我用图来表示,这样就会更清楚了。
这就是为什么它输出的是-4.
我们在看一个程序:
#include<stdio.h>
int main()
{
int a[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int *p1 = (int *)(&a + 1);
int *p2 = (int *)(*(a + 1));
printf( "%d %d\n" , *(p1 - 1), *(p2 - 1));
system( "pause" );
return 0;
}
输出结果:
下面我们用图分析:
六、二级指针
二级指针是用来存放一级指针变量的地址的。
转载于:https://blog.51cto.com/mnt3918290/1716019