c语言中关于指针的一些问题。(纯手打,码字不易,求个关注)

指针的概念

指针是一种数据类型,是一个变量在内存中所对应单元的地址。指针是用来存放地址的量。

(几个比较重要的概念:数组的首地址是数组名a的地址。数组首个元素的地址是a[0],比如一个数组a[10],系统会给a[0]~a[9]分配存储地址。

a[0]a[1]a[2]a[3]a[4]a[5]a[6]a[7]a[8]a[9]

指针会根据你的指令指向某个地址。

如何定义一个指针?

指针类型(int,float,double.......) *p;

这个时候p就会成为一个指针变量。然后接下来让p指向对应的地址。

*指针必须指向地址。

*p和p的区别

我们有时候会看到

#include<stdio.h>
int main()
{
    int a[10];
    for(int i=0;i<10;i++)
        scanf("%d",&a[i]);//为数组中十个元素赋值,比如输入1 2 3 4 5 6 7 8 9 10
    int *p=&a[3];
    printf("%d",*p);
}
//输出4(就是a[3])

有人可能会奇怪,我们知道p是指针应该是指向一个地址,*p指向的才是地址中的内容。

(举个例子:一个瓶子,里面装的是水,瓶子就相当于地址,水就相当于地址中的内容,所以指针指向的是装水瓶子(地址),而不是里面的水(内容))

这里看上去是*p指向地址,但是在定义里,int *p是指区别于普通变量的指针变量p的意思。

所以本质上还是p指向地址,*p表示其指向的内容。

关于如何辨别到底什么是地址,什么是元素的问题?

数组的首地址是数组名a的地址。我们要理解性的去记忆.

以一下程序为例子。

#include<stdio.h>
int main()
{
    int a[10]={1,2,3,4,5,6,7,8,9,10};
    int *p,*q,*m,*n;//定义四个指针变量。
    p=a;//根据数组的首地址是数组名a的地址,a是一个地址,所以此时p->a[0]
    q=a+1;//a是一个地址(即a[0]的地址),a+1是地址加1,就是a[1]的地址,所以q指向a[1]
    m=&a[0];//这一条语句和p=a一样,m也指向a[0],因为a是地址,a[0]是数组元素,不是地址,
              //所以应该取地址符
    n=&a[1];//和q那条语句等价,参考上一行的注释
    printf("%d\n%d\n%d\n%d",*p,*q,*m,*n);
}

 指向函数的指针

int (*p)();//定义一个指向函数的指针,该函数的返回值为整形数据。

int *f();//定义一个返回值为指针的函数,该指针指向一个整形数据。

注:在c语言中,()的优先级大于*,所以*指针变量名外部必须用括号,否则指针变量名先与后面的()结合。

更深刻的理解指针的作用

一段关于函数的代码:

#include<stdio.h>
hanshu(int x,int y)
{
    return(x>y)?x:y;//两个值x,y,如果x>y为真,输出第二个,即x。如果为假,输出第三个,即y。

}
int main()
{
    int (*p)();
    int a,b,c,d,e;
    scanf("%d,%d,%d",&a,&b,&d);
    c=(*p)(a,b);
    e=(*p)(c,d);
    printf("%d %d",c,e);
}

这样就可以用指针调用函数了。

即:c=(*p)(a,b)等价于c=hanshu(a,b)

指针的基本运算

众所周知,指针是地址,所以指针之间的运算就是地址间的运算。

*******只有当两个指针指向用类型的数据元素时,才能进行关系运算!!!!*****************

当p和q指向同类型的数据元素时:

p<q:如果p地址小于q地址,表达式为1(真),否则为0(假)

p>q:同理比较地址。

p==q:单等是赋值,双等是判断。如果p地址和q地址相等,即p,q指向同一个元素的地址时,

                表达式为1(真),否则表达式为0(假)

p!=q判断p,q地址是否相等,即p,q指向不同元素的地址时,

                表达式为1(真),否则表达式为0(假)

*****任何指针p与NULL进行p==NULL或p!=NULL运算均有意义。

*****p==NULL指的是p为空的时候成立

*****p!=NULL指的是p不为空的时候成立。

一道小问题:

#include<stdio.h>
int fun(char *x)
{
    char *y=x;
    while(*y++);
    return y-x-1;
}
int main()
{
    char c[]="UESTC.EDU.CN";
    printf("%d",fun(c+2));
    return 0;
}

首先分析主函数,输出的是fun(c+2)

看fun函数,指针x指向c+2(字符串末尾系统会自动补一个’\0‘,而'\0'和0完全等价,实际程序中可以写0或'\0'。但是这俩和\0不一样,\本身是转义字符,转的是后面的0,所以\0实际上是0的ASCII码,即48)而指针y指向x,相当于指针y和x都指向c[2]的地址。

UESTC.EDU.CN'\0'

                                           ^

                                            |

                                            x和y

正常的while循环后面无;  控制的是后面的一条语句,但是这里while后面有一个;  说明循环在while内部循环,直到循环体内部=0的时候跳出循环。即y指向字符串中的最后一位'\0'时为止。

此时y指向c[13],x->c[2],根据指针的运算关系,fun函数中return一个y-x-1即13-2-1=10。

指针和二维数组(行指针)

1、

        a[0],a[1],a[2]..........是一堆数组名,在C语言中数组名代表数组的首地址,因此a[0]代表第0行一维数组中第0列元素的地址。即&a[0][0];所以,a[1]的值就是&a[1][0];

        由此,a[0]+1在二维数组中就是第0行第一列的元素的地址了。(就是把第一行数组a[0]看作一个一维数组的首地址。)

        由前面我们知道,a[0]等价于*(a+0),a[1]等价于*(a+1),所以a[0]+1等价于*((a+0)+1),都是&a[0][1],因此a[0][1]本身的值也就是*(a[0]+1)或*(*(a+0)+1)。也就是a[i]和*(a+1)是等价的。

        从形式上看,可以认为a[i]是第i个元素。但是,如果a是一个一维数组的数组名,那么实际上来看,a[i]就是数组a中第i个元素中的内容。在这里a[i]是有物理地址的,是占内存单元的。如果a是一个二维数组,则a[i]是一个一维数组名,它本身不占内存单元。他只是一个地址,也就是这个二维数组里的dii+1行的首地址。同样的道理,a+1是地址,也就是a[1]。而*(a+1)等价于a[1]。

        所以:a[i][j]的存储地址是    首地址+i*N+j。

 (图片来自c语言程序设计(第2版)清华大学出版社)

所以行指针的问题要牢记这是指针!!!!

关于二级指针:

看下列一段代码

int a[10]={1,2,3,4,5,6,7,8,9,10}, *p=&a[1];

int *q=p;

这里看上去指针p指向a[1]指针q指向指针p,但其实这段代码所表达的是指针p,q都指向a[1]。而不是q指向p。

那么我们如何表示一个指向指针的指针呢?

答案是:二级指针。

看这段代码:

int a[10]={1,2,3,4,5,6,7,8,9,10}, *p=&a[1];

int **q=p;

这是二级指针q便是指向p的指针了。

三个小练习

1、

  1. 编写程序:输入 10 个整数,将其中最小的数与第一个数对换,将其中最大的 数与最后一个数对换。

         编程要求: 程序中要求有三个函数: (1)输入 10 个数; (2)进行处理(交换); (3)输出处理后的 10 个数

#include <stdio.h>
#define n 10
void spp(int *a)
{
    int m1,m2,i;
    int t1,t2;
    i=0;
    t1=*a;
    t2=*a;
    m1=0;
    m2=0;
    for(;i<10;i++)
    {
        if(*(a+i)>t1)
            {
                t1=*(a+i);
                m1=i;
            }
        if(*(a+i)<t2)
            {
                t2=*(a+i);
                m2=i;
            }
    }
    *(a+m1)=*(a+9);
    *(a+9)=t1;
    *(a+m2)=*a;
    *a=t2;
}
int main()
{
    int a[n];
    int i;
    for(i=0;i<n;i++)
    {
        scanf("%d",&a[i]);
    }
    spp(a);
    int j;
    for(j=0;j<n;j++)
    printf("%d,",a[j]);
    return 0;
}

2、

  1. 写一函数,将一个字符串逆置。

        ●编程要求: (1)要求使用指针作为函数参数; (2)在主函数中输入字符串,并输出逆置后的字符串。

#include<stdio.h>
#include<string.h>
void xinxi(char *p,int n)
{
    int i;
    char t;
    for(i=0;i<(n/2);i++)
    {
        t=*(p+i);
        *(p+i)=*(p+n-i);
        *(p+n-i)=t;
    }

    puts(p);
}
int main()
{
    char a[10];
    gets(a);
    int n;
    n=strlen(a);
    xinxi(a,n);
    return 0;
}

3、

  1. 编写函数,实现两个串的连接。(不用strcat)

●编程要求: (1)要求连接函数的参数和返回值类型均为指针类型; (2)要求在主函数中输入连接前的两个字符串,连接后,在主函数中输出连接后的字符 串。

#include<stdio.h>
#include<stdlib.h>
void xx(char *p,char *q)
{
    int i;
    while(*q!=0)
    q++;
    while(*p!=0)
    {
        *q=*p;
        q++;
        p++;
    }
    *q=0;
}
int main()
{
    char a[10];
    char b[10];
    scanf("%s",&a);
    scanf("%s",&b);
    xx(a,b);
    printf("%s",b);
}

暂时想到的就这些,还有什么问题可以在下面留言,大家一起交流。本文有什么问题也欢迎各位大佬指正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

五弦奏南风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值