从零开始学编程---第一步-C语言(二十七)

时间:2014-03-09 00:45    点击:

字符串指针暂时讲完了
现在让我们看看多级指针,和多维数组一样,我们只了解二级指针就可以了
多级指针可由二级指针推导而成

前面一开始说了指针也是一种数据类型,这种数据类型只存储地址
如 int a,*p=&a; 就是把a的地址存储到指针变量p里,也可以说指针变量p指向a的地址
那么指针变量p自己本身有地址吗?那是肯定的
如果再定义一个指针变量p1来存储(也就是指向)指针变量p的地址可以吗
绝对可以的,但并不是像下面这样
int *p;
int *p1=&p;

为了区分级别,需要多加一个*号,也就是像下面这样
int *p;
int **p1=&p;

这就说明了p1是一个指针的指针
如果是一个指针的指针的指针,那就是3级了,要***号才可以,后面就这样类推拉

其实我们学过的二维数组名就类似于二级指针
但也不能这样int a[2][3],**p=a
原因是二维数组名它并不是一个指针的指针(本来就不是指针嘛),不构成2级
自己要有地址的,能存储地址才是指针
指针指向一维数组
int a[5],*p=a 实际就是p存储了a这个地址,并不是说明a就是指针拉
所以反过来就是错误的 a=*p

不过二级指针就可以指向指针数组拉
int *a[5];
int **p=a;
指针数组名虽然说它并不是二级指针,但它实际也是一个指针的地址
int **p=a; 就是把a[0]这个指针的地址给了p

二级指针如何实现访问内容?
看下面代码一清二楚
#include <stdio.h>
main()
{
int a=1;
int *p=&a;
int **p1=&p;
printf("%d\n",&a); //a的地址
printf("%d\n",&p); //p的地址
printf("%d\n",p1); //代表p的地址
printf("%d\n",*p1); //代表p的内容,也就是a的地址
printf("%d\n",**p1); //代表*p,也就是a的内容

}

下面我们来做做关于多级指针的题目
在此之前,我们再学一个字符串函数strcmp()
这是一个字符串比较函数,很好很强大

char a[20]="hehe" b="haha"
strcmp(a,b);
a,b两个字符串进行比较,strcmp函数会对两个字符串自左向右逐个字符相比(按ASCII值大小相比较),直到出现不同的字符或遇'\0'为止
strcmp是有返回值的,比如比较a,b的第一个字符,b的第一字符ASCII值比a第一字符大,就是a<b,如果相等,则比较后一个字符,如果全部相等,则a=b
当a<b时,返回值<0 当a=b时,返回值=0 当a>b时,返回值>0 

还记得我们之前用指针数组做的字符串排序吧
下面用strcmp函数就简单多了
代码如下
#include <stdio.h>
#include <string.h>
main()
{
char a[20]="xiaohua",b[20]="zhangqiang",c[20]="china",d[20]="lili",e[20]="meimei",t[20];
char *pstr[5]={a,b,c,d,e};
puts("排序前");
for(int i=0;i<5;i++)
puts(pstr[i]);
for(i=0;i<4;i++)
for(int j=i+1;j<5;j++)
if(strcmp(pstr[i],pstr[j])>0)
{
strcpy(t,pstr[i]);
strcpy(pstr[i],pstr[j]);
strcpy(pstr[j],t);
}
puts("排序后");
for(i=0;i<5;i++)
puts(pstr[i]);



注意t数组是作为两个数组交换来使用的
之前我们排序是改变指针数组里指针的指向
但这里我们用的 strcpy函数,直接改变数组的内容
strcpy 应该知道是干什么用的吧
呵呵,这个字符串排序程序是不是要比之前的简单多了


下面还是字符串排序的程序,不过要加入二级指针在里面,做什么呢,看了就知道

#include <stdio.h>
#include <string.h>
main()
{
int i;
void sort(char **);
char str[5][20],*pstr[5],**p;
puts("请输入5个字符串");
for(i=0;i<5;i++)

pstr[i]=str[i];
gets(pstr[i]);
}
p=pstr;
sort(p);
puts("排序后的结果为");
for(i=0;i<5;i++)
puts(pstr[i]);


}

