第八章 善于使用指针

什么是指针?

1、通过地址能找到所需的变量单元,地址指向该变量单元,地址形象化地称为“指针”;

2、直接访问和简接访问

  直接访问:通过变量名找到对应存储单元;

  简接访问:将变量a的地址存储到另一个变量中,通过这个变量找到a的地址,访问a变量;

 

指针变量

3、存放地址的变量:指针变量;

a_pointer=&a;
a_pointer为指针变量;&为取地址符,指针变量的值(假设a地址为2000)为地址(即指针);
  区别:指针是一个地址,而指针变量是一个存放地址的变量;

4、使用指针变量访问变量的例子

 1)通过指针访问整型变量:
#include <stdio.h>
int main()
{
    int a,b;
    int *pointer_1,*pointer_2;                    // 定义两个指针变量
    a=100;b=10;    
    pointer_1=&a;                                // 给指针变量取a的地址
    pointer_2=&b;                                // 给指针变量取b的地址
    printf("a=%d,b=%d\n",a,b);
    printf("*pointer_1=%d,*pointer_1=%d\n\n",*pointer_1,*pointer_2);    // *表示取a,b变量的值
        //如果后面输出没有“*”则是取到了a,b的地址,*表示取值符
}
View Code
5、怎么定义指针变量
// 基类型    *指针变量名;
  int *pointer_1,*pointer_2;
float *pointer_3;        // pointer_3是指向float型变量的指针变量
char *pointer_4;        // pointer_4是指向字符型变量的指针变量
int *pointer_1=&a,*pointer_2=&b;                // 定义指针变量同时初始化指向a,b

  注意点:1)为什么要指定基类型?

        ① 不同的类型的数据在内存中的字节数是不同的(如:整型占4个字节、双精度数据占8字节)

        ② 指针的运算(加、减)会有应用到,例如指针移动位置+1或者-1,意思是地址值+4,地址值-4,而双精度则是+8或者-8。

      2)赋值给指针变量的数据只能是变量地址,而且该变量的类型要与指针变量的基类型相同。

6、怎么引用指针变量

  1)给指针变量赋值;

p=&a;

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

p=&a;
printf("%d",*p);
*p=1;        // 将1赋给p所指向的变量,如果指向a,则a为1

  3)引用指针变量的值。

printf("%o",p);    // 以八进制输出p的值,如果p指向a,则是a的地址&a

 

  小结: (1)&取地址运算符,&a是变量a的地址;

      (2)*指针运算符(简接访问运算符),*p是指针变量p指向的对象的值。

  例子:输入a和b两个整型,按先大后小的顺序输出a和b

#include <stdio.h>
int main()
{
    int *p1,*p2,*p,a,b;                    // 定义两个整型变量和三个整型指针变量
    scanf("%d,%d",&a,&b);                // 输入两个整型的变量给a和b
    p1=&a;p2=&b;                        // p1指向a的地址,p2指向b的地址
    if(a<b)
    {
        p=p1;p1=p2;p2=p;                // 交换指向
        printf("a=%d,b=%d\n",a,b);        
        printf("Max=%d,Min=%d\n",*p1,*p2);    
        return 0;
    }
}
View Code

  ps:这里已经体现到a和b的值保持不变,实际上是改变了p1和p2的值,所以交换的不是整型变量的值,而是指针变量的值。

  注意点:纯地址和类型(地址的基类型),若a和b的纯地址相同(假设 2000),但是因为其类型不同,所以&a,&b是两个不同的地址,定义时一定要注意。

 

7、指针变量作为函数参数

  函数参数可以是 整型、浮点型、字符型,当然可以指针类型啦!作用是将一个变量的地址 传送到另一个函数中。

  例子1:比较a和b的大小,先大后小输出,用函数处理和用指针变量作函数参数。

  思路:将指向两个变量的指针变量(内放两个变量的地址)作为实参传递给形参,在形参中交换两个变量的值。

#include <stdio.h>
int main()
{
    void swap(int *p1,int *p2);                // 对swap函数进行声明
    int a,b;
    int *pointer_1,*pointer_2;                // 定义两个指针变量
    scanf("%d,%d",&a,&b);
    pointer_1=&a;pointer_2=&b;                // 分别指向a和b的地址
    if(a<b)
    {
        swap(pointer_1,pointer_2);            // 如果a<b,调用swap函数
    }
    printf("max=%d\nmin=%d\n",a,b);
    return 0;

}

