指针的应用

一.数组与指针

 1.指针数组(存放指针的数组

  例如:char *p[10]; float *p[10];int * q[4][3];

  ♦ 一个数组,及其元素均为指针类型的数据——指针数组

   一维指针数组:类型名 数组名[数组长度];

   二维指针数组:类型名 数组名[行][列];

   • 一维数值指针数组:

     int array[8]  = {1,2,3,4,5,6,7,8};

     int *p[6] = {&array[0],&array[1],&array[2],&array[3]};

     p[4] = &array[4];

     p[5] = &array[5];

   • 二维数值指针数组:

     int *a[2][3];

     a[0][1] = &array[0];

   • 指针数组与字符串的联系:

     char *name[2] = {"hello","world"};

     name[0] = "hello";

     name[1] = "world";

    数组中存放的是字符串的首地址,而不是字符串。

//指针数组
#include <stdio.h>

int main(int argc,char *argv[])
{
    char *name[] = {"Follow me","BASIC","Grate Wall"};
    int i;
    for(i = 0;i < 3;i++)
    {
        printf("%s",name[i]);
    }
    return 0;
}

 2.数组元素指针(指向数组元素的指针)

  ♦ 数组元素在内存中分配的地址——数组元素的指针

   • 定义一个指向数组元素的指针

     int a_1[10];

     int a_2[5][5];

     int *p,*q;

     p = &a_1[0];  //一维数组元素的指针

     q = &a_2[1][1];  //二维数组元素的指针

   • 在C语言中,设定一维数组名代表了数组第一个元素的地址。

     int array[5];

     int *p;

     p = array[0];  等价于  p = array;

   • 引用数组元素

    1.下标法:array[1];

    2.指针法:*(p + i);或*(array+i);

     • array为数组的首地址是一个常量,不能进行array++或++array操作。

     • p是变量,其值为array数组的首地址,可以进行++操作。

//采用指针法引用数组元素
#include <stdio.h>
int main(int argc,char *argv[])
{
    int a[5],i,*pa,*pb;
    pa = a;    //数组名代表数组首地址
    pb = &a[0];    //数组的第一个元素的地址也代表数组首地址
    for(i = 0;i < 5;i++)
    {
        a[i] = i;
        printf("a[%d] = %d\n",i,a[i]);    //0 1 2 3 4
        printf("a[%d] = %d\n",i,*(a+i));    //0 1 2 3 4
        printf("a[%d] = %d\n",i,*(pa+i));    //0 1 2 3 4
        printf("a[%d] = %d\n",i,*pb++);    //0 1 2 3 4
    }
    //printf("a[%d] = %d\n",i,a++);    //a为常量,不能进行++操作
    return 0; 
}

  3.数组指针(行指针)(指向数组的指针)

  ♦ 数组元素在内存中的起始地址——数组元素的指针

   • 定义一个数组元素的指针

     int a[10]  = {1,2,3,4};

     int *p = a;  //p为数组元素的指针

   • 数组在内存中的起始地址称为数组的指针

     int a[10] = {1,2,3,4};

     int (*q)[10]  = &a;  //q为指向数组的指针

     a为一维数组名,代表数组第一个元素的指针。

     &a:对a取地址,代表将整个数组当做一个整体,将这个地址付给q——数组指针。

//数组指针
#include <stdio.h>

int main(int argc,char *argv[])
{
    int a[5] = {1,2,3,4,5};
    int (*p)[5]  = &a;    //定义一个一维数组,指向一个一维数组
    printf("%d\n",*((int *)(p+1)-1));    //5,p+1移动整个数组的长度
    return 0;
}

   • 指向二维数组的数组指针

     int a[3][4] = {{1,3,2,4},{5,6,7,34},{5,9,6,8}};

     int (*p)[4] = a;

     p等价于指向二维数组第0行,可完全替代a的作用。

   • 二维数组名

     • 二维数组名是一个二级指针

     • a代表了二维数组第0行的行地址

     • a+1代表了第一行的行地址

     • *(a+1)代表了第1行第0列元素的地址

     • *(a+1)+2代表了第1行第2列元素的地址

     • *(*(a+1)+2)代表了第1行第2列元素

   • 多维数组与指针

表示形式 含义
a 数组名,指向一维数组a[0],即0行首地址
a+1、&a[1] 1行首地址
*(a+0)、*a、a[0] 0行0列元素地址
*(a+1)、(*(a+1)+0)、a[1] 1行0列元素a[1][0]的地址
*(a+1)+2、a[1]+2、&a[1][2] 1行2列元素a[1][2]的地址
*(a[1]+2)、*(*(a+1)+2)、a[1][2] 1行2列元素a[1][2]的值
//数组指针
#include <stdio.h>
int main(int argc,char *argv[])
{
    int a[3][5]={
                  {1,2,3,4,5},
                  {6,7,8,9},
                  {14,12,23,66}
                };
    int i,j;
    int (*p)[5] = a;    //定义一个数组指针指向一个二维数组
    for(i = 0;i < 5;i++)    //采用数组指针打印二维数组的第一行
        printf("%d\n",*(p[0]+i));
    for(i = 0;i < 5;i++)    //采用数组指针打印二维数组的第二行
        printf("%d\n",*(p[1]+i));
    for(i = 0;i < 3;i++)    //采用数组指针打印二维数组的全部内容
    {
        for(j=0;j<5;j++)
        {    
            printf("%d\t",*(p[i]+j));
            if(j == 4)
                printf("\n");
        }
    }
    return 0;
}

二.函数与指针

 1.指针作函数参数

  • 指针变量作函数的参数

//指针作函数的参数
#include <stdio.h>

int exchange1(int p1,int p2)
{
    int temp;
    temp = p1;
    p2 = p1;
    p2 = temp;  
}

int exchange2(int *p1,int *p2)
{
    int temp;
    temp = *p1;
    *p1 = *p2;
    *p2 = temp;
}

int main(int argc,char *argv[])
{
    int a,b;
    scanf("%d %d",&a,&b);    //a = 9,b = 3
    if(a > b)
        //exchange1(a,b);    //a = 9,b = 3
        exchange2(&a,&b);    //a = 3,b = 9
    printf("a = %d,b = %d\n",a,b);
    return 0;
}

  • 一维数组名作函数的参数

//一维数组名作函数的参数
#include <stdio.h>

// int max(int a[],int num)    //数组作形参时,无需指定下表
int max(int *a,int num)    //数组名作形参时,将演变为指针
{
    int i,max;
    max = a[0];
    for(i = 0;i  < num;i++)
    {
        if(a[i] > max)
            max = a[i];
    }
    printf("a_len = %d\n",sizeof(a));    //a_len = 4
    return max;
}

int main(int argc,char *argv[])
{
    int a[5] = {4,7,9,6,5};
    printf("max = %d\n",max(a,5));    //max = 9
    return 0;
}

     • 数组名作形参时,无需指定其下标。

     • 数组名作形参时,将演变为指针。

  • 二维数组名作函数的参数

//一维数组名作函数的参数
#include <stdio.h>

// int max(int a[][3],int x,int y)    //二维数组名作形参时,行可以不写出来,列必须写出来
int max(int (*a)[3],int x,int y)    //数组指针作形参,完全等价于二维数组的应用
{
    int i,j,max;
    max = a[0][0];
    for(i = 0;i  < x;i++)
    {
        for(j = 0;j < y;j++)
        {
            if(a[i][j] > max)
                max = a[i][j];
        }
    }
    printf("a_len = %d\n",sizeof(a));    //a_len = 4
    return max;
}

int main(int argc,char *argv[])
{
    int a[2][3] = {
                    {4,7,9},
                    {11,6,5}
                  };
    printf("max = %d\n",max(a,2,3));    //max = 11
    return 0;
}

     • 二维数组名作参数,行可以不写出来,列必须写出来。

     • 数组指针作形参,完全等价于二维数组的应用。

 2.指针作函数返回值

   • 返回指针值的函数,一般定义形式:类型名 *函数名(参数列表);例如:int *fun(int x,int y);

   • 在调用时要先定义一个适当的指针来接收函数的返回值,这个适当的指针其类型应为函数返回指针所指向的类型。

     char *pc = NULL;

     pc = (char *)malloc(100 * sizeof(char));  //表示分配100个字节的内存空间,并强制转换为字符数组类型,函数的返回值为指向该字符数组的指针,把该指针赋予指针变量pc

 3.指向函数的指针(函数指针)

  ♦ 一个函数在编译时被分配一个入口地址,这个地址就称为——函数的指针。

    • 函数名代表函数的入口地址。

    • 指向函数指针变量的定义格式:int (*p)(int,int);

      例如:int (*p)(int,int);

         p = max;  //将函数的入口地址赋给函数指针变量p

         c = (*p)(a,b);  //调用max函数

      • 说明:

        • p不是固定指向哪个函数的,而是专门用来存放函数入口地址的变量,在程序中把哪个函数的入口地址赋给他,它就指向哪个函数。

        • p不能像指向变量的指针变量一样进行p++、p--等无意义的操作。

  ♦ 函数指针的应用——回调函数(钩子函数)

    • 函数指针变量常见的用途之一是把指针作为参数传递到其它函数。

    • 指向函数的指针也可以作为参数,以实现函数地址的传递,这样就能够在被调用的函数中使用实参函数。

//函数指针
//process称为回调函数,process并没有直接调用max、min、add函数,而是通过fun函数指针接收相应函数首地址,进行调用,调用后把结果返回给主调函数main
#include <stdio.h>

int max(int x,int y)
{
    return x>y?x:y;
}

int min(int x,int y)
{
    return x<y?x:y;
}

int add(int x,int y)
{
    return x+y;
}

int process(int x,int y,int (*fun)(int ,int ))    //回调函数
{
    int result;
    result = (*fun)(x,y);
    return result;
}

int main(int argc,char *argv[])
{
    int a,b,result;
    
    printf("input a and b:");
    scanf("%d %d",&a,&b);
    
    result = process(a,b,max);
    printf("max = %d\n",result);
    
    result = process(a,b,min);
    printf("min = %d\n",result);
    
    result = process(a,b,add);
    printf("add = %d\n",result);
    return 0;
}

三.其它特殊指针

 1.main函数带参  ./a.out hello peter ……

   • main函数可以接收来自操作系统或者其它应用传递的参数。

   • int main(int argc,char *argv[])

   • 说明:

     • argc代表参数的个数

     • argv存放各参数首地址(系统把每个参数当作一个字符串放在系统缓冲区,把首地址放在指针数组中)

     • ./a.out hello peter …… 依次存放在argv[0] argv[1] argv[2] …… 中

//main函数传参
//如执行    ./a.out hello lucy
//输出:
// argc = 3
// argv[0] = ./a.out
// argv[1] = hello
// argv[5] = lucy

#include <stdio.h>

int main(int argc,char *argv[])
{
    int i;
    printf("argc = %d\n",argc);
    for(i = 0;i < argc;i++)
        printf("argv[%d] = %s\n",i,argv[i]);
    return 0;
}

 2.指向指针的指针  char **p

  ♦ 一个变量有自己的地址,我们可以用一个指针变量指向它,指针变量同样也是变量,我们也可以定义另一个指针指向这个指针变量——指向指针的指针

    int a;

    int *p = &a;

    int *q = &p;

  ♦ 通过函数改变指针的指向

//通过函数改变指针的指向
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void GetMemory1(char *p,int num)    //泄露一块内存
{
    p = (char *)malloc(num * sizeof(char));
}

void GetMemory2(char **p,int num)    //在子函数内部改变主调函数定义指针的指向
{
    *p = (char *)malloc(num * sizeof(char));
}

int main(int argc,char *argv[])
{
    char *str = NULL;
    // GetMemory1(str,100);    //段错误
    GetMemory2(&str,100);    //hello kitty
    strcpy(str,"hello kitty");
    printf("str = %s\n",str);
    free(str);
    return 0;
}

 3.void类型的指针  void *指针

  ♦ void指针是一种很特别的指针,并不指定它是指向哪一种类型的数据,而是根据需要转换为所需数据类型。

    int a = 0;

    float b = 0;

    void *p = &a;

    void *q = &b;

  ♦ 常用于函数的返回值和参数,用于不确定类型指针指向

    void malloc(unsigned int num_types);

    void memset(void *str,char ch,size_t n);

 4.函数指针数组

  函数指针数组的定义  例如:int * (*p[10])(int a[3][4],int num)  //定义了一个函数指针数组,带有两个参数,返回值为int *类型

posted @ 2017-11-25 15:24  lemongirl  阅读(1346)  评论(0编辑  收藏  举报