指针数组、数组指针、指针的指针及函数指针详解与用法

先来介绍一下指针: 

指针一种类型,理论上来说它包含其他变量的地址,因此有的书上也叫它:地址变量。
是类型就有大小且都是4字节大小,里边只是存储了一个变量的地址而已。
大小为4字节是因为寻址空间为(32位)0x00000000~~0xffffffff,最大只需要4字节,所以就设定指针类型大小为四字节
不管什么类型的指针,char * 、int * 、int (*)[] 、string * 、float *、long  long *、 double * 、
int (*)()(函数指针)等等。
都是说明了本指针所指向的地址空间是什么类型而已,了解了这个基本上所有的问题都好象都变的合理了。

下面我们再来看一下指针数组和数组指针:

//如果你想了解指针最好理解以下的公式 : 
int*ptr;         //指针所指向的类型是int 
char*ptr;        //指针所指向的的类型是char 
int**ptr;        //指针所指向的的类型是int* (也就是一个int * 型指针) 
int(*ptr)[3];    //指针所指向的的类型是int()[3] //二维指针的声明 

指针数组:一个数组里存放的都是同一个类型的指针,通常我们把他叫做指针数组。 比如 int * a[10];它里边放了10个int * 型变量,由于它是一个数组,已经在栈区分配了10个(int * )的空间,也就是32位机上是40个字节,每个空间都可以存放一个int型变量的地址,这个时候你可以做个循环去初始化它。

例子:

#include
#include
#include
int main()
{
    int *ptr[2];
    ptr[0] = (int *)malloc(4);
    ptr[1] = (int *)malloc(4);
    int i = 0;
    for(i; i < 2; i++)
    {
        *ptr[i] = (i + 1) * 10;
    }
    printf("sizeof(ptr)=%d\n",sizeof(ptr));
    i = 0;
    for(i; i < 2; i++)
    {
        printf("sizeof(ptr[%d])=%d\n", i, sizeof(ptr[1]));
        printf("sizeof(*ptr[%d])=%d:*ptr[%d]=%d\n", i, sizeof(*ptr[1]), i, *ptr[i]);
    }

    int *qtr[2];
    int arr[] = {1, 2, 3};
    int brr[] = {10, 20, 30};
    qtr[1] = arr;
    qtr[2] = brr;
    i = 0;
    for(i; i < 3; i++)
    {
        printf("qtr[1][%d] = %d\n", i, qtr[1][i]);
    }
    i = 0;
    for(i; i < 3; i++)
    {
        printf("qtr[1][%d] = %d\n", i, qtr[2][i]);
    }

    char *str[5] = {"hello","world","welcome"};
    printf("sizeof(str)=%d\n",sizeof(str));
    i = 0;
    for(i; i < 3; i++)
    {
        printf("str[%d]=%s\n", i, str[i]);
        printf("sizeof(str[%d])=%d\n", i, sizeof(str[i]));
        printf("strlen(str[%d])=%d\n", i, strlen(str[i]));
    }
    free(ptr);
    return 0;
}
//注:动态分配的内存块要手动释放,然会造成内存泄漏

运行结果为:
指针数组、数组指针、指针的指针及函数指针详解与用法_第1张图片

数组指针: 一个指向一维或者多维数组的指针;

int main()
{
   int * b = int arr[10];//指向一维数组的指针b    
   int (*b2)[10] = int brr[10][10]; //注意,这里的b2指向了一个二维int型数组的首地址. 注意:在这里,b2等效于二维数组名,但没有指出其边界,即最高维的元素数量,但是它的最低维数的元素数量必须要指定!就像指向字符的指针,即等效一个字符串,不要把指向字符的指针说成指向字符串的指针。这与数组的嵌套定义相一致。 
   int(*b3) [30] [20];   //三级指针――>指向三维数组的指针; 
   int (*b4) [20];       //二级指针; 
   b3 = int crr[1] [20] [30]; 
   b4 = int drr[30] [20];//两个数组都是由600个整数组成,前者是只有一个元素的三维数组,每个元素为30行20列的二维数组,而另一个是有30个元素的二维数组,每个元素为20个元素的一维数组。
                         //再次重申:这里的b2的类型是int (*) ,这样表示一个指向二维数组的指针。 b3表示一个指向(指向二维数组的指针)的指针,也就是三级指针.
   return 0;
}
  • 补充: 不管是一维数组还是多维数组存储地址空间都是连续的。