void swap(int *p1,int *p2)
{
    int temp;
    temp=*p1;                                // *p1和*p2互换
    *p1=*p2;                                
    *p2=temp;
}
View Code

  调用函数时,将实参变量的值传送为形参变量,采用的依然是“值传递” 方式,p1和poiner_1都是指向变量a,p2和poiner_2都是指向变量b,函数执行完后p1和p2已经被释放了。但是main函数中的a和b是已经交换过的值。本例采用的是交换a和b的值,和p1和p2的值不变,与上面相反,是通过改变形参的值然后传回到实参函数中,交换完之后形参就释放了。

  例子2:企图通过改变 指针形参的值 来改变 指针实参的值

#include <stdio.h>
int main()
{
    void swap(int *p1,int *p2);                
    int a,b;
    int *pointer_1,*pointer_2;                
    scanf("%d,%d",&a,&b);
    pointer_1=&a;pointer_2=&b;                
    if(a<b)
    {
        swap(pointer_1,pointer_2);            
    }
    printf("max=%d\nmin=%d\n",a,b);
    return 0;

}

void swap(int *p1,int *p2)                    // 形参是指针变量
{
    int *p;
    p=p1;                                    // 交换p1和p2的指向
    p1=p2;                                
    p2=p;
}
View Code

  这种是错误的,因为C语言形参和实参之间的数据传递都是单向的 “值传递 ” 方式不可能通过执行调用函数来改变实参的 指针变量的值(地址),但是可以通过改变实参指针变量所指变量的值(具体数据)

  所以,回归到了例子1的思想,只能是改变形参的值来传回到实参函数中指针变量pointer具体指向的值,下面更好分析一点。

#include <stdio.h>
int main()
{
    void swap(int *p1,int *p2);                
    int a,b;
    int *pointer_1,*pointer_2;    
    printf("Please enter a b\n");
    scanf("%d,%d",&a,&b);
    pointer_1=&a;pointer_2=&b;                
    if(a<b)
    {
        swap(pointer_1,pointer_2);            // 如果a<b,调用swap函数
    }
    printf("a=%d\nb=%d\n",a,b);
    printf("max=%d\nmin=%d\n",a,b);
    return 0;

}

void swap(int *p1,int *p2)
{
    int temp;
    temp=*p1;                                // *p1和*p2互换
    *p1=*p2;                                
    *p2=temp;
    printf("p1的值:%d\n",*p1);
    printf("p2的值:%d\n",*p2);
    printf("p1的地址:%o\n",p1);
    printf("p2的地址:%o\n",p2);

}
View Code

  例子3:输入3个整数a,b,c,要求按大小顺序将它们输出。用函数实现改变这3个变量的值。

#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:\n");
    scanf("%d,%d,%d",&a,&b,&c);
    p1=&a;p2=&b;p3=&c;        
    exchange(p1,p2,p3);                                    // 指针变量作实参,调用exchange函数
    printf("a=%d\nb=%d\nc=%d\n",a,b,c);
    return 0;
}

void exchange(int *q1,int *q2,int *q3)            // 定义将3个变量的值交换的函数
{    void swap(int *pt1,int *pt2);                // 对swap函数声明
    if(*q1<*q2)    swap(q1,q2);                    // 取指针变量q1(形参)从实参传过来的地址的值(即实参p1指向a的地址的值)与b比较,如果b大那么交换位置
    if(*q1<*q3)    swap(q1,q3);
    if(*q2<*q3)    swap(q2,q3);
}    

void swap(int *pt1,int *pt2)
{
    int tmp;
    tmp=*pt1;                                // *p1和*p2互换
    *pt1=*pt2;                                
    *pt2=tmp;

}
View Code

 

通过指针引用数组

8、数组元素的指针是数组元素的地址

int a[10]    //定义a为包含10个整型数据的数组
int *p;        // 定义p为指针整型变量的指针变量
p=&a[0];    // 把a[0]元素的地址赋值给指针变量p        

8、数组名代表数组中的首元素(即序号为0的元素)的地址,下面两条语句等价:

p=&a[0];
p=a;      // a为首元素的地址

  ps:形参数组不占内存空间。

9、定义指针变量(数组)

int *p=&a[0];

  等价于

int *p;
p=&a[0]
int *p=a;

