zoukankan      html  css  js  c++  java
  • 第8章 善于利用指针

    学习 C 语言的指针既简单又有趣。通过指针,可以简化一些 C 编程任务的执行,还有一些任务,如动态内存分配,没有指针是无法执行的。所以,想要成为一名优秀的 C 程序员,学习指针是很有必要的。

    正如您所知道的,每一个变量都有一个内存位置,每一个内存位置都定义了可使用连字号(&)运算符访问的地址,它表示了在内存中的一个地址。请看下面的实例,它将输出定义的变量地址:

    #include <stdio.h>

    int main ()

    {

       int  var1;

       char var2[10];

       printf("var1 变量的地址: %p ", &var1  );

       printf("var2 变量的地址: %p ", &var2  );

       return 0;

    }

    当上面的代码被编译和执行时,它会产生下列结果:

    var1 变量的地址: 0x7fff5cc109d4

    var2 变量的地址: 0x7fff5cc109de

    通过上面的实例,我们了解了什么是内存地址以及如何访问它。接下来让我们看看什么是指针。

    8.1什么是指针?

    指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。就像其他变量或常量一样,您必须在使用指针存储其他变量地址之前,对其进行声明。

    指针变量声明的一般形式为:type *var-name;

    在这里,type 是指针的基类型,它必须是一个有效的 C 数据类型,var-name 是指针变量的名称。用来声明指针的星号 * 与乘法中使用的星号是相同的。但是,在这个语句中,星号是用来指定一个变量是指针。以下是有效的指针声明:

    int    *ip;    /* 一个整型的指针 */

    double *dp;    /* 一个 double 型的指针 */

    float  *fp;    /* 一个浮点型的指针 */

    char   *ch;     /* 一个字符型的指针 */

    所有指针的值的实际数据类型,不管是整型、浮点型、字符型,还是其他的数据类型,都是一样的,都是一个代表内存地址的长的十六进制数。不同数据类型的指针之间唯一的不同是,指针所指向的变量或常量的数据类型不同。

    8.2指针变量

    8.2.1 使用指针变量的例子

    #include <stdio.h>

    int main()

    { int a=100,b=10;

    //定义整型变量a,b,并初始化

    int *pointer_1,*pointer_2;

    //定义指向整型数据的指针变量pointer_1, pointer_2

    pointer_1=&a; //把变量a的地址赋给指针变量pointer_1

           pointer_2=&b;   //把变量b的地址赋给指针变量pointer_2

    printf("a=%d,b=%d ",a,b); //输出变量a和b的值

    printf("*pointer_1=%d,*pointer_2=%d ",*pointer_1,*pointer_2);

    //输出变量a和b的值

    return 0;

    }

    运行结果:

     

    上面变量对应的关系,如图:

    注:定义指针变量时,左侧应有类型名,否则就不是定义指针变量。也称为基类型

    8.2.2 怎样定义指针变量

    定义指针的一般形式为:

    类型名 *指针变量名;

    如:

    int *pointer_1;

    注意:左端的int是在定义指针变量时必须指定的"基类型".换个说法就是类型名一定要有,否则是错误的定义。

    如:

    *pointer_1; //企图定义pointer_1为指针变量。出错

    int *pointer_1; //正确,必须指定指针变量的基类型

    说明:在定义指针变量时要注意一下几点:

    (1) 指针变量前面的"*"表示该变量的类型为指针型变量。

    (2) 在定义指针变量时必须制定基类型(即类型名)

    一个变量的指针的含义包括两个方面,一是以存储单元编号表示的地址(如编号为2000的字节),一是它指向的存储单元的数据类型(如int,char,float等)。

    (3)如何表示指针类型

    指向整型数据的指针类型表示为"int*",读作"指向int的指针"或简称"int指针"

    (4) 指针变量中只能存放地址(地址),不要讲一个整数赋给一个指针变量。如:

    *pointer_1 = 100; //pointer_1是指针变量,100是整数,不合法的赋值

    8.2.3 怎样引用指针变量

    (1) 给指针变量赋值。如:

    p = &a; //把a的地址赋给指针变量a

    (2) 引用指针变量指向的变量

    如果已执行"p = &a;",即指针变量p指向了整型变量a,则

    printf("%d",*p);

    作用:以整数形式输出指针变量p所指向的变量的值,即变量a的值

    注:一定要带'*',否则输出地址

    也可以用指针的形式对a重新赋值

    *p = 1;

    表示将1赋值给p当前p指向的变量,因为p指向变量a,则相当于把1赋值给a,即和"a = 1"等价。

    (3) 引用指针变量的值。如:

    printf("%o",p);

    作用:以八进制数形式输出指针变量p的值,如果p指向了a,就是输出了a的地址,即&a.

    printf("%o",&a);   <==> printf("%o",p); //两者等价

    例:

    #include <stdio.h>

    int main()

    {

    int *p,a;

    a = 100;

    p = &a;

    printf("%o ",p);

    printf("%o ",&a);

    }

     

    要熟练掌握两个有关的运算符:

    (1) &取地址运算符。&a是变量a的地址。

    (2) * 指针运算符(或称“间接访问”运算符),*p代表指针变量p指向的对象。

    #include <stdio.h>

    int main()

    { int *p1,*p2,*p,a,b;     //p1,p2的类型是int *类型

    printf("please enter two integer numbers:");

           scanf("%d,%d",&a,&b);                       //输入两个整数

    p1=&a;       //使p1指向变量a

    p2=&b;       //使p2指向变量b

    if(a<b)       //如果a<b

    { p=p1;p1=p2;p2=p;}   //使p1与p2的值互换

    printf("a=%d,b=%d ",a,b);   //输出a,b

    printf("max=%d,min=%d ",*p1,*p2); //输出p1和p2所指向的变量的值

    return 0;

    }

    运行结果:

     

    指针在过程中的变化:

     

    8.2.4 指针变量作为函数参数

    例:对输入的两个整数按大小顺序输出。现用函数处理,而且用指针类型的数据作函数参数。

    #include <stdio.h>

    void swap(int *p1,int *p2);    //对swap函数的声明

    int main()

    {

    int a,b;

    int *pointer_1,*pointer_2; //定义两个int *型的指针变量

    printf("please enter a and b:");

    scanf("%d,%d",&a,&b);  //输入两个整数

    pointer_1=&a;   //使pointer_1指向a

           pointer_2=&b;                 //使pointer_2指向b

    if(a<b) swap(pointer_1,pointer_2); //如果a<b,调用swap函数

    printf("max=%d,min=%d ",a,b);  //输出结果

    return 0;

    }

    void swap(int *p1,int *p2)   //定义swap函数

    { int temp;

    temp=*p1;     //使*p1和*p2互换

    *p1=*p2;

    *p2=temp;

    }

    运行结果:

     

    指针变量pointer_1 pointer_2的变量过程

     

    注:要注意区分以下三种形式,可自己调试,增加理解

    void swap(int *p1,int *p2)//定义swap函数

    { int temp;

    temp=*p1;//使*p1和*p2互换

    *p1=*p2;

    *p2=temp;

    }

    void swap(int *p1,int *p2)

    { int *temp;

    *temp=*p1;

          *p1=*p2;

    *p2=*temp;

    }

    void swap(int x,int y)

    { int temp;

    temp=x;

          x=y;

    y=temp;

    }

    下面有一个问题需要注意,也可以直接跳过,如果想提升可以理解,以理解为主,不需要死记硬背。

    指针变量作为函数参数

    函数的调用可以(而且只可以)得到一个返回值(即函数值),而使用指针变量作参数,可以得到多个变化了的值。如果不用指针变量是难以做到这一点的。要善于利用指针法。

    如果想通过函数调用得到n个要改变的值,可以这样做:

    1.在主调函数中设n个变量,用n个指针变量指向它们;

    2.设计一个函数,有n个指针形参。在这个函数中改变这n个形参的值;

    3.在主调函数中调用这个函数,在调用时将这n个指针变量作实参,将它们的值,也就是相关变量的地址传给该函数的形参;

    3.在执行该函数的过程中,通过形参指针变量,改变它们所指向的n个变量的值;

    5.主调函数中就可以使用这些改变了值的变量。

    例:输入3个整数a,b,c,要求按由大到小的顺序将它们输出。用函数实现。

    #include <stdio.h>

    int main()

    { void exchange(int *q1, int *q2, int *q3); //函数声明

    int a,b,c,*p1,*p2,*p3;

    printf("please enter three numbers:");

    scanf("%d,%d,%d",&a,&b,&c);

    p1=&a;p2=&b;p3=&c;

    exchange(p1,p2,p3);

    printf("The order is:%d,%d,%d ",a,b,c);

    return 0;

    }

    void exchange(int *q1, int *q2, int *q3) //将3个变量的值交换的函数

    { void swap(int *pt1, int *pt2);  //函数声明

    if(*q1<*q2) swap(q1,q2);  //如果a<b,交换a和b的值

    if(*q1<*q3) swap(q1,q3);  //如果a<c,交换a和c的值

    if(*q2<*q3) swap(q2,q3);  //如果b<c,交换b和c的值

    }

    void swap(int *pt1, int *pt2)   //交换2个变量的值的函数

    { int temp;

    temp=*pt1;    //交换*pt1和*pt2变量的值

    *pt1=*pt2;

    *pt2=temp;

    }

    运行结果:

     

    8.3 通过指针引用数组

    8.3.1 数组元素的指针

    数组元素的指针就是素组元素的地址。

    int a[10]={1,3,5,7,9,11,13,15,17,19};  //定义a为包含10个整型数据的数组

    int *p;         //定义p为指向整型变量的指针变量

    p=&a[0];        //把a[0]元素的地址赋给指针变量p

    引用数组元素的方式

    (1) 下标法

    (2) 指针法

    p=&a[0]; //p的值是a[0]的地址

    等价于

    p=a; //p的值是数组a首元素(即a[0])的地址

    注:程序中的数组名不代表整个数组,只代表数组首元素的地址。

    以下是指针变量的初始化方式:

    (1) 在定义后再单独进行初始化

    int *p;

    p=&a[0]; //不应写成*p=&a[0];

    (2) 在定义时直接进行初始化

    int *p=&a[0];

    等价于

    int *p=a;

    以上三种的作用:将a数组首元素(即a[0])的地址赋值给指针变量p(而不是赋给*p)

    8.3.2 在引用数组元素时指针的运算

    在指针已指向一个数组元素时,可以对指针进行以下运算:

    1.加一个整数(用+或+=),如p+1,表示指向同一数组中的下一个元素;

    2.减一个整数(用-或-=),如p-1,表示指向同一数组中的上一个元素;

    3.自加运算,如p++,++p;

    4.自减运算,如p--,--p。

    两个指针相减,如p1-p2(p1和p2都指向同一数组中的元素时才有意义),结果为两个地址之差除以数组元素的长度。

    注:两个地址不能相加,如p1+p2是无实际意义的。

    说明:

    *(p+i)或*(a+i)是p+i或a+i所指向的数组元素,即a[i]。

    p的初值为&a[0],则p+i和a+i就是数组元素a[i]的地址

    []实际上是变址运算符,即将a[i]按a+i计算地址,然后找出此地址单元中的值。

    例:有一个整型数组a,有10个元素,要求输出数组中的全部元素。

    //下标法

    #include <stdio.h>

    int main()

    { int a[10];

    int i;

    printf("please enter 10 integer numbers:");

    for(i=0;i<10;i++)

    scanf("%d",&a[i]);

    for(i=0;i<10;i++)

    printf("%d ",a[i]);

    //数组元素用数组名和下标表示

    printf("% ");

    return 0;

    }

    //通过数组名计算数组元素地址,找出元素的值

    #include <stdio.h>

    int main()

    { int a[10];

    int i;

    printf("please enter 10 integer numbers:");

    for(i=0;i<10;i++)

    scanf("%d",&a[i]);

    for(i=0;i<10;i++)

    printf("%d ",*(a+i));

    //通过数组名和元素序号计算元素地址找到该元素

    printf(" ");

    return 0;

    }

    //用指针变量指向数组元素

    #include <stdio.h>

    int main()

    { int a[10];

    int *p,i;

    printf("please enter 10 integer numbers:");

    for(i=0;i<10;i++)

    scanf("%d",&a[i]);

    for(p=a;p<(a+10);p++)

    printf("%d ",*p);

    //用指针指向当前的数组元素

    printf(" ");

    return 0;

    }

    说明:

    第(1)和第(2)种方法执行效率是相同的。C编译系统是将a[i]转换为*(a+i)处理的,即先计算元素地址。因此用第(1)和第(2)种方法找数组元素费时较多。

    第(3)种方法比第(1)、第(2)种方法快,用指针变量直接指向元素,不必每次都重新计算地址,像p++这样的自加操作是比较快的。这种有规律地改变地址值(p++)能大大提高执行效率。

    例:通过指针变量输出整型数组a的10个元素。

    #include <stdio.h>

    int main()

    { int *p,i,a[10];

    p=a;    //p指向a[0]  ①

    printf("please enter 10 integer numbers:");

    for(i=0;i<10;i++)

    scanf("%d",p++); //输入10个整数给a[0]~a[9]

    for(i=0;i<10;i++,p++)

    printf("%d ",*p); //想输出a[0]~a[9] ②

    printf(" ");

    return 0;

    }

    #include <stdio.h>

    int main()

    { int i,a[10],*p=a; //p的初值是a,p指向a[0]

    printf("please enter 10 integer numbers:");

    for(i=0;i<10;i++)

    scanf("%d",p++);

    p=a;    //重新使p指向a[0]

    for(i=0;i<10;i++,p++)

    printf("%d ",*p);

    printf(" ");

    return 0;

    }

    运行上面两个代码看看有何区别,造成这种区别的原因是什么?

    答:第一种是因为指针已经到了数组的末尾,继续循环则超出了数组的范围,则会直接输出P对应的地址值。如图所示:

     

    技巧:

    设p开始时指向数组a的首元素(即p=a)

    (1)

    p++; //使p指向下一元素a[1]

    *p; //得到下一个元素a[1]的值

    (2)

    *p++; /*由于++和*同优先级,结合方向自右而左,因此它等价于*(p++)。先引用p的值,实现*p的运算,然后再使p自增1*/

    (3)

    *(p++); //先取*p值,然后使p加1

    *(++p); //先使p加1,再取*p

    (4)

    ++(*p); /*表示p所指向的元素值加1,如果p=a, 则相当于++a[0],若a[0]的值为3,则a[0]的值为4。注意: 是元素a[0]的值加1,而不是指针p的值加1*/

    (5)

    如果p当前指向a数组中第i个元素a[i],则:

    *(p--) //相当于a[i--],先对p进行“*”运算,再使p自减

    *(++p) //相当于a[++i],先使p自加,再进行“*”运算

    *(--p) //相当于a[--i],先使p自减,再进行“*”运算

    8.3.4 用数组名作函数参数

    以变量名和数组名作为函数参数的比较

     

    C语言调用函数时虚实结合的方法都是采用“值传递”方式,当用变量名作为函数参数时传递的是变量的值,当用数组名作为函数参数时,由于数组名代表的是数组首元素地址,因此传递的值是地址,所以要求形参为指针变量。

    注意:实参数组名代表一个固定的地址,或者说是指针常量,但形参数组名并不是一个固定的地址,而是按指针变量处理。

    例:将数组a中n个整数按相反顺序存放

    #include <stdio.h>

    int main()

    { void inv(int x[],int n); //inv函数声明

    int i,a[10]={3,7,9,11,0,6,7,5,4,2};

    printf("The original array: ");

    for(i=0;i<10;i++)

    printf("%d ",a[i]); //输出未交换时数组各元素的值

    printf(" ");

    inv(a,10);    //调用inv函数,进行交换

    printf("The array has been inverted: ");

    for(i=0;i<10;i++)

    printf("%d ",a[i]); //输出交换后数组各元素的值

    printf(" ");

    return 0;

    }

    void inv(int x[],int n)  //形参x是数组名

    { int temp,i,j,m=(n-1)/2;

    for(i=0;i<=m;i++)

    { j=n-1-i;

    temp=x[i]; x[i]=x[j]; x[j]=temp; //把x[i]和x[j]交换

    }

    return;

    }

    #include <stdio.h>

    int main()

    { void inv(int *x,int n);

    int i,a[10]={3,7,9,11,0,6,7,5,4,2};

    printf("The original array: ");

    for(i=0;i<10;i++)

    printf("%d ",a[i]);

    printf(" ");

    inv(a,10);

    printf("The array has been inverted: ");

    for(i=0;i<10;i++)

    printf("%d ",a[i]);

    printf(" ");

    return 0;

    }

    void inv(int *x,int n)   //形参x是指针变量

    { int *p,temp,*i,*j,m=(n-1)/2;

    i=x; j=x+n-1; p=x+m;

    for(;i<=p;i++,j--)

    { temp=*i; *i=*j; *j=temp;} //*i与*j交换

    return;

    }

    以上两种方式所得结果是一致的,只是实现的形式有区别。所得结果都为下图所示:

    说明:以下的形式要认识,以下四种都是等价的

     

    例:将数组a中n个整数按相反顺序存放,用指针变量作实参。

    #include <stdio.h>

    int main()

    { void inv(int *x,int n); //inv函数声明

    int i,arr[10],*p=arr;  //指针变量p指向arr[0]

    printf("The original array: ");

    for(i=0;i<10;i++,p++)

    scanf("%d",p);  //输入arr数组的元素

    printf(" ");

    p=arr;    //指针变量p重新指向arr[0]

    inv(p,10);    //调用inv函数,实参p是指针变量

    printf("The array has been inverted: ");

    for(p=arr;p<arr+10;p++)

    printf("%d ",*p);

    printf(" ");

    return 0;

    }

    void inv(int *x,int n)         //定义inv函数,形参x是指针变量

    { int *p,m,temp,*i,*j;

    m=(n-1)/2;

    i=x;j=x+n-1;p=x+m;

    for(;i<=p;i++,j--)

    { temp=*i;*i=*j;*j=temp;}

    return;

    }

    注:如果用指针变量作实参,必须先使指针变量有确定值,指向一个已定义的对象。

    例:用指针方法对10个整数按由大到小顺序排序。(选择排序法)

    //形参是数组

    #include <stdio.h>

    int main()

    { void sort(int x[],int n); //sort函数声明

    int i,*p,a[10];

    p=a;     //指针变量p指向a[0]

    printf("please enter 10 integer numbers:");

    for(i=0;i<10;i++)

    scanf("%d",p++); //输入10个整数

    p=a; //指针变量p重新指向a[0]

    sort(p,10);   //调用sort函数

    for(p=a,i=0;i<10;i++)

    { printf("%d ",*p); //输出排序后的10个数组元素

    p++;

    }

    printf(" ");

    return 0;

    }

    void sort(int x[],int n)//x是形参数组名

    { int i,j,k,t;

    for(i=0;i<n-1;i++)

    { k=i;

    for(j=i+1;j<n;j++)

    if(x[j]>x[k]) k=j;

    if(k!=i)

    { t=x[i]; x[i]=x[k]; x[k]=t;}

    }

    }

    //形参是指针

    #include <stdio.h>

    int main()

    { void sort(int x[],int n); //sort函数声明

    int i,*p,a[10];

    p=a;     //指针变量p指向a[0]

    printf("please enter 10 integer numbers:");

    for(i=0;i<10;i++)

    scanf("%d",p++); //输入10个整数

    p=a; //指针变量p重新指向a[0]

    sort(p,10);   //调用sort函数

    for(p=a,i=0;i<10;i++)

    { printf("%d ",*p); //输出排序后的10个数组元素

    p++;

    }

    printf(" ");

    return 0;

    }

    void sort(int *x,int n) //形参x是指针变量

    { int i,j,k,t;

    for(i=0;i<n-1;i++)

    { k=i;

    for(j=i+1;j<n;j++)

    if(*(x+j)>*(x+k)) k=j; //*(x+j)就是x[j],其他亦然

    if(k!=i)

    { t=*(x+i); *(x+i)=*(x+k); *(x+k)=t;}

    }

    }

    8.3.5 通过指针引用多维数组

    注:这章节了解即可,如果想提升自己,不仅需要理解还需要会使用。

    理解以下这张图便可了解多维数组:

     

    说明:我们在这里说明一下,在计算机底层是不存在二维数组这种存储方式的,都是以一维数组为基础逻辑化出二维数组,而一维数组不仅是逻辑化更是物理上也是可以实现的。总的来说,二维数组是一维数组的逻辑化,本质上二维数组还是一维数组。只是为了方便人们理解,才把二维数组分为行和列的方式显示。

    如果用一个指针变量pt来指向此一维数组:

    int (*pt)[4];

    //表示pt指向由4个整型元素组成的一维数组,此时指针变量pt的基类型是由4个整型元素组成的一维数组

    例:输出二维数组的有关数据(地址和元素的值)。

    #include <stdio.h>

    int main()

    { int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};

    printf("%d,%d ",a,*a);    //0行起始地址和0行0列元素地址

    printf("%d,%d ",a[0],*(a+0));   //0行0列元素地址

    printf("%d,%d ",&a[0],&a[0][0]);  //0行起始地址和0行0列元素地址

    printf("%d,%d ",a[1],a+1);   //1行0列元素地址和1行起始地址

    printf("%d,%d ",&a[1][0],*(a+1)+0); //1行0列元素地址

    printf("%d,%d ",a[2],*(a+2));   //2行0列元素地址

    printf("%d,%d ",&a[2],a+2);   //2行起始地址

    printf("%d,%d ",a[1][0],*(*(a+1)+0)); //1行0列元素的值

    printf("%d,%d ",*a[2],*(*(a+2)+0)); //2行0列元素的值

    return 0;

    }

    结果如下:

     

    说明:要注意看懂以上的格式,区分各种形式的联系和区别。如a和*a在输出上是等价的

    例:有一个3×4的二维数组,要求用指向元素的指针变量输出二维数组各元素的值。

    #include <stdio.h>

    int main()

    { int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};

    int *p;       //p是int *型指针变量

    for(p=a[0];p<a[0]+12;p++)   //使p依次指向下一个元素

    { if((p-a[0])%4==0) printf(" "); //p移动4次后换行

                  printf("%4d",*p);                           //输出p指向的元素的值

    }

    printf(" ");

    return 0;

    }

    运行结果:

    例:输出二维数组任一行任一列元素的值。

    #include <stdio.h>

    int main()

    { int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};  //定义二维数组a并初始化

    int (*p)[4],i,j;   //指针变量p指向包含4个整型元素的一维数组

    p=a;     //p指向二维数组的0行

    printf("please enter row and colum:");

    scanf("%d,%d",&i,&j); //输入要求输出的元素的行列号

    printf("a[%d,%d]=%d ",i,j,*(*(p+i)+j));   //输出a[i][j]的值

    return 0;

    }

    #include <stdio.h>

    int main()

    { int a[4]={1,3,5,7};  //定义一维数组a,包含4个元素

    int (*p)[4];   //定义指向包含4个元素的一维数组的指针变量中

    p=&a;    //使p指向一维数组

    printf("%d ",(*p)[3]); //输出a[3],输出整数7

    return 0;

    }

    比较:

    ① int a[4];(a有4个元素,每个元素为整型)

    ② int (*p)[4];

    第②种形式表示(*p)有4个元素,每个元素为整型。也就是p所指的对象是有4个整型元素的数组,即p是指向一维数组的指针,见图8.24。应该记住,此时p只能指向一个包含4个元素的一维数组,不能指向一维数组中的某一元素。p的值是该一维数组的起始地址。虽然这个地址(指纯地址)与该一维数组首元素的地址相同,但它们的基类型是不同的。

    指向由m个元素组成的一维数组的指针变量

    要注意指针变量的类型,从“int (*p)[4];”可以看到,p的类型不是int *型,而是int (*)[4]型,p被定义为指向一维整型数组的指针变量,一维数组有4个元素,因此p的基类型是一维数组,其长度是16字节。“*(p+2)+3”括号中的2是以p的基类型(一维整型数组)的长度为单位的,即p每加1,地址就增加16个字节(4个元素,每个元素4个字节),而“*(p+2)+3”括号外的数字3,不是以p的基类型的长度为单位的。由于经过*(p+2)的运算,得到a[2],即&a[2][0],它已经转化为指向列元素的指针了,因此加3是以元素的长度为单位的,加3就是加(3×4)个字节。虽然p+2和*(p+2)具有相同的值,但由于它们所指向的对象的长度不同,因此(p+2)+3和*(p+2)+3的值就不相同了。

    一维数组名可以作为函数参数,多维数组名也可作函数参数。

    用指针变量作形参,以接受实参数组名传递来的地址。可以有两种方法:

    ① 用指向变量的指针变量;

    ② 用指向一维数组的指针变量。

    例8:有一个班,3个学生,各学4门课,计算总平均分数以及第n个学生的成绩。

    #include <stdio.h>

    int main()

    { void average(float *p,int n);

    void search(float (*p)[4],int n);

    float score[3][4]={{65,67,70,60},{80,87,90,81},{90,99,100,98}};

    average(*score,12);  //求12个分数的平均分

    search(score,2);   //求序号为2的学生的成绩

    return 0;

    }

    void average(float *p,int n)  //定义求平均成绩的函数

    { float *p_end;

    float sum=0,aver;

    p_end=p+n-1;

    //n的值为12时,p_end的值是p+11,指向最后一个元素

    for(;p<=p_end;p++)

    sum=sum+(*p);

    aver=sum/n;

    printf("average=%5.2f ",aver);

    }

    void search(float (*p)[4],int n)

    //p是指向具有4个元素的一维数组的指针

    { int i;

    printf("The score of No.%d are: ",n);

    for(i=0;i<4;i++)

                  printf("%5.2f ",*(*(p+n)+i));

    printf(" ");

    }

    运行结果:

     

    注:实参与形参如果是指针类型,应当注意它们的基类型必须一致。不应把int *型的指针(即数组元素的地址)传给int (*)[4] 型(指向一维数组)的指针变量,反之亦然。

    例:在上例的基础上,查找有一门以上课程不及格的学生,输出他们的全部课程的成绩。

    #include <stdio.h>

    int main()

    { void search(float (*p)[4],int n); //函数声明

    float score[3][4]={{65,57,70,60},{58,87,90,81},{90,99,100,98}};

    //定义二维数组函数score

    search(score,3);    //调用search函数

    return 0;

    }

    void search(float (*p)[4],int n)

    //形参p是指向包含4个float型元素的一维数组的指针变量

    { int i,j,flag;

    for(j=0;j<n;j++)

    { flag=0;

    for(i=0;i<4;i++)

    if(*(*(p+j)+i)<60) flag=1;

    //*(*(p+j)+i)就是score[j][i]

    if(flag==1)

    { printf("No.%d fails,his scores are: ",j+1);

    for(i=0;i<4;i++)

    printf("%5.1f ",*(*(p+j)+i));

    //输出*(*(p+j)+i)就是输出score[j][i]的值

    printf(" ");

    }

    }

    }

    运行结果:

     

    8.4 通过指针引用字符串

    8.4.1 字符串的引用方式

    (1)用字符数组存放一个字符串,可以通过数组名和下标引用字符串中一个字符,也可以通过数组名和格式声明“%s”输出该字符串。

    (2)用字符指针变量指向一个字符串常量,通过字符指针变量引用字符串常量。

    例:定义一个字符数组,在其中存放字符串″I love China!″,输出该字符串和第8个字符。

    #include <stdio.h>

    int main()

    { char string[]="I love China!"; //定义字符数组sting

    printf("%s ",string);  //用%s格式声明输出string,可以输出整个字符串

           printf("%c ",string[7]);         //用%c格式输出一个字符数组元素

    return 0;

    }

    运行结果:

     

    例:通过字符指针变量输出一个字符串。

    #include <stdio.h>

    int main()

    { char *string="I love China!"; //定义字符指针变量string并初始化

    printf("%s ",string);  //输出字符串

    return 0;

    }

    运行结果:

     

    在C语言中只有字符变量,没有字符串变量。

    char *string="I love China!";

    等价于

    char *string;  //定义一个char *型变量

    string=″I love China!″;

    //把字符串第1个元素的地址赋给字符指针变量string

    注:string被定义为一个指针变量,基类型为字符型。它只能指向一个字符类型数据,而不能同时指向多个字符数据,更不是把″I love China!″这些字符存放到string中(指针变量只能存放地址),也不是把字符串赋给*string。只是把″I love China!″的第1个字符的地址赋给指针变量string。

    可以对指针变量进行再赋值,string=″I am a student.″;  //对指针变量string重新赋值

    可以通过字符指针变量输出它所指向的字符串,printf(″%s ″,string); //%s可对字符串进行整体的输入输出

    说明:%s是输出字符串时所用的格式符,在输出项中给出字符指针变量名string,则系统会输出string所指向的字符串第1个字符,然后自动使string加1,使之指向下一个字符,再输出该字符……如此直到遇到字符串结束标志′′为止。注意,在内存中,字符串的最后被自动加了一个′′。

    例:将字符串a复制为字符串b,然后输出字符串b。

    //用数组的方式输出

    #include <stdio.h>

    int main()

    { char a[]="I am a student.",b[20]; //定义字符数组

    int i;

    for(i=0;*(a+i)!='';i++)

    *(b+i)=*(a+i);  //将a[i]的值赋给b[i]

    *(b+i)='';    //在b数组的有效字符之后加''

    printf("string a is:%s ",a);//输出a数组中全部有效字符

    printf("string b is:");

    for(i=0;b[i]!='';i++)

    printf("%c",b[i]); //逐个输出b数组中全部有效字符

    printf(" ");

    return 0;

    }

    //用指针变量

    #include <stdio.h>

    int main()

    { char a[]="I am a boy.",b[20],*p1,*p2;

    p1=a;p2=b;

    //p1,p2分别指向a数组和b数组中的第一个元素

    for(;*p1!='';p1++,p2++)  //p1,p2每次自加1

    *p2=*p1;

    //将p1所指向的元素的值赋给p2所指向的元素

    *p2='';   //在复制完全部有效字符后加''

    printf("string a is:%s ",a); //输出a数组中的字符

    printf("string b is:%s ",b); //输出b数组中的字符

    return 0;

    }

    8.4.2 字符指针作函数参数

    例:用函数调用实现字符串的复制

    (1) 用字符数组名作为函数参数

    #include <stdio.h>

    int main()

    { void copy_string(char from[], char to[]);

    char a[]="I am a teacher.";

    char b[]="You are a student.";

    printf("string a=%s string b=%s ",a,b);

    printf("copy string a to string b: ");

    copy_string(a,b);  //用字符数组名作为函数实参

    printf(" string a=%s string b=%s ",a,b);

    return 0;

    }

    void copy_string(char from[], char to[])    //形参为字符数组

    { int i=0;

    while(from[i]!='')

    { to[i]=from[i]; i++;}

    to[i]='';

    }

    运行结果:

     

    (2) 用字符型指针变量作实参

    #include <stdio.h>

    int main()

    { void copy_string(char from[], char to[]); //函数声明

    char a[]="I am a teacher.";  //定义字符数组a并初始化

    char b[]="You are a student."; //定义字符数组b并初始化

           char *from=a,*to=b; //from指向a数组首元素,to指向b数组首元素

    printf("string a=%s string b=%s ",a,b);

    printf("copy string a to string b: ");

    copy_string(from,to); //实参为字符指针变量

    printf(" string a=%s string b=%s ",a,b);

    return 0;

    }

    void copy_string(char from[], char to[])   //形参为字符数组

    { int i=0;

    while(from[i]!='')

    { to[i]=from[i]; i++;}

    to[i]='';

    }

    运行结果:

     

    说明:指针变量from的值是a数组首元素的地址,指针变量to的值是b数组首元素的地址。它们作为实参,把a数组首元素的地址和b数组首元素的地址传递给形参数组名from和to(它们实质上也是指针变量)。其他与程序(1)相同。

    (3) 用字符指针变量作形参和实参

    #include <stdio.h>

    int main()

    { void copy_string(char *from, char *to);

    char *a="I am a teacher.";  //a是char*型指针变量

    char b[]="You are a student."; //b是字符数组

    char *p=b;  //使指针变量p指向b数组首元素

    printf("string a=%s string b=%s ",a,b); //输出a串和b串

    printf("copy string a to string b: ");

    copy_string(a,p); //调用copy_string函数,实参为指针变量

    printf(" string a=%s string b=%s ",a,b); //输出改变后的a串和b串

    return 0;

    }

    void copy_string(char *from, char *to) //定义函数,形参为字符指针变量

    { for(;*from!='';from++,to++)

    { *to=*from;}

    *to='';

    }

    运行结果:

     

    函数void copy_string(char *from, char *to)还有以下等价几种,只需记住一种即可,其它做了解。最好的情况当然是全都会用。

    void copy_string(char *from, char *to)

    { for(;(*to++=* from++)!='';);

    //或for(;*to++=* from++;);

    }

    void copy_string(char *from, char *to)

    { while((*to=*from)!='')

    //或while(*to=*from)

    { to++; from++;}

    }

    void copy_string(char *from, char *to)

    { while(*from!='')

    //或while(*from) ,因为''的ASCII码为0

    *to++=*from++;

    *to='';

    }

    void copy_string(char *from, char *to)

    { while((*to++=*from++)!='');

    //或while(*to++=*from++)

    }

    void copy_string(char from[],char to[])

    { char *p1, *p2;

    p1=from;p2=to;

    while((*p2++=*p1++)!='');

    }

    字符指针作为函数参数时,实参与形参的类型有以下几种对应关系:

     

    8.4.3 使用字符指针变量和字符数组的比较

     

    例:改变指针变量的值。

    #include <stdio.h>

    int main()

    { char *a="I love China!";

    a=a+7;   //改变指针变量的值,即改变指针变量的指向

    printf("%s ",a); //输出从a指向的字符开始的字符串

    return 0;

    }

    运行结果:

     

    #include <stdio.h>

    int main()

    { char str[]={"I love China!"};

    str=str+7;

    printf("%s ",str);

    return 0;

    }

    说明:

    (1)指针变量的值是可以改变的,而字符数组名代表一个固定的值(数组首元素的地址),不能改变。

    (2)指针变量a的值是可以变化的。printf函数输出字符串时,从指针变量a当时所指向的元素开始,逐个输出各个字符,直到遇''为止。而数组名虽然代表地址,但它是常量,它的值是不能改变的。

    8.5 指向函数的指针

    8.5.1 什么是函数指针

    如果在程序中定义了一个函数,在编译时会把函数的源代码转换为可执行代码并分配一段存储空间。这段内存空间有一个起始地址,也称为函数的入口地址。每次调用函数时都从该地址入口开始执行此段函数代码。

    函数名就是函数的指针,它代表函数的起始地址。

    可以定义一个指向函数的指针变量,用来存放某一函数的起始地址,这就意味着此指针变量指向该函数。例如: int (*p)(int,int);

    定义p是一个指向函数的指针变量,它可以指向函数类型为整型且有两个整型参数的函数。此时,指针变量p的类型用int (*)(int,int)表示。

    8.5.2 用函数指针变量调用函数

    例:用函数求整数a和b中的大者

    //(1)通过函数名调用函数

    #include <stdio.h>

    int main()

    { int max(int,int); //函数声明

    int a,b,c;

    printf("please enter a and b:");

    scanf("%d,%d",&a,&b);

    c=max(a,b);  //通过函数名调用max函数

    printf("a=%d b=%d max=%d ",a,b,c);

    return 0;

    }

    int max(int x,int y)  //定义max函数

    { int z;

    if(x>y) z=x;

    else z=y;

    return(z);

    }

    (2) 通过指针变量调用它所指向的函数

    #include <stdio.h>

    int main()

    { int max(int,int); //函数声明

    int (*p)(int,int); //定义指向函数的指针变量p

    int a,b,c;

    p=max;   //使p指向max函数

    printf("please enter a and b:");

    scanf("%d,%d",&a,&b);

    c=(*p)(a,b);  //通过指针变量调用max函数

    printf("a=%d b=%d max=%d ",a,b,c);

    return 0;

    }

    int max(int x,int y)  //定义max函数

    { int z;

    if(x>y)z=x;

    else z=y;

    return(z);

    }

    运行结果:

     

    8.5.3 怎样定义和使用指向函数的指针变量

    定义指向函数的指针变量的一般形式为:

    类型名 (*指针变量名)(函数参数表列)

    如:

    int (*p)(int,int);

    说明:

    (1) 定义指向函数的指针变量,并不意味着这个指针变量可以指向任何函数,它只能指向在定义时指定的类型的函数。

    (2)  如果要用指针调用函数,必须先使指针变量指向该函数。

    (3) 在给函数指针变量赋值时,只须给出函数名而不必给出参数。

    (4) 用函数指针变量调用函数时,只须将(*p)代替函数名即可(p为指针变量名),在(*p)之后的括号中根据需要写上实参。

    (5) 对指向函数的指针变量不能进行算术运算,如p+n,p++,p--等运算是无意义的。

    (6) 用函数名调用函数,只能调用所指定的一个函数,而通过指针变量调用函数比较灵活,可以根据不同情况先后调用不同的函数。

    例:输入两个整数,然后让用户选择1或2,选1时调用max函数,输出二者中的大数,选2时调用min函数,输出二者中的小数。

    #include <stdio.h>

    int main()

    { int max(int,int); //函数声明

    int min(int x,int y); //函数声明

    int (*p)(int,int); //定义指向函数的指针变量

    int a,b,c,n;

    printf("please enter a and b:");

    scanf("%d,%d",&a,&b);

    printf("please choose 1 or 2:");

    scanf("%d",&n); //输入1戓2

    if(n==1) p=max; //如输入1,使p指向max函数

    else if (n==2) p=min; //如输入2,使p指向min函数

    c=(*p)(a,b);  //调用p指向的函数

    printf("a=%d,b=%d ",a,b);

    if(n==1) printf("max=%d ",c);

    else printf("min=%d ",c);

    return 0;

    }

    int max(int x,int y)

    { int z;

    if(x>y) z=x;

    else z=y;

    return(z);

    }

    int min(int x,int y)

    { int z;

    if(x<y) z=x;

    else z=y;

    return(z);

    }

    运行结果:

     

    OR

     

    8.5.4 用指向函数的指针作函数参数

     

    例:有两个整数a和b,由用户输入1,2或3。如输入1,程序就给出a和b中的大者,输入2,就给出a和b中的小者,输入3,则求a与b之和。

    #include <stdio.h>

    int main()

    { int fun(int x,int y, int (*p)(int,int)); //fun函数声明

           int max(int,int);                //max函数声明

    int min(int,int);   //min函数声明

    int add(int,int);   //add函数声明

    int a=34,b=-21,n;

    printf("please choose 1,2 or 3:");

    scanf("%d",&n);   //输入1,2或3之一

    if(n==1) fun(a,b,max);  //输入1时调用max函数

    else if(n==2) fun(a,b,min); //输入2时调用min函数

    else if(n==3) fun(a,b,add); //输入3时调用add函数

    return 0;

    }

    int fun(int x,int y,int (*p)(int,int)) //定义fun函数

    {      int result;

    result=(*p)(x,y);

           printf("%d ",result);             //输出结果

    }

    int max(int x,int y) //定义max函数

    { int z;

    if(x>y) z=x;

    else z=y;

    printf("max=" );

           return(z);            //返回值是两数中的大者

    }

    int min(int x,int y) //定义min函数

    { int z;

    if(x<y) z=x;

    else z=y;

    printf("min=");

    return(z);  //返回值是两数中的小者

    }

    int add(int x,int y) //定义add函数

    { int z;

    z=x+y;

    printf("sum=");

    return(z);  //返回值是两数之和

    }

    运行结果:不同选择有不同的答案

     

    8.6 返回指针值的函数

    定义返回指针值的函数的一般形式为:

    类型名 *函数名(参数表列);

    解释:

    一个函数可以返回一个整型值、字符值、实型值等,也可以返回指针型的数据,即地址。其概念与以前类似,只是返回的值的类型是指针类型而已。如:

    int *a(int x,int y);

    a是函数名,调用它以后能得到一个int*型(指向整型数据)的指针,即整型数据的地址。x和y是函数a的形参,为整型。

    注:在“*a”两侧没有括号,在a的两侧分别为*运算符和()运算符。而()优先级高于*,因此a先与()结合,显然这是函数形式。这个函数前面有一个*,表示此函数是指针型函数(函数值是指针)。最前面的int表示返回的指针指向整型变量。

    例:有a个学生,每个学生有b门课程的成绩。要求在用户输入学生序号以后,能输出该学生的全部成绩。用指针函数来实现。

    #include <stdio.h>

    int main()

    { float score[][4]={{60,70,80,90},{56,89,67,88},{34,78,90,66}};

    //定义数组,存放成绩

    float *search(float (*pointer)[4],int n); //函数声明

    float *p;

    int i,k;

    printf("enter the number of student:");

    scanf("%d",&k); //输入要找的学生的序号

    printf("The scores of No.%d are: ",k);

    p=search(score,k); //调用search函数,返回score[k][0]的地址

    for(i=0;i<4;i++)

    printf("%5.2f ",*(p+i)); //输出score[k][0]~score[k][3]的值

    printf(" ");

    return 0;

    }

    float *search(float (*pointer)[4],int n)

    //形参pointer是指向一维数组的指针变量

    { float *pt;

    pt=*(pointer+n); //pt的值是&score[k][0]

    return(pt);

    }

    运行结果:

     

    例:对上例题,找出其中有不及格的课程的学生及其学生号。

    #include <stdio.h>

    int main()

    { float score[][4]={{60,70,80,90},{56,89,67,88},{34,78,90,66}};

    //定义数组,存放成绩

    float *search(float (*pointer)[4]); //函数声明

    float *p;

    int i,j;

    for(i=0;i<3;i++)    //循环3次

    { p=search(score+i);

    //调用search函数,如有不及格返回score[i][0]的地址,否则返回NULL

    if(p==*(score+i))

    //如果返回的是score[i][0]的地址,表示p的值不是NULL

    { printf("No.%d score:",i);

    for(j=0;j<4;j++)

    printf("%5.2f  ",*(p+j));

    //输出score[i][0]~score[i][3]的值

    printf(" ");

                  }

    }

           return 0;

    }

    float *search(float (*pointer)[4])

    //定义函数,形参pointer是指向一维数组的指针变量

    { int i=0;

    float *pt;

    pt=NULL; //先使pt的值为NULL

    for(;i<4;i++)

    if(*(*pointer+i)<60) pt=*pointer;

                  //如果有不及格课程,使pt指向score[i][0]

    return(pt);

    }

    运行结果:

    8.7 指针数组和多重指针

    8.7.1 什么是指针数组

    定义一维指针数组的一般形式为

    类型名 *数组名[数组长度];

    如:

    int *p[4];

    一个数组,若其元素均为指针类型数据,称为指针数组,也就是说,指针数组中的每一个元素都存放一个地址,相当于一个指针变量。

    指针数组比较适合用来指向若干个字符串,使字符串处理更加方便灵活。

    例:将若干字符串按字母顺序(由小到大)输出。

     

    #include <stdio.h>

    #include <string.h>

    int main()

    { void sort(char *name[],int n);  //函数声明

    void print(char *name[],int n); //函数声明

    char *name[]={"Follow me","BASIC",

           "Great Wall","FORTRAN","Computer design"};

    //定义指针数组,它的元素分别指向5个字符串

    int n=5;

           sort(name,n);     //调用sort函数,对字符串排序

    print(name,n); //调用print函数,输出字符串

    return 0;

    }

    void sort(char *name[],int n)   //定义sort函数

    { char *temp;

    int i,j,k;

    for(i=0;i<n-1;i++)   //用选择法排序

    { k=i;

    for(j=i+1;j<n;j++)

    if(strcmp(name[k],name[j])>0) k=j;

    if(k!=i)

    { temp=name[i]; name[i]=name[k]; name[k]=temp;}

    }

    }

    void print(char *name[],int n) //定义print函数

    { int i;

    for(i=0;i<n;i++)

    printf("%s ",name[i]);

    //按指针数组元素的顺序输出它们所指向的字符串

    }

    运行结果:

     

    在了解了指针数组的基础上,需要了解指向指针数据的指针变量,简称为指向指针的指针。

     

    例:使用指向指针数据的指针变量。

    #include <stdio.h>

    int main()

    { char *name[]={"Follow me","BASIC","Great Wall","FORTRAN","Computer design"};

    char **p;

    int i;

    for(i=0;i<5;i++)

    { p=name+i;

    printf("%s ",*p);

    }

    return 0;

    }

    运行结果:

     

    说明:

    (1) p是指向char*型数据的指针变量,即指向指针的指针。在第1次执行for循环体时,赋值语句“p=name+i;”使p指向name数组的0号元素name[0],*p是name[0]的值,即第1个字符串首字符的地址,用printf函数输出第1个字符串(格式符为%s)。执行5次循环体,依次输出5个字符串。

    (2) 指针数组的元素也可以不指向字符串,而指向整型数据或实型数据等。

    例:有一个指针数组,其元素分别指向一个整型数组的元素,用指向指针数据的指针变量,输出整型数组各元素的值。

    #include <stdio.h>

    int main()

    { int a[5]={1,3,5,7,9};

    int *num[5]={&a[0],&a[1],&a[2],&a[3],&a[4]};

    int **p,i;    //p是指向指针型数据的指针变量

    p=num;    //使p指向num[0]

    for(i=0;i<5;i++)

    { printf("%d ",**p);

    p++;

    }

    printf(" ");

    return 0;

    }

    运行结果:

     

    8.7.3 指针数组作main函数的形参

     

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

    { while(argc>1)

    { ++argv;

    printf("%s ", *argv);

    --argc;

    }

    return 0;

    }

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

    { while(argc-->1)

    printf("%s ", *++argv);

    return 0;

    }.

     

    8.8 动态内存分配与指向它的指针变量

    8.8.1 什么是内存的动态分配

     

    8.8.2 怎样建立内存的动态分配

    1. 用malloc函数开辟动态存储区

    函数原型为

    void *malloc(unsigned int size);

    作用:在内存的动态存储区中分配一个长度为size的连续空间。形参size的类型定为无符号整型(不允许为负数)。此函数的值(即“返回值”)是所分配区域的第一个字节的地址,或者说,此函数是一个指针型函数,返回的指针指向该分配域的第一个字节。如:

    malloc(100);              //开辟100字节的临时分配域,函数值为其第1个字节的地址

    指针的基类型为void,即不指向任何类型的数据,只提供一个纯地址。如果此函数未能成功地执行(例如内存空间不足),则返回空指针(NULL)。

    2.用calloc函数开辟动态存储区

    函数原型为

    void *calloc(unsigned n, unsigned size);

    作用:在内存的动态存储区中分配n个长度为size的连续空间,这个空间一般比较大,足以保存一个数组。

    p=calloc(50,4);  //开辟50×4个字节的临时分配域,把首地址赋给指针变量p

    用calloc函数可以为一维数组开辟动态存储空间,n为数组元素个数,每个元素长度为size。这就是动态数组。函数返回指向所分配域的第一个字节的指针;如果分配不成功,返回NULL。

    3.用realloc函数重新分配动态存储区

    函数原型为

    void *realloc(void *p,unsigned int size);

    作用:如果已经通过malloc函数或calloc函数获得了动态空间,想改变其大小,可以用realloc函数重新分配。

    realloc(p,50); //将p所指向的已分配的动态空间改为50字节

    用realloc函数将p所指向的动态空间的大小改变为size。p的值不变。如果重分配不成功,返回NULL。

    4.用free函数释放动态存储区

    函数原型为

    void free(void *p);

    作用:释放指针变量p所指向的动态空间,使这部分空间能重新被其他变量使用。p应是最近一次调用calloc或malloc函数时得到的函数返回值。

    free(p);  //释放指针变量p所指向的已分配的动态空间

    free函数无返回值。

    说明:以上4个函数的声明在stdlib.h头文件中,在用到这些函数时应当用“#include <stdlib.h>”指令把stdlib.h头文件包含到程序文件中。

    8.8.3 void指针类型

    C 99允许使用基类型为void的指针类型。可以定义一个基类型为void的指针变量(即void*型变量),它不指向任何类型的数据。在将它的值赋给另一指针变量时由系统对它进行类型转换,使之适合于被赋值的变量的类型。

    int *pt;

    pt=(int *)mcaloc(100); //mcaloc(100)是void *型,把它转换为int *型

    注:不要把“指向void类型”理解为能指向“任何的类型”的数据,而应理解为“指向空类型”或“不指向确定的类型”的数据。

    例:建立动态数组,输入5个学生的成绩,另外用一个函放数检查其中有无低于60分的,输出不合格的成绩。

    #include <stdio.h>

    #include <stdlib.h>    //程序中用了malloc函数,应包含stdlib.h

    int main()

    { void check(int *);    //函数声明

    int *p1,i;      //p1是int型指针

    p1=(int *)malloc(5*sizeof(int)); //开辟动态内存区,将地址转换成int *型,然后放在p1中

           for(i=0;i<5;i++)

                  scanf("%d",p1+i);                   //输入5个学生的成绩

    check(p1);     //调用check函数

    return 0;

    }

    void check(int *p)     //定义check函数,形参是int*指针

    { int i;

    printf("They are fail:");

    for(i=0;i<5;i++)

                  if(p[i]<60) printf("%d ",p[i]);        //输出不合格的成绩

    printf(" ");

    }

    运行结果:

     

    有关指针的小结

     

  • 相关阅读:
    删库了一定要跑路吗?爱情 36 技之记忆重生!
    程序员和他的朋友们!
    聊起 BigTable,让你不再胆怯
    [Ant Design] Warning: Instance created by `useForm` is not connected to any Form element. Forget to pass `form` prop?
    Git 常用命令
    Git 名词解释
    js中void 0和undefined的区别
    js运算符优先级
    搭建React项目(低配版)
    mac常用shell指令笔记
  • 原文地址:https://www.cnblogs.com/CSAH/p/11006938.html
Copyright © 2011-2022 走看看