void sort(char **p)

int i,j;
char *t;

for(i=0;i<4;i++)
for(j=i+1;j<5;j++)
if(strcmp(*(p+i),*(p+j))>0)
{
t=*(p+i);
*(p+i)=*(p+j);
*(p+j)=t;

}


}


这个程序用了函数
首先是存储5个字符串,现在用二维数组方便多了,不需要一下定义5个数组了
然后用指针数组的每个元素(存储)指向二维数组每个行元素的地址(行元素是一维数组名)
定义一个二级指针p指向指针数组名pstr
p=pstr
注意pstr并不是真正的指针,它只是个地址,它是pstr[0]这个指针的地址
p就是地址pstr,也就是pstr[0]的地址
*p就取pstr的内容 也就是pstr[0]的内容 则str[0]的地址
**p就是取pstr指向地址的内容 也就是pstr[0]指向地址的内容,则str[0]的内容

定义了一个排序函数sort,因为要传递的是p,p是个二级指针,所以形参得定义二级指针
*(p+i)就是*p[i],由于p是pstr的地址,则pstr[0]地址
p[i]并不是指针数组的元素内容,而是元素的地址
*p[i]才表示指针数组的元素内容,而元素内容就是其他数组(字符串)的数组名(地址)
有了数组名,就可以代表字符串,也就可以用strcmp进行比较拉

这里会有点晕,所以要熟悉熟悉再熟悉

你可能有会疑问,为什么函数形参里要用二级指针,用一级指针不可以吗
如果这里用一级指针,在函数里修改的任何值,在主函数没任何变化,也就是白忙一场
你可能还会问为什么,不是传的地址吗?
没错,给形参给一个变量传地址,在函数中这个变量发生改变,那么在主函数里这个变量随之也改变
下面的代码就是实现这个功能
#include <stdio.h>
fun(int *a)
{
*a=5;
}


main()

int a=3;
fun(&a);
printf("%d",a);
}

但如果是让指针变量在主函数外改变而发生改变则无法实现
如下面
#include <stdio.h>


fun(int *p)
{
int b=5;
p=&b;
}


main()

int a=3;
int *p=&a;
fun(p);
printf("%d",*p);



}
这里在fun函数修改指针变量p的内容,也就是改变它的指向,在主函数中不会有任何改变
我们知道,对函数进行值传递,在函数里改变,在主函数里不会有任何改变
其实上面的指针传递就是值传递,指针变量的值就是指向变量的地址,在函数里修改也只是修改指针变量的值,也就是地址
传递形参地址,再在函数里通过地址改变值,值才会在主函数里发生改变
所以这里要传递指针变量的地址才行,也就需要用到二级指针

所以必须这样
#include <stdio.h>


fun(int **p)
{
int b=5;
*p=&b;
}


main()

int a=3;
int *p=&a;
fun(&p);
printf("%d",*p);


}

这样在主函数里指针变量p的指向就发生改变了

下面就说说指针函数和函数指针
指针函数就是函数的返回值是一个指针,这个相当好理解
看下面代码一清二楚
#include <stdio.h>
int *fun(int a)
{
int *p=&a;
return p;
}
main()

int a=1;
printf("%d\n",fun(a)); //a的地址
printf("%d\n",*fun(a)); //a的值

}

fun函数返回指针变量p的值
fun(a)就是p的值,也就是a的地址
*fun(a) 则就是*p 则a的值

返回的指针是什么类型,则函数就要定义什么类型
假如p是char类型的指针,则fun函数就要定义成char类型
指针函数的形参可以是任何数据类型,所以指针类型也可以的
在这里你一定要搞清
int fun(int *p)和int *fun(int p)的区别,别混淆了
一个是是形参为指针类型,一个是函数为指针类型

函数指针和指针函数十分相似,加个小括号,就成了函数指针
int (*fun)();
这就是一个指针,专门指向函数的
函数其实也是有地址的,每个函数都有一个首地址(也就是函数第一条指令的地方)
函数指针也就是指向这个地址
下面看下面这个程序
#include <stdio.h>