10、通过指针引用数组元素

  1)下标法,用数组名加下标,如a[i]形式;
  2)指针法,即地址法,数组名代表首元素的地址,因此通过 数组名计算数组中序号为 i 的元素的地址,形式为 *(a+i)。用一个指针变量p指向首元素,然后用*(p+i)调用a数组中序号为i的元素
  例子1:有一个数组存放10个学生的年龄,用不同的方法输出数组中的全部元素

    ① 用数组名加下标找到所需要的数组元素;

    ② 通过数组名计算数组元素地址,找到所需要的数组元素;

    ③ 通过指针变量计算数组元素地址,找到所需要的数组元素;

    ④ 用指针变量先后指向各数组元素。

#include <stdio.h>
int main()
{
    int a[10]={15,16,21,23,12,17,18,19,20,24};
    int i,*p=a;                    // 定义指针变量p并指向数组首元素
    printf("a[i]:\n");                
    for(i=0;i<10;i++)            // ① 用数组名加下标
    {
        printf("%d\t",a[i]);
    }
    printf("\n*(a+i):\n");        // ② 用数组名计算数组元素地址,找到元素
    for(i=0;i<10;i++)
    {
        printf("%d\t",*(a+i));
    }
    printf("\n*(p+i):\n");        // ③ 用指针变量计算数组元素地址,找到元素
    for(i=0;i<10;i++)
    {
        printf("%d\t",*(p+i));
    }
    printf("\n(a+10):\n");        // ④ 用指针变量先后指向数组中各个元素
    for(p;p<(a+10);p++)        
    {
        printf("%d\t",*p);
    }
    printf("\n");
    return 0;
}
View Code

注意:  

  ① 如果指针变量p已指向数组中的一个元素,则p+1指向同一数组中的下一个元素。

  p+1不是将p的值简单的加1,而是加一个数组元素 所占用的字节数。如果数组元素是float型,每个元素占4字节,则p+1意味着使p的值(是地址)加4字节。p+1所代表的地址实际上是p+1×d,d是一个数组元素所占的字节数,若p的值是2000,则p+1的值不是2001,而是2004

  ② 如果指针变量p的初值为&a[0],则p+i和a+i就是数组元素a[i]的地址,或者说,它们指向a数组的第i个元素;

  ③ *(p+i)或*(a+i)是p+i或a+i所指向的数组元素,即a[i]。例如*(p+5)*(a=5)就是a[5]。 也就是说:*(p+5)、(a+5)和a[5]三者等价

  ④ 方括号[ ]实际上是变址运算符即将a[i]按a+i计算地址,然后找出此地址单元中的值。找的元素的地址,然后找出该单元中的内容。若数组a的首元素的地址为1000,设数组为float型,则a[3 ]的地址是这样计算的: 1000+3×4=1012,然后从1012地址所指向的float型单元取出元素的值,即a[3]的值。

  例子2:通过指针变量读入数组的10个元素,然后输出这10个元素:

#include <stdio.h>
int main()
{
    int a[10];
    int i,*p=a;                    // 定义指针变量p并指向数组首元素
    for(i=0;i<10;i++)
    {
        scanf("%d",p++);        // p=a    a++,即相当于    a+所占字节数*i
    }
    p=a;                        // 此处划重点,如果不加的话,在执行完第一个for循环后,p的值已经是p+10,所以要重新指回数组首元素地址
    for(i=0;i<10;i++,p++)
    {
        printf("%d\t",*p);            
    }
    return 0;
}
View Code

  注意点:(1)在执行完第一个for循环后,p的值已经是p+10,所以要重新指回数组首元素地址。

      (2)a[10]包含10个元素,指针变量p指向某一数组元素,但是实际,上指针变量p可以指向数组以后的内存单元。如果在程序中引用数组元素a[10],虽然并不存在这个元素(最后一个元素是a[9]),编译是合法的。系统把它按* (a+10)处理,即先找出(a+10)的值(是一个地址),然后找出它指向的单元的内容。这样做虽然是合法的(在编译时不出错),但应避免出现这样的情况,要确保数组中的有效元素。

      (3)指向数组的指针变量也可以带下标,如p[i]。对p[i]处理成*(p+i),但要确保p[i]当前的值。

 

用数组名作函数参数

