SamZhou-2019.github.io

View on GitHub

学习笔记-C语言的指针

C语言的指针既难理解又麻烦,很多东西我也理解不了。

以下程序写于2018年10月,整理与解释于2020年10月初。

形参&实参

这个程序中的swap函数当中的形参为指针。使用该函数时,需要传入地址作为实参。

#include <stdio.h> 

void swap(char *px,char *py)//形参为指针
{
	char *p;
	p=*px;*px=*py;*py=p;//中间指针不加星号
}

int main()
{
    char *p1,*p2;
    char a,b;
	for(;;)
    {
        scanf("%d",&a);//输入一个大于0的整数a
        if(a<0) break;//小于零就退出
        b=a+2;
        p1=&a;//指针p1指向地址a
        p2=&b;//指针p2指向地址b
        printf("'%c'\n'%c'\n",a,b);//输出整数a,b对应的ASCII码
        swap(p1,p2);//调用函数,实参为指针p1,p2
        printf("'%c'\n'%c'\n",a,b);//可以发现p1和p2指向的空间,即a和b,里面的数据已经被交换
	}
	return 0; 
}

C语言当中,如果传入函数的是一个变量,则处理的仅仅是这个变量的“复制品”,而不改变变量本身。(如下方程序,函数swap仅仅是改变了被赋值的px和py。函数执行完毕后,a和b并未发生改变)。如使a和b发生改变,则应使用上方程序的方法。

#include <stdio.h> 

void swap(char px,char py)//形参为指针
{
	char p;
	p=px;px=py;py=p;//中间指针不加星号
}

int main()
{
    char a,b;
	for(;;)
    {
        scanf("%d",&a);//输入一个大于0的整数a
        if(a<0) break;//小于零就退出
        b=a+2;
        printf("'%c'\n'%c'\n",a,b);//输出整数a,b对应的ASCII码
        swap(a,b);//调用函数,实参为指针p1,p2
        printf("'%c'\n'%c'\n",a,b);//可以发现p1和p2指向的空间,即a和b,里面的数据已经被交换
	}
	return 0; 
}

形参和实参的确不太好理解😓

#include<stdio.h>

void sort(int* e,int* r,int* t)
{
    int temp;
    if(*e>*r) {temp=*e;*e=*r;*r=temp;};
    if(*e>*t) {temp=*e;*e=*t;*t=temp;};
    if(*r>*t) {temp=*t;*t=*r;*r=temp;};
    printf("%d %d %d\a",*e,*r,*t);
}

int main()
{
    int a,b,c;
    scanf("%d%d%d",&a,&b,&c);
    int *p,*q,*s;
    p=&a;
    q=&b;
    s=&c;
    m(p,q,s);
    return 0;
} 

数组名也是一种指针

数组名是指向数组第一个元素的指针,如在下面的程序中,a即&a[0]。而要读取后面的元素,只需读取指针向后移动之后的地址,如在下面的程序中,a[5]即*(a+5)。

#include <stdio.h>

void main()
{
	char *u;
	char a[10]={'a','s','d','f','g','h','j','k','p','l'};
	u=&a;//将u指向数组a的第一个元素
	for(;u<(a+10);)
	{
        sleep(1);//间隔一秒输出
        printf("%c\t",*u++);//将u向后移动即可读取a的后续元素
	}
}

注意*(u+i)和*u+i的区别。 *u+i的运算顺序是先取指针u的值,再将该值与i相加。

#include <stdio.h>

void main()
{
    float *u;
    int i;
    int a[]={};
    u=&a[0];
    for(i=0;i<10;i++)
    {
        printf("%f\t",*u+i);//c[0]+i
        printf("%f\t\n",*(u+i));//依次输出c[0]-c[9]
    }
    putchar("\n");
    return 0;
}

作为指针的数组名同时也可以作为实参,传入到形参包含数组的函数中。函数的操作会影响实际的数组。如在下面的程序中,函数reverse会使得数组a发生改变。

#include <stdio.h>

void reverse(int x[],int n)
{
    int i,t;
    for(;i<n/2;i++) 
    {
        t=x[i];
        x[i]=x[n-i-1];
        x[n-1-i]=t;}
}

int main()
{
    int a[10]={1,2,3,4,5,6,7,8,9,10};
    int i;int *b;
    for(i=0;i<10;i++) printf("Before reserve:%d\t",a[i]);
    printf("\n");
    b=a;
    reverse(b,10);
    for(i=0;i<10;i++) printf("After reserve:%d\t",a[i]);
    printf("\n\a");
    return 0;
}

