目录
(3)例:编写一个指针函数,把整数123转换成字符串“123”。
一、指针函数
1.基本形式
指针函数是指一个函数的返回值为地址量的函数
指针函数的定义的一般形式如下
<数据类型> * <函数名称>(<参数说明>)
{ 语句序列;}
*返回值:全局变量的地址/static变量的地址/字符串常量的地址/堆的地址(需要学maloc).
下面的代码是否有问题,若有问题,如何修改?
#include <stdio.h>
char * mystring(){
char str[20];
strcpy(str, "Hello");
return str;
}
int main(int argc, const char * argv[])
{
printf("%s\n",mystring()); //error
return 0;
}
mystring返回的是一个指针。因为str是个局部变量。用的是栈上空间,自动分配自动回收。
改正方案:
1全局变量,但是没必要再去用返回值,直接出结果了。
2用静态变量static,变量的寿命得到了延长 static char str[20];
3字符串常量 char * str = "hello"; 但是不方便修改
#include <stdio.h>
#include <string.h>
char * mystring(){
static char str[20];
strcpy(str, "Hello");
return str;
}
int main(int argc, const char * argv[])
{
printf("%s\n",mystring()); //ok
return 0;
}
2.编程实例
(1)例:编写一个指针函数, 删除一个字符串中的空格
#include <stdio.h>
#include <string.h>
char *Del_Space(char * s);
int main(int argc, const char * argv[])
{
char str[] = "who are you?";
char s[50], s2[25];
strcpy(s2,strcpy(s,Del_Space(str)));
printf("%s\n", s);
return 0;
}
char *Del_Space(char * s){
char * r =s; //指向头指针
char * p =s;
while(*s ){ //等同于 while(*s != '\0')
if(*s == ' '){
s++;
}
else{
*p = *s;
s++;
p++;
}
}
*p = '\0'; //需要注意\0要写过来
return r;
}
//结果
whoareyou?
分解语句s2是目标指针,strcpy(s,del_space(str))是源指针。在往里分解其中s是目标指针,del_space(str)是源指针去掉了空格。相当于连续赋值,但是指针里面用的是strcpy函数。这个函数其实返回的既不是全局变量,也不是局部变量,也不是静态变量,也不是堆空间,也不是字符串常量。其实返回的是一个参数
(2)例:编写一个指针函数, 实现字符串连接
#include <stdio.h>
#include <string.h>
char *Mstrcat(char * dest, char * src);
int main(int argc, const char * argv[])
{
char str1[50] = "who are you?";
char str2[] = "Your Dad";
printf("%s\n",Mstrcat(str1 , str2));
return 0;
}
char *Mstrcat(char * dest, char * src){
char * r = dest;
while( * dest){ //可以简化成 while(* dest++)
dest++;
}
while( * src){ //可以简化成 while(*dest++ = *src++)
*dest = *src;
dest++;
src++;
}
*dest = '\0'; //需要注意\0要写过来
return r;
}
//结果
who are you?Your Dad
设计也是考虑2个指针,目标指针找到\0位置,然后接新串,并各自增。再找个指针,记录原来的位置
(3)例:编写一个指针函数,把整数123转换成字符串“123”。
应用场景,比如网络上面给一个6000端口号,把字符串变成整数,叫做atoi,还有itoa。
#include <stdio.h>
char * itoa(int n);
int main()
{
int n;
char * s;
printf("input:");
scanf("%d", &n);
s = itoa(n);
puts(s);
return 0;
}
char * itoa(int n)//itoa()自定义函数名称,整数转换成字符串
{
int r, i, j;
static char p[50];
while(n){
r = n % 10;
n /= 10;
p[i] = r + '0';//数字和字符的ASCII码相差48
i++;
}
p[i] = '\0';
j = i-1;
i = 0;
while(i < j){//数组反转,两个指针对位交换,从两边向中间走。
r = p[i];
p[i] = p[j];
p[j] = r;
i++;
j--;
}
return p;
}
另一种写法 ,如果题目给了字符串空间,我们需要2个参数
#include <stdio.h>
char * itoa(char * p, int n);
int main()
{
int n;
char s[50], *r;//给一个数组,可以利用数组的空间
// char * s;
printf("input:");
scanf("%d", &n);
r = itoa(s,n);//把输入的(n)放到数组(s)中
puts(r);
puts(s);
return 0;
}
char * itoa(char * p, int n)
{
int r, i = 0, j;
// static char p[50];数组不用声明空间了,数组空间是上面传递来的
//不用再数组内部考虑空间大小
while(n){
r = n % 10;
n /= 10;
p[i] = r + '0';
i++;
}
p[i] = '\0';
j = i-1;
i = 0;
while(i < j){
r = p[i];
p[i] = p[j];
p[j] = r;
i++;
j--;
}
return p;
}
扩展把字符串转换成整数
#include <stdio.h>
#include <string.h>
int *atoi(char * arr);
int main(int argc, const char * argv[])
{
char str1[] = "123";
int * p;
p = atoi(str1);
printf("%d",(int)p);
return 0;
}
int *atoi(char * arr){
int n = 0;
while( * arr){
n = n * 10+(*arr - '0') ;
arr++;
}
return (int *)n;
}
//结果123
3.总结
主要介绍了指针函数的概念以及指针函数的编写
为什么要用指针函数?
答:主要是能够用一个指针的方式指向一个函数,并且还可以更换指向别的函数,比如有多个函数的申明,它们有不同的具体实现,如果需要调用它们,就可以用一个指针轮流指向它们。
指针函数可以返回什么样的指针?
函数可以返回整形、字符串、实型值、无返回值、也可以返回指针类型的数据,即返回一个内存地址,像这样返回地址的函数成为指针函数。
二、递归函数
1.基本概念
递归函数是指一个函数的函数体中直接或间接调用了该函数自身递
归函数调用的执行过程分为两个阶段:
- 递推阶段:从原问题出发,按递归公式递推从未知到已知最终达到递归终止条件
- 回归阶段:按递归终止条件求出结果,逆向逐步代入递归公式,回归到原问题求解
2.编程实例
(1)编写一个递归函数,计算n!编写一个递归函数
思路:
f(10) = 10!=10*9! 9!=9*8! ...一直到 1!=1*0! 规定0的阶乘=1,递归回原路。
递归阶段:f(n) = n*f(n-1)
回归阶段:规定0! = 1,程序找到归口之后迭代回去(必须有归口,否则要死循环)
#include <stdio.h>
#include <string.h>
int recursion(int n);
int main(int argc, const char * argv[])
{
int n;
printf("input number:\n");
scanf("%d",&n);
printf("%d\n", recursion(n));
return 0;
}
int recursion(int n){
if(n == 0){
return 1;
}
while(n>1){
return n*recursion(n-1);
}
}
//结果
syj@ubuntu:~$ ./a.out
input number:
1
1
syj@ubuntu:~$ ./a.out
input number:
0
1
syj@ubuntu:~$ ./a.out
input number:
10
(2)计算斐波那契数列
一般而言,兔子在出生两个月后,就有繁殖能力,一对兔子每个月能生出一对小兔子来。如果所有兔子都不死,那么一年以后可以繁殖多少对兔子?我们不妨拿新出生的一对小兔子分析一下:第一个月小兔子没有繁殖能力,所以还是一对;两个月后,生下一对小兔,对数共有两对;三个月以后,老兔子又生下一对,因为小兔子还没有繁殖能力,所以一共是三对。
思路:
数列:1、1、2、3、5、8、13、21、34...
递归阶段:f(n) = f(n-1) + f(n-2)
回归阶段:规定f(1) = 1 ; f(2) =1;把归口条件迭代回公示内交给程序计算
#include <stdio.h>
#include <string.h>
int fib(int n);
int main(int argc, const char * argv[])
{
int n = 1;
while (n <= 10){
printf("%d ", fib(n));
n++;
}
printf("\n");
return 0;
}
int fib(int n){
int i = 1;
if(n == 1 || n == 2){
return 1;
}
while(i <= n){
return fib(n-1)+fib(n-2);
}
}
//结果
1 1 2 3 5 8 13 21 34 55
三、函数指针
1.表达形式
1函数指针用来存放函数的地址,这个地址是一个函数的入口地址
2函数名代表了函数的入口地址
3函数指针变量说明的一般形式如下
<数据类型> (*<函数指针名称>)(<参数说明列表>);
- -<数据类型>是函数指针所指向的函数的返回值类型
- -<参数说明列表>应该与函数指针所指向的函数的形参说明保持一致
- -(*<函数指针名称>)中,*说明为指针()不可缺省,表明为函数的指针
调用就是(*p)(m,n)把函数名换成(*P)
用在什么地方?
使得函数功能更强,比如排序,能整型、字符、double都能排序
程序案例:
#include <stdio.h>
int add(int a, int b)
{
return a+b;
}
int main()
{
int m = 10, n = 20;
//声明时形参可以省略,写出函数原型即可
int (* p)(int a, int b); //函数指针的声明
p = add;//赋值
// printf("%d\n", add(m,n));
printf("%d\n", (* p)(m, n));//调用
return 0;
}
//结果
//30
2.函数指针数组
函数指针数组是一个保存若干个函数名的数组
一般形式如下
<数据类型> (*<函数指针数组名称> [<大小>] )(<参数说明列表> );
其中,<大小>是指函数指针数组元数的个数其它同普通的函数指针 类似指针数组
编程实例
#include <stdio.h>
int add(int a, int b)
{
return a+b;
}
int sub(int a, int b)
{
return a-b;
}
int main()
{
int m = 10, n = 20;
int (* p[2])(int, int);
p[0] = add;
printf("%d\n", (* p[0])(m, n));
p[1] = sub;
printf("%d\n", (* p[1])(m, n));
return 0;
}
//结果
30
-10
3.总结
主要介绍了递归函数,函数指针以及函数指针数组的相关内容思考编写递归函数要注意什么问题?
答:递归阶段正不正确;回归阶段结束条件是否正确
程序:调用C库中的qsort函数来实现整形数组的排序。
qsort函数数组原型
void qsort(void * base, size_t nmemb, size_t size, int ( * compar) (const void *, const void *));
通过linux的man qsort命令查看函数要求:
函数的作用是:对包含nmemb元素的数组进行排序。base参数指向数组的开始。
数组的内容根据compare所指向的比较函数升序排序,调用该函数时有两个参数指向被比较的对象。
如果第一个参数被认为分别小于、等于或者大于第二个,比较函数必须返回一个小于、等于或大于零的整数。如果两个成员比较相等,则它们在排序数组中的顺序未定义。
qsort_r()函数与qsort()相同,不同之处是比较函数compare有第三个参数。一个指针被传递给比较函数
通过参数。通过这种方式,比较函数不需要使用全局变量来传递任意参数,因此是可重入的,使用起来是安全的在线程。
#include <stdio.h>
#include <stdlib.h>
int compare(const void * p, const void *q);
int main()
{
int s[] = {89, 58,78,10,8,7,64}, n, i;
n = sizeof(s)/sizeof(int);
qsort(s, n, sizeof(int), compare);
for(i = 0; i < n; i++)
printf("%d ", s[i]);
puts("");
return 0;
}
int compare(const void * p, const void *q)
{//p、q为待比较的两个元素
return (*(int *)p - *(int *)q);//如果是q-p,那么是降序
//由于函数参数为void型,所以要进行强制转换
}
//结果
7 8 10 58 64 78 89 //升序