11、数组名作为函数参数时,各元素的值发生了变化,实参数组元素的值随之变化,这是为什么?

  因为实参数组名代表该数组首元素的地址,而形参是用来接收从实参 传递过来数组首元素地址 的,编译时将形参数组名作为指针变量来处理。

  例如:下面两种形式是等价的。

void f(int arr[],int n);        // 形参为数组形式
void sort(int *arr,int n);        // 定义为指针变量形式,即arr首地址

  即*arr等于int arr[0],*(arr+1)、*(arr+2)、*(arr+3) 分别等于array[1]、array[2]、array[3],*a(arr+i)和arr[i]无条件等价。

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

 

  • C语言调用函数时虚实结合的方法都是采用“值传递”方式;
  • 当用变量名作为函数参数时传递的是变量的值,当用数组名作为函数实参时,由于数组名代表的是数组首元素地址,因此传递的值是地址,所以要求形参指针变量;
  • 用户可以认为有一个形参数组,它从实参数组那里得到起始地址,因此形参数组实参数组共占同一段内存单元,在调用函数期间,如果改变了形参数组的值,也就是改变了实参数组的值。当然在主调函数中可以利用这些已改变的值;
  • 实参数组名代表一个固定的地址,或者说是指针常量,但形参数组不是一个固定的地址值,而是作为指针变量,在函数调用开始后,它的值等于实参数组首元素的地址,在函数执行期间,它可以再被赋值 
void f(arr[],int n)
{
    printf("%d\n",*arr);            // 输出array[0]的值
    arr=arr+3;
    printf("%d\n",*arr);            // 输出array[3]的值
}

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

#include <stdio.h>
int main()
{
    void inv(int x[],int n);
    int i,a[10]={3,5,7,9,11,13,15,17,19,21};
    printf("The original array:\n");
    for(i=0;i<10;i++)
    {
        printf("%d\t",a[i]);
    }
    printf("\n");
    inv(a,10);
    printf("The array has been inverted:\n");
    for(i=0;i<10;i++)
    {
        printf("%d\t",a[i]);
    }
    printf("\n");
    return 0;
}

void inv(int x[],int n)
{
    int i,j,temp,m=(n-1)/2;
    for(i=0;i<m;i++)
    {
        j=n-1-i;
        temp=x[i];
        x[i]=x[j];
        x[j]=temp;
    }
    printf("\n");
}
View Code
void inv(int *x,int n)
{
    int *p,*i,*j,temp,m=(n-1)/2;
    i=x;j=x+n-1;p=x+m;
    for( ;i<=p;i++,j--)
    {
        temp=*x;
        *i=*j;
        *j=temp;
    }
    printf("\n");
}
View Code

总结:归纳起来,如果有一个实参数组,要想在函数中改变此数组中的元素的值,实参与形参的对应关系有以下4种情况。
  (1) 形参和实参都用数组名。
  (2) 实参用数组名,形参用指针变量。
  (3) 实参形参都用指针变量。先使实参指针变量p指向数组a,然后将p作实参,将 &a[0] 传给形参指针变量x。x的初始值也是&a[0]通过x值的改变可以使x指向数组a的任一元素。
  (4) 实参为指针变量,形参为数组名。应该注意,必须先使实参指针变量有确定值,即指向数组一个元素。

   例2 :用选择排序对10个整数按由大到小顺序排序,用数组名作为实参。

#include <stdio.h>
int main()
{
    void sort(int x[],int n);
    int i,a[10]={67,14,43,132,141,13,112,111,19,21};
    int *p=a;
    printf("The original array:\n");
    for(i=0;i<10;i++)
    {
        printf("%d\t",a[i]);
    }
    printf("\n");
    sort(p,10);
    printf("The array has been inverted:\n");
    for(i=0;i<10;i++)
    {
        printf("%d\t",a[i]);
    }
    printf("\n");
    return 0;
}

void sort(int x[],int n)
{
    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)                    // 如果k不等于当前下标,即后面的大嘛,如果等于当前下标那就剩一个元素啦
        {
            t=x[i];
            x[i]=x[k];
            x[k]=t;
        }
    }
}
View Code

  函数的头部也可以改为如下形式:

void sort(int *x,int n)

  函数也可以改为如下:

void sort(int *x,int n)
{
    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)                    // 如果k不等于当前下标,即后面的大嘛,如果等于当前下标那就剩一个元素啦
        {
            t=*(x+i);
            *(x+i)=*(x+k);
            *(x+k)=t;
        }
    }
}

 