int max(int a,int b)
{
if(a>b)
return a;
else
return b;
}


main()

int a,b,c;
int (*p)(int a,int b);
p=max;
puts("输入两个数");
scanf("%d,%d",&a,&b);
c=(*p)(a,b); //等效于max(a,b)
printf("最大数为:%d\n",c);


}

int (*p)(int a,int b);
这是定义一个函数指针p,这是定义的一个模型,因为它要指向的函数必须形参和类型一致
int (*p)(int a,int b);
也可写为 int (*p)(int ,int);

p=max;(p=&max也可) 这是将max函数的首地址赋值到指针p中,也就是p指向max函数
C语言里,函数名代表函数的首地址,这点和数组一样

一旦指向函数
p就相当于函数名max了
c=(*p)(a,b); 也可以这样 c=p(a,b); 

下面程序绝对能看懂
#include <stdio.h>

void a()
{
puts("haha");
}

void b()
{
puts("hehe");
}


main()

void (*p)();
p=a;
p();
p=b;
p();

}

其实指针函数和函数指针的纠结程度就像指针数组和数组指针

说了这么多,大家可能还不知道函数指针到底有什么用

在程序里,有时候许多函数的功能不同,但它们的返回值和参数列表都相同,在这种情况下,就可以构造一个通用的函数,把函数指针作为这个函数的参数,这样有利于进行程序的模块快设计。

看下面代码
#include <stdio.h>

int plus(int a,int b);
int minus(int a,int b) ;
int multiply(int a,int b) ;
int divide(int a,int b) ;
int mathfun(int (*p)(int,int),int a,int b);
main()

int a,b;
puts("请输入两个数");
scanf("%d,%d",&a,&b);
printf("a+b=%d\n",mathfun(plus,a,b));
printf("a-b=%d\n",mathfun(minus,a,b));
printf("a*b=%d\n",mathfun(multiply,a,b));
printf("a\\b=%d\n",mathfun(divide,a,b));


}

int plus(int a,int b) //加
{
return a+b;
}
int minus(int a,int b) //减
{
return a-b;
}
int multiply(int a,int b) //乘
{
return a*b;
}
int divide(int a,int b) //除
{
return a/b;
}

int mathfun(int (*p)(int,int),int a,int b) //计算操作
{
return (*p)(a,b);
}

这里的函数指针做了形参
在主函数里,只用了一个通用函数进行其他功能的计算
要实现某个功能只需要在通用函数的形参里引用那个函数名
在这里,你可能感觉不到它有多方便,但在以后一些大程序里,你会发现有函数指针对处理函数有多方便
总之,指针是个好东西

上面的程序还是改成float类型比较好

#include <stdio.h>

float plus(float a,float b);
float minus(float a,float b) ;
float multiply(float a,float b) ;
float divide(float a,float b) ;
float mathfun(float (*p)(float,float),float a,float b);
main()

float a,b;
puts("请输入两个数");
scanf("%f,%f",&a,&b);
printf("a+b=%.3f\n",mathfun(plus,a,b));
printf("a-b=%.3f\n",mathfun(minus,a,b));
printf("a*b=%.3f\n",mathfun(multiply,a,b));
printf("a\\b=%.3f\n",mathfun(divide,a,b));


}

float plus(float a,float b) //加
{
return a+b;
}
float minus(float a,float b) //减
{
return a-b;
}
float multiply(float a,float b) //乘
{
return a*b;
}
float divide(float a,float b) //除
{
return a/b;
}

float mathfun(float (*p)(float,float),float a,float b) //计算操作
{
return (*p)(a,b);

}

这样小数也能计算


来源:幻想编程//所属分类:站长原创/更新时间:2014-03-09 00:45
顶一下
(11)
100%
踩一下
(0)
0%
上一篇:从零开始学编程---第一步-C语言(二十六)
下一篇:Microsoft Visual C++ V6.0的安装与使用
相关内容