在C语言中,不仅是变量,数组,有地址,其实我们的函数也有地址
有地址的东西,我们就可以用一个指针变量来保存它们的地址,并且
可以通过该指针去访问指向的对象
函数也有地址--->函数指针
11.函数指针
函数指针是一个指针,只不过该指针指向的是一个函数。
1)函数指针如何定义呢
指向的类型 *指针的变量名;
int sum(int,int);
int sum(int a,int b)
{
}
要定义一个指针变量p,来保存函数sum的地址,怎么定义呢?
指向的类型 *p;
typeof(sum) *p;
如何来描述一个函数的类型的?
函数的类型 三要素:
函数的返回值 函数名(函数的参数类型列表)
int (int,int)==>就是描述一个类似sum函数的
返回值类型 (形参的类型列表)
如:
int *abc(int a,float b)
{
}
描述abc的类型:
int *(int,float)
->是一个返回int*类型名,带一个int,一个float的函数
类型。
int sum(int a,int b)
{
}
描述sum的类型
int (int,int)
typeof(sum) *p;
==>
int (int,int) *p;
int (*p)(int,int);
函数指针的定义方法:
指向函数的返回值类型 (*指针变量名) (指向函数的形式参数类型列表);
练习:
1.请定义一个指针变量p,来保存函数abc的地址
int *abc(int a,int *b)
{
}
int* (*p)(int,int *);
2.请定义一个指针变量p,来保存如下函数的地址
int sum_array(int *a,int n)
//int a[10];int *p=a;
//*(p+i) <==> p[i]
int sum_array(int a[],int n)
{
}
int (*p)(int *,int);
数组名作为函数的参数(形参,实参),当作一个指针来用的。
2)怎么给函数指针赋值呢?
p=函数的地址
函数的地址怎么获取呢?
&对象名 => 取对象的地址
&函数名
函数名:在C语言中,函数名本身就代表函数的首地址《-------------
练习:
int sum_array(int *a,int n)
{
}
//定义一个函数指针p 指向sum_array
int (*p)(int *,int)=&sum_array;
int (*p)(int *,int)=sum_array;
<==>
int (*p)(int *,int);
p=&sum_array;
int (*p)(int *,int);
p=sum_array;;
<==>
int *p=&a;
int *p;
p=&a;
p指向函数sum_array
3)怎么通过函数指针去调用指向的函数呢?
p=&sum_array
*p=>*&sum_array==>sum_array
p=sum_array
p=>sum_array
通过函数指针去调用指向的函数。有如下两种方式:
p为函数指针名
1)(*p)(实参列表)
2)p(实参列表)
练习:
1.在main中定义了一个函数指针p,通过p去调用sum_array
求数组元素的和-->sum_array
==>一维数组
int sum_array(int a[],int n);
int sum_array(int *a,int n)//int a[10]; a typeof(a) typeof(&a[0))
=>tyepof(a[0]) *=>int*
{
int i;
int sum =0;
for(i=0;i<n;i++)
{
sum+=a[i];//*(a+i) <==> a[i]
}
return sum;
}
main()
{
int a[10]={.....};
int (*p)(int* ,int)=sum_array;
int sum=p(a,10);
//int (*p)(int* ,int)=&sum_array;
//int sum=(*p)(a,10);
}
2.在main中定义了一个函数指针p,通过p去调用xxx_num
==>二维数组
int xxx_num(int (*a)[4],int n);
int xxx_num(int a[][4],int n)
{
printf("恭喜你调用成功!\n");
return 0;
}
int (*p)(int (*)[4],int);
----------------------------------------------------
int a[3][4]; => int[4] a[3];
a[0] //一维数组 a[0]是这个一维数组的名字
//是一个含有4个int类型元素的一维数组
a[1]
a[2]
int[4] a[]
数组当作函数的参数的时候,形参一般这样子设计:
数组元素的类型 数组名[],元素的个数
int a[][4],int n (1)
数组名当函数参数,当指针来看:
a=>&a[0]
typeof(a)==>typeof(&a[0])==>typeof(a[0]) *
=>int[4] *
数组元素的类型 *指针变量名,元素的个数 (2)
int (*a)[4], int n(2)
---------------------------------------------------------------------
12.二级指针与多级指针
int a;
可以定义一个指针变量p来保存a的地址
typeof(a) *p;//指向类型 *指针变量;
int *p;
p=&a;//p->a
*p=>*&a==>a
p本身也有地址,我们可以定义一个指针变量p2来保存p的地址
typeof(p) * p2;
int ** p2;
//p2二级指针,它保存的是一个一级指针的地址
//有人说,我要分清到底是两级指针还是一级指针,怎么区分呢?
//QNMLB,你只要知道它是一个指针(并且它保存了谁的地址)就可以了
p2=&p;
*p2 =>*&p=>p
**p2 ==> **&p =>*p=>a
a=1024;
*p=1024;
**p2=1024;
------------------------
main()
{
int n;
scanf("%d",&n);
int a[n];//动态数组,根据用户输入,数组元素的个数动态改变的。
}
“如何来实现动态数组的分配呢?”
13.动态内存分配函数
malloc/realloc/calloc
free
NAME
malloc, free, calloc, realloc - allocate and free dynamic memory
#include <stdlib.h>
void *malloc(size_t size);
malloc: memory allocate 内存分配
malloc用来动态分配一个size大小的内存空间,并且把分配到内存空间
的首地址返回。malloc分配的空间的生存期,是随进程持续性。
malloc分配的空间一旦分配给你,它不会自动释放,一定要手动调用
free或这个进程消亡了。
size:要分配的空间的大小(以字节为单位)
返回值:
成功返回分配到的空间的首地址,
失败返回NULL;
例子:
int a[n];
==>
int *p=(int*)malloc(n*sizeof(int))
p[0]
p[1]
..
*(p+i)
malloc可能会导致内存泄漏(产生一些垃圾内存)
int *p=malloc(100);//p指向一个100字节的内存空间
//通过p指针操作访问这段空间
p=&a;//p指向a,那100字节的空间,就没有指向指向了它
...
这100个字节的空间,是不是访问不了啦,但是这段空间
又不能分配给别人,这样的内存,我们称之为“垃圾内存”
把这种现象称之为“内存泄漏”
java 垃圾回收机制!!!
void free(void *ptr);
free 用来释放ptr所指向的内存的
ptr必须是malloc/calloc/realloc这是三个函数申请的内存
------------------------------------------------------------------
void *calloc(size_t nmemb, size_t size);
作用类型malloc,不过它是数组分配函数,
它分配一个数组空间,它带两个参数
n表示分配多少个元素
size表示每个元素占多少字节
so.它共分配的连续空间的大小:size*n
并且calloc分配空间,它把空间的内容全部置0、
=>malloc(n*size)
清0
------------------------------------------------------------------
void *realloc(void *ptr, size_t size);
re:repeat 重复
realloc用来把ptr(由malloc/calloc/realloc返回动态内存的地址)
指向的动态内存,扩展到size大小
ptr == NULL
realloc(NULL,size)<==>malloc(size);
ptr != NULL
扩展
1.size>原来的大小
realloc用来把ptr指向的内存,扩展到size字节,原来的前面的内存
内容保持不变,后面的新增的内存内容不会初始化
a.原址扩建
b.整体搬迁
2.size == 0
realloc(ptr,0)<==>free(ptr)
3.size<原来的大小
这种情况,作者自己都没有考虑到这种行为。
结果未定义的(undefined ,什么结果都有可能发生);
14.数组作为函数参数的问题
把数组当作函数参数:
(1)数组元素类型 数组名[],元素的个数
(2)数组元素类型 *指针变量名,元素的个数
1)
int a[10]
func(int p[],int n);
func(int*p,int n);
2)
int a[3][4];
int[4] p[],int n==> int p[][4],int n
int[4]* p,int n==>int (*p)[4],int n
15.main的参数问题
在linux下面,程序运行的时候,可以带参数,只不过所有的参数都当作是字符串
来处理。
如:
./main 123 456 "abc efg"
参数多个字符串==>字符串的数组
char *argv[]={"./main","123","456","abc efg"};
在运行main函数的时候,可以把上面那个数组传递给main函数,
so,linux下面的C程序的main的参数可以如下定义:
元素个数,数字元素类型 数组名[]
or
元素个数,数组元素类型 *指针变量名 //char * *argv
int main(int argc,char *argv[])
or
int main(int argc,char** argv)
{
}
练习:
请使用主函数的参数计算两个数的和 积
./main 456 + 789
=>
./main 456 * 789
=>
总结:
1.指向
如果p保存了a的地址,那么我们就说p指向a
p指向的类型就是a的类型
指针变量的定义
指向的类型 * 指针变量名;
&x:指针,因为保存了x的地址
typeof(&x)=> typeof(x) *
2.&地址 <==>地址对应的那个对象(变量,函数)
*&a <==> a
3.
*(a+i) <==> a[i],when i>=0
例子:
p[3][4]<==>*(p[3]+4]<==>*(*(p+3)+4)
4.指针作加减的问题
p+i(p是一个指针,i是一个整数)
不是简单的加减数值,而是加减i个指向单元的长度
5.数组名的问题
数组名可以看作是指向第一个元素类型的常量指针
数组名在数值上为第一个元素的地址
数组名a,在代码中有两个意思
1)代表整个数组
typeof(a)
siezof(a)
&a
2)在合适的情况下,可以当作指针看
&a[0]
6.指针数组与数组指针
主函数的参数的那个数组 是指针数组
7.字符串API函数
strlen
strcpy strncpy
strcmp/strncmp
strcat/strncat
8.函数指针
9.动态内存
malloc/realloc/calloc/free
作业:
0.前面的那套试卷 数组和函数
输入 ./a.out 3 + 5 ,输出 8 。(3位数以上 8位数以下的数)(7/2 = 3.5)输出字符串
1 . 字符串的大小写转换:请编写一个函数,实现讲字符串的大小写转换功能,数字不变。
例:输入 AD5ChadCtT 输出 ad5cHADcTt
2. 字符串的倒序 : 请编写一个函数,实现字符串的倒序输出。
例:输入 woshishazi520 输出 025izahsihsow
3. 删除字符串中的子串 :请编写一个函数,实现下面的功能:
输入2个字符串S1和S2,要求删除字符串S1中出现的所有子串S2,即结果字符串中不能包含S2。
例 输入:
Tomcat is a male ccatat
cat
输出:
Tom is a male
4. 字符串的循环左移:请编写一个函数,实现下面的要求:
输入:输入在第1行中给出一个不超过100个字符长度的、以回车结束的非空字符串;第2行给出非负整数N。
例: Hello World!
3
输出:在一行中输出循环左移N次后的字符串。
例 :lo World!Hel
5. 字符串的最长对称子串。请编写一个函数,得到一个字符串的最长对称子串及其长度:(考虑奇数和偶数的情况)
例 输入: nihaoaoahhaoin
输出: haoaoah 7
6.经典混合题
#include<stdio.h>
#include<windows.h>
int main()
{
char *c[] = { "ENIER","NEW","POINT","FIRST" };
char **cp[] = { c + 3,c + 2,c + 1,c };
char ***cpp = cp;
printf("%s\n", **++cpp);
printf("%s\n", *--*++cpp + 3);
printf("%s\n", *cpp[-2] + 3);
system("pause");//暂停 等待
return 0;
}