通过指针引用字符串

 13、定义一个字符指针,使它指向一个字符串。

#include <stdio.h>
int main()
{    
    char string[20]={"I love china!"};
    char *p;
    p=string;
    // char *string="I love china!";    // 等价于上面三条语句和下面两句,这里没有数组名,只能通过指针变量名来引用
    // char *string;                        // 这里也可以先声明字符指针变量,再给其赋值
    // string={"I love China!"};
    printf("%s\n",p);
    return 0;
}

  注意点:① 可以用指针指向字符串常量,但是不能通过指针变量去改变字符串常量。

      ② 字符指针变量也是取字符串的首个字符的地址,内存中,字符串最后会自动加 ‘\0’ ,字符串遇到 ‘\0’ 结束。

      ③ 取字符串的个别字符可以用 下标法指针法

  例子1:有一个字符数组a,在其中存放字符串"I am a boy." ,要求把该字符串复制到字符数组b中。

  1)数组方法实现 

#include <stdio.h>
int main()
{    
    char a[]="I am a boy.",b[20];
    int i;
    for(i=0;*(a+i)!='\0';i++)         // *(a+i)!='\0'好好理解这句的意思,循环条件是当前字符不为结束符时'\0'
    {
        *(b+i)=*(a+i);
    }
    *(b+i)='\0';                      // 在已复制到b数组中的字符串最后加'\0',表示字符串结束
    printf("string a is :%s\n",a);
    printf("string b is :");
    for(i=0;b[i]!='\0';i++)
    {
        printf("%c",b[i]);            // 下标方法访问数组元素
    }
    printf("\n");
    return 0;
}
View Code

  程序中a和b都定义为字符数组,可以通过地址访问其数组元素。在for语句中,先检查a[i]是否为'\0'  (程序中的a[i]是以* (a+i)形式表示的)。如果不等于\0',表示字符串尚未处理完,就将a[i]的值赋给b[i],即复制一个字符到数组b中的相应位置。在for循环中将a串全部复制给了b串。最后还应将\0复制过去

  2)指针变量方法实现

#include <stdio.h>
int main()
{    
    char a[]="I am a boy.",b[20];
    char *p1,*p2;
    p1=a;p2=b;                    // p1指针变量指向a的第一个字符,p2指针变量指向b的第一个字符
    int i;
    for(;*p1!='\0';p1++,p2++)         
    {
        *p2=*p1;                // 将p1指向的a串中的字符复制到p2所指向的b串中的位置
    }    
    p2='\0';                    // p2执行完for语句之后,p2指向最后一个位置应该加上'\0'表示结束
    printf("string a is :%s\n",a);
    printf("string b is :");
    for(i=0;b[i]!='\0';i++)
    {
        printf("%c",b[i]);            
    }
    printf("\n");
    return 0;

}
View Code

14、字符指针作为函数参数

  1、字符串函数从一个函数传递到另一个函数,可以用地址传递,即字符数组名作为参数,也可以是使用 指向字符的指针变量 作为参数。

  例子1:复制字符串,用函数调用函数实现

#include <stdio.h>
int main()
{    
    void copy_string(char * from,char * to);// 函数声明
    char *a="I am a teacher.";                // 定义a为字符指针变量,指向一个字符串
    char b[]="Your are a student.";            // 定义b为字符数组,内放一个字符串
    char *p=b;                                // p指针变量指向字符数组b的首元素
    printf("string a=%s\nstring b=%s\n",a,p);
    printf("\ncopy string a to string b:\n");
    copy_string(a,p);                        // 用字符串作实参
    printf("\nstring a=%s\nstring b=%s\n\n",a,p);
    return 0;
}

void copy_string(char * from,char * to)        // 形参是字符指针变量
{
    for( ;*from!='\0';from++,to++)            // from[0]和a[0]是同一个存储单元
    {
        *to=*from;                            // 只要a串没结束就复制到b数组中
    }
    *to='\0';
}
View Code

  例子2、有两个字符串:字符串a的内容为"I am a teacher.",字符串b的内容为"You are a student."。 要求把字符串b连接到字符串a的后面。即字符串a的内容为"I am a teacher. You are a student."。 

