一、指针与数组的比较

指针数组
保存数据的地址,任何存入指针变量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;

}

这个程序的输出结果:

数组与指针_编译器_02

这是为什么哪?

在分析这个程序之前,我先给大家介绍一下,大端小端。

大段就是低位放在高地址出,高位放在低地址处。

数组与指针_动态_03

小端就是低位放在低地址处,高位放在高地址处。

数组与指针_编译器_04

有的编译器是大段存储,有的编译器是小端存储,我用的vs2013就是小端存储。

现在我们来分析上面那个代码:

(&a+1)向后偏移了一个数组的大小,(int*)类型是四个字节的大小,所以p[-1]就是偏移了四个字节。(int)a就是一个整型,((int)a+1)就相当与加了一个数字1,偏移一个字节。如图所示:

数组与指针_编译器_05

我这个编译器是按小端排列的,所以输出就是上面那个结果。

如果你不知道你编译器是大端存储还是小端存储,你可以编译一个程序检验一下。

程序代码:

#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;

}

先看一下它的输出结果:

数组与指针_编译器_06

为什么输出的不是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;

}

它的输出结果:

数组与指针_编译器_07

P是一个数组指针,它访问的是【4】这么大,二a这个数组每一行是5,下面我用图来表示,这样就会更清楚了。

数组与指针_编译器_08

这就是为什么它输出的是-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;

}

输出结果:

数组与指针_元素_09

下面我们用图分析:

数组与指针_编译器_10

六、二级指针

二级指针是用来存放一级指针变量的地址的。

数组与指针_编译器_11