现在我们来对比一下指向一维数组和二维数组的指针的一些常用用法:

int arr[10];
int *ptr = arr;
&arr+1;     //加整个数组的大小,即加 4*10
arr+1;      //到下一个元素位置,即加 4
arr[0]+1;   //0号元素的值加 1
&arr[0]+1;  //到下一个元素位置,即加 4
&ptr+1;     //加一个指针类型的大小,即加 4
ptr+1;      //到下一个元素位置,即加 4
ptr[0];     //取0号位置元素


int brr[3][4];
int (*ptrarr)[] = brr;
&brr+1;              //加整个数组的大小,即加 4*12
brr+1;               //加第一个一维数组的大小,即加 4*4
brr[0]+1;            //加第一个一维数组的一个元素的大小,即加 4
&brr[0]+1;           //加第一个一维数组的大小,即加 4*4
brr[0][0]+1;         //第一个一维数组的第一个元素值加1
&brr[0][0]+1;        //加到第一个一维数组第一个元素的下一个元素
&ptr+1;              //加一个指针类型的大小,即加 4
ptr+1;               //加第一个一维数组的大小,即加 4*4
ptr[0]+1;            //加第一个一维数组的一个元素的大小,即加 4
&ptr[0]+1;           //加第一个一维数组的大小,即加 4*4

指针的指针:

  • 顾名思义,就是保存指针的地址,指向指针的指针。一级指针可以获取到两个值,一个是保存的地址,另一个是所指向地址空间里面的值。指针的指针可以获取到三个值,第一个是保存的指针的地址,第二个是保存指针所指向的地址,第三个是保存指针所指向的地址空间里面的值。一级指针有一次解引用,二级指针有两次解引用,以此类推。每次解引用得到的是指向所保存的值。
int main()
{
   int a = 10;
   int *ptr = &a;
   int **pptr = &ptr;
   printf("ptr=%x\n",ptr); 
   printf("*ptr=%d\n",*ptr);
   printf("pptr=%x\n",pptr);
   printf("*ptr=%x\n",*pptr);
   printf("**ptr=%d\n",**pptr); 
   return 0;
}

这里写图片描述

函数指针:

  • 函数指针就是指向函数首地址的指针,函数也有地址空间,首地址即为函数名又为函数的入口,通过函数指针就能够调用该指针所指向的函数。

例子:

int sum(int a, int b)
{
    return a + b;
}
int main()
{
    int a = 10;
    int b = 20;
    int (*p)() = sum;
    int m = (*p)(a, b);
    //int m = p(a, b);
    printf("m=%d\n",m);
    return 0;
}

这里写图片描述

函数指针用途:

  • 作为函数的参数传递,回调函数(泛型编程)。

  • 表移动,转移表(系统函数调用指针表,用于调用系统函数)。

指针和数组名的区别与联系:
在大多数情况下数组名和指针是可以互相使用的,如函数的形参传递、数组值的访问等大多数情况下可以互用。不同点在于1、对数组名取地址计算大小计算的是整个数组的大小,执行 +1 操作加的是整个数组的大小。而对于指针加的都是类型的大小;2、对于数组名的值无法修改,因为一旦修改了数组名 =的值数组的数据就会丢失,不能执行 arr += 1 的操作。而对于指针,他的值,即指向的地址可以任意修改(在确保安全的前提下没有限制),可以执行 p += 1 的操作。

关于数组指针和函数指针,接触比较少,总结的也不多,欢迎大家前来补充。

你可能感兴趣的:(c)