#include <stdio.h>
int main()
{    
    void link_string(char *arr1,char *arr2);
    char a[40]="I am a teacher.";                    // 定义a字符数组
    char b[]="You are a teacher.";                    // 定义a字符数组
    char *p1=a,*p2=b;                                // 定义字符指针变量p1,p2分别指向a和b
    printf("string a: %s\nstring b: %s\n",p1,p2);    // 输出连接前的字符串
    link_string(p1,p2);                                // 调用link_string函数,指针变量作为参数(形参?)
    printf("string a: %s\nstring b: %s\n",a,b);
    return 0;
}

void link_string(char * arr1,char * arr2)            // 形参是字符指针变量
{
    int i;
    for(i=0;*arr1!='\0';i++)                        // 将指针变量指向'\0',不为'\0',arr1地址自加。这不用i也可以实现
    {        
            arr1++;                                    // 当遇到'\0'后,arr1当前的指针指向a数组"."后一位,即'\0'
    }
    for( ;*arr2!='\0';arr1++,arr2++)                
    {
        *arr1=*arr2;                                // 只要arr2串没结束就复制到arr1中
    }
    *arr1='\0';                                        // 在复制结束后加一个结束表示符'\0'
}
View Code

15、调用函数时实参与形参的对应关系

  

16、使用字符指针变量和字符数组的区别

  1)本质

    字符数组:由若干个元素组成,每个元素存放一个字符;

    字符指针变量:存放的是一个地址(字符串中第1个字符的地址);

  2)赋值方式

    ① 字符数组:只能对各个元素赋值

char b[]={'I',' ','a','m',' ','h','a','p','p','y'};
char str[14]={"I love China!"};

    ② 字符指针

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

      等于

char *a="I love China!";

  3)字符数组,编译时分配的是一段确定的地址;字符指针变量,分配的内存单元是可以用来存放一个字符变量的地址,是一个字符型数据,如果未赋值,它的指向是未知的

    char str[10];
    scanf("%s",str);
    char *a,str[10];
    a=str;
    scanf("%s",a);

    错误做法如下:虽然不会错误,但是会提示警告,提醒未给指针变量赋初值。&a是内存分配一段临时的地址,但a是值在内存中是未知的,所以这样做是非常危险的。

    char *a;
    scanf("%s",a);
    printf("%s",a);

  4)改变指针变量的值

    例1:改变指针变量的值

    char *a="I love China!";
    a=a+7;                  // a一开始指向第1个字符,a+7指向的是第8个字符        
    printf("%s\n",a);
    return 0;

    下面是错误做法:

    char str[]="I love China!";
    str=str+7;                // 数组名是一个常量,代表数组初始地址,是不能改变的        
    printf("%s\n",str);

  5)指针变量带下标的形式

char *a="I love China!";

    a [5] 指的是第6个字符,即“e”。虽然未定义数组a,但是字符串在内存中是以数组形式存放的,A[5]*(a+5)处理。

  6)数组元素可以再赋值,而字符指针变量指向的字符串是不可以被取代的。

char a[]="House";
char *b="House";
a[2]='r';            // 合法,再赋值
b[2]='r';          // 非法,字符串常量(地址)不能改变

 

 提高部分

1、在字符指针作为函数参数(14点)例子1 的改进

  1)在本程序中将“* to= * from”的操作放在while语句括号内的表达式中,而且把赋值运算判断是否为'\0'的运算放在一个表达式中,先赋值后判断

void copy_string(char * from,char * to)       
{
    while((*to=*from)!='\0')                    
    {
            to++;from++;                       
    }
}

  2)把上面程序的to++和from++运算与*to=*from合并,它的执行过程是,先将*from赋给*to,然后使to和from增值

void copy_string(char * from,char * to)       
{
    while((*to++ = *from++)!='\0');
}

  3)当*from不等于\0时,将*from赋给*to然后使to和from增值

void copy_string(char * from,char * to)       
{
    while(*from!='\0')
    {
        *to++=*from++;
        *to='\0';
    }
}

  4)用其ASCII码来代替“ while(*from!= '\0') ”可以用“ while( *from!=0) ”代替('\0'的ASCII代码为0)。而关系表达式“ *from!=0 ”又可简化为“ *from ”。这是因为若* from的值不等于0,则表达式“

* from”为真,同时“ * from!=0 ” 也为