而在二维数组中,需要使用两次*运算符才能正确读取数据,否则读取的是地址。如在下面的程序中*(*(a+i)+j)指的是a[i][j],而*(a+i)+j指的是&a[i][j]。这是因为二维数组是“一维数组的数组”,你可以理解成是“数组的嵌套”,“数组套数组”。

#include <stdio.h>

int main()
{
    int a[3][3]={\{20,80,70},{30,40,50},{90,60,10}\};
    int i,j;
    for(i=0;i<3;i++) 
    {
        for(j=0;j<3;j++) 
        {
            printf("%d\t",*(*(a+i)+j));//输出值
        }
        printf("\n");
    }
    for(i=0;i<3;i++) 
    {
        for(j=0;j<3;j++) 
        {
            printf("%d\t",*(a+i)+j);//输出地址
        }
        printf("\n"); 
    }
return 0;
}

指向数组的指针

可以用一个数组类型的指针指向数组。跟单个元素类型(如int *a)不同的是,指针的内容是一个数组。它实质上是定义了一个指针,后面的中括号相当于在这个地址上向后移动的位数,相当于*(a+i)。

#include <stdio.h>

int main()
{
    int c[3]={5,4,1}; 
    int (*a)[3];
    a=&c;
    printf("%d  %d  %d",(*a)[0],(*a)[1],(*a)[2]); 
/*	 a     a+1    a+2
	 |     |      |
	\|/   \|/    \|/
	 c[0]  c[1]   c[2]    */
    return 0;
}

指针的数组

你可以把它理解成定义了一组指针,每一个指针指向数组对应的元素。

字符串名本身也是一个指针,因为字符串在C语言中就是一个字符数组。

#include <stdio.h>
int main()
{
    char *p[5]={"app","odd","age","dog","kid"};//定义指针字符串
/*	p[0]->"app"
	p[1]->"odd"
	p[2]->"age"
	p[3]->"dog"
	p[4]->"kid"  */
    char *temp;
    temp=p[2];
    p[2]=p[3];
    p[3]=temp;//运算时不加星号 
    printf("%s",p[3]);//输出时不加星号 
    return 0;
}

指向函数的指针

函数也有指针!你可以直接给一个指针赋值,这个指针不需要初始化,赋的值就是函数名。使用时直接用*运算符和实参组合即可。如下面的程序中(*w)(p,q),w就是一个函数指针。

#include <stdio.h>

int min(int x,int y)
{
    int z;
    if(x<y) z=x;
    else  z=y;
}

int max(int x,int y)
{
    int z;
    if(x>y) z=x;
    else z=y; 
}

int sum(int x,int y)
{
    int z;
    z=x+y; 
}

int main()
{
    int p=15,q=16;
    char a;
    a=getchar();
    if(a=='0')
        w=max;//*w-->max
    else if(a=='1')
        w=min;//*w-->min
    else w=sum;//*w-->sum
    printf("%d",(*w)(p,q));
    return 0;
}	

返回指针的函数

这个是返回一个指针,不要和上面的搞混了!

定义函数时,可以把返回值定义为一个指针

#include <stdio.h> 

int *p(int (*r)[4],int i)//注意这里的第一个形参。二维数组是数组套数组,同样数组名是指针套指针。这里int (*r)[4]是在二维数组中一个指向同一维度四个元素的数组的指针。
{
	int *s;
	s=(r+i);
	return(s);
}

int main()
{
    int c[3][4]={\{6,3,2,5},{6,7,9,2},{9,5,4,1}\}; 
/*    6 3 2 5  i=0
      6 7 9 2  i=1
      9 5 4 1  i=2 */ 
    int *a;
    a=p(c,1);
/*    6 3 2 5  i=0
 a-> [6 7 9 2] i=1
      9 5 4 1  i=2 */ 
    printf("%d\t%d\t%d\t%d\a",*a,*(a+1),*(a+2),*(a+3));
    return 0;
   } 

建议还是不要用这么麻烦的数组和指针😖

指针与结构体

指针常常与结构体结合起来用于数据结构和算法编程。

#include<stdio.h>
struct one
{
    int num;
    char word[50];
    int data;
    int *x;
}
int main()
{
    struct one a,b,c;
    struct one *p;
    (a.x)=&(b.num);
    (b.x)=&(c.num);
    (c.x)=NULL;
    scanf("%d%s%d",&a.num,&a.word,&a.data);
    scanf("%d%s%d",&b.num,&b.word,&b.data);
    scanf("%d%s%d",&c.num,&c.word,&c.data);
    p=&a;
    printf("%d\t%s\t%d\t",(*p).num,(*p).word,(*p).data);//这里结构体指针p指向的是结构体a
    return 0;
}