void copy_string(char * from,char * to)       
{
    while(*from)        
    {
        *to++=*from++;
        *to='\0';
    }
}

  5)进一步化简

    while(*to++=*from++);

    等价于

    while((*to++=*from++)!='\0');

  6)for循环实现

    for(;(*to++=*from++)!=0;);

    等价于

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

  7)用字符数组名作为函数形参,在函数中另定义两个指针变量p1,p2,指向copy_string函数形参接收到的实参传过来的地址。

void copy_string(char * from,char * to)       
{
    char *p1,*p2;
    p1=from;p2=to;
    while((*p2++=*p1++)!='\0');
}

2、多维数组的指针

    int a[3][4]={{1,3,5,7},{9,11,13,15},{17,19,21,23}};
    int (*p)[4];        // 定义指向一维数组的指针变量,表示一维数组中有4个元素,p的值就是该一维数组的起始地址
    p=a+1;              // 指向数组序号为1的行(a[1]开头的:a[1][0]、a[1][1]....)
  • a+1代表序号为1的行首地址
  • 若二维数组的首行的首地址为2000,一个整型数据占4字节,则a+1的值应该是2000+4X4=2016,因为第0行有4个整型数据
  • a+1指向a[1](即a[1]的首地址),a+2指向a[2](即a[2]的首地址).....啰嗦哈哈哈

 

3、指向函数的指针

  • 定义的函数都会分配一段存储空间,这段存储空间的起始地址(入口地址),称为 函数的指针
  • 定义一个指向函数的指针变量:

  定义:一般形式:数据类型 (* 指针变量名)(函数参数列表)

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

  调用前:指针调用函数,必须先使指针变量指向该函数:

    p=max                // p指向max函数,就是把max函数的入口地址给了p指针变量

  调用:只需将 (*p) 代替为函数名即可(p为指针变量名)

  p=max; 
  c=(*p)(a,b);

    相当于

  c=max(a,b); 

  指向函数的指针变量 的一个重要用途是把一个函数的入口地址 作为实参 传递给其他函数的形参,此时形参是指向函数的指针变量。这样就能够在被调用的函数中使用实参函数

4、返回指针值的函数

  函数可以返回一个整型值、实型值、字符型值,当然可以返回指针型的数据

  一般定义形式:

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

int *a(int x,int y);

   a为函数名,前面有“ * ”表示此函数是指针型函数(函数值是指针),int表示返回的指针是指向整型变量的,x和y是函数a的形参(整型)。调用它以后能得到一个指向整型数据的指针(地址)。

  注意:定义时,*a 两侧没有括号,如果有括号就是指向函数的指针变量了。

 

5、指针数组

  • 数组元素均为指针类型的数据(全是存放地址),成为 指针数组
  • 一般定义形式:

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

int *p[4];

    前面的“ * ”表示此数组是指针类型的,每个数组元素(相当于一个指针变量)都可指向一个整型变量。

 

6、多重指针

  指针数据的指针变量,称为 指向指针的指针。(套娃)

  name是一个指针数组,它的每一个元素是一一个指针型数据,其值为地址。name数组的每一元素都有相应的地址。数组名name代表该指针数组首元素的地址。Name+i是name[i]的地址。Name+i就是指向指针型数据的指针。还可以设置一个指针变量p,它指向指针数组的元素。p就是指向指针型数据的指针变量

char **p;    // 定义一个指向指针数据的指针变量

  p的前面有两个*号。* *p相当于*( * p),显然* p是指针变量的定义形式。

  如果没有最前面的*,那就是定义了一个指向字符数据的指针变量。现在它前面又有一个*号,表示指针变量p是指向一个字符指针变量 (即指向字符型数据的指针变量)的。

  * p就是p所指向的另一个指针变量,利用指针变量访问另一个变量就是“间接访问”。

  如果在一个指针变量中存放一个目标变量的地址,这就是“单级间址”。指向指针数据的指针用的是“二级间址”方法。

p的前面有两个*号。* *p相当于*( * p),显然* p是指针变量的定义形式。如果没有最前面的¥,那就是定义了一个指向字符数据的指针变量。现在它前面又有一个*号,表示指针变量p是指向一个字符指针变量(即指向字符型数据的指针变量)的。* p就是p所指向的另一个指针变量,在本章开头已经提到了“间接访问”的方式。利用指针变量访问另

 

posted @ 2021-03-11 13:45  一个特立独行的猪  阅读(389)  评论(0)    收藏  举报