咱们可以给一个函数传一个 整型、字符型、浮点型的数据,也可以给函数传一个地址。
函数的传参方式:复制传参、地址传参、全局传参(几乎用不到)
1 #include
2
3 //函数的传参方式之复制传参:将实参的值传递给形参,不管形参怎么改变,跟实参都没有关系
4 void myfun1(int a, int b)
5 {
6 int temp;
7 temp = a;
8 a = b;
9 b = temp;
10
11 printf("in fun: a = %d, b = %d\n", a, b);
12 printf("&a = %p, &b = %p\n", &a, &b);
13 }
14
15 void test1()
16 {
17 int a = 100, b = 20;18
19 printf("before fun: a = %d, b = %d\n", a, b);
20 printf("&a = %p, &b = %p\n", &a, &b);
21
22 myfun1(a, b);
23
24 printf("after fun: a = %d, b = %d\n", a, b);
25
26 }
27
28 int main(int argc, char *argv[])
29 {
30 test1();
31
32 return 0;
33 }
1 //函数的传参方式之地址传参:将实参的地址传递给形参,形参保存的地址的内容
2 //进行任何操作,实参的值也会跟着改变
3 void myfun2(int *p, int *q)
4 {
5 int temp;
6 temp = *p;
7 *p = *q;
8 *q = temp;
9
10 printf("in fun: *p = %d, *q = %d\n", *p, *q);
11 printf("p = %p, q = %p\n", p, q);
12 }
13
14 void test1()15 {
16 int a = 100, b = 20;
17
18 printf("before fun: a = %d, b = %d\n", a, b);
19 printf("&a = %p, &b = %p\n", &a, &b);
20
21 myfun2(&a, &b);
22
23 printf("after fun: a = %d, b = %d\n", a, b);
24
25 }
26
27 int main(int argc, char *argv[])
28 {
29 test1();
30
31 return 0;
32 }
注意:如果实参是一个普通变量,地址传参的话就需要形参是一级指针,
如果实参是一个一级指针,地址传参的话就需要形参是一个二级指针,
以此类推
将数组作为参数传递给函数,不存在复制传参和地址传参,本质都是地址传参,所以在函数内部对数组进行改变,则函数执行完毕后,原本的数组也会改变,因为传递给函数的都是数组的地址。
1 //传一维数组
2 //void fun1(int p[])//形式1
3 void fun1(int *p)//形式2(常用)
4 {
5 printf("%d\n",p[2]);
6 printf("%d\n",*(p+3));
7 }
8
9 void test2()
10 {
11 int a[10]={1,2,3,4,5,6,7,8};
12 fun1(a);
13 }
14
15 //传二维数组
16 //void fun2( int p[][4] )//形式1
17 void fun2( int (*p)[4] )//形式2:通过数组指针
18 {
19 //p[x][y] <==> *(*(p + x) + y)
20 printf("%d\n", p[0][2]);
21 printf("%d\n", *(*(p+1) + 2));
22 }
23
24 void test3()
25 {
26 int a[2][4] = {1, 2, 3, 4,
27 5, 6, 7, 8};
28 fun2(a);
29 }
30
31 //传指针数组
32 void fun3(char **q)
33 {
34 int i;
35 for(i=0;i<3;i++)
36 {
37 printf("%s\n",q[i]);
38 }
39 }
40
41 void test4()
42 {
43 char *p[3]={"hello","world","kitty"};
44 fun3(p);
45 }
指针函数本质是一个函数,只不过函数的返回值是一个指针
1 //指针函数:指针作为函数的返回值
2 char *fun4()
3 {
4 //栈区开辟的空间会随着当前代码段的结束而释放空间
5 //char str[100]="hello world";
6
7 //静态区的空间不会随着当前代码段的结束而释放空间
8 static char str[100]="hello world";
9
10 return str;
11 }
12
13 void test5()
14 {
15 char *p;
16 p = fun4();
17 printf("p = %s\n", p);
18 }
咱们定义的函数,在运行程序的时候,会将函数的指令加载到内存的代码段,所以函数也有起始地址。
c语言规定:函数的名字就是函数的首地址,即函数的入口地址 咱们就可以定义一个指针变量,来存放函数的地址,这个指针变量就是函数指针变量。
返回值类型 (*函数指针变量名)(形参列表);
1 int (*p)(int,int);//定义了一个函数指针变量p,p指向的函数
2 //必须有一个整型的返回值,有两个整型参数。
3 int max(int x,int y) { }
4 int min(int x,int y) { }
5 //可以用这个p存放这类函数的地址。
6 p=max; p=min;
1.通过函数的名字去调函数(最常用的)
1 int max(int x,int y) { }
2 int main()
3 {
4 int num;
5 num=max(3,5);
6 }
2.可以通过函数指针变量去调用
1 int max(int x,int y) { }
2 int main()
3 {
4 int num;
5 int (*p)(int ,int);
6 p=max;
7 num=p(3,5);
8 }
函数指针数组:本质是一个数组,数组里面的每一个元素都是一个函数指针
返回值类型 (*函数指针变量名[函数指针的个数])(形参列表);
int(*p[10])(int,int);
定义了一个函数指针数组,有10个元素p[0] ~p[9],每个元素都是函数指针变量,指向的函数,必须有整型的返回值,两个整型参数。
函数指针最常用的地方在于将一个函数作为参数传递给另一个函数的时候,要使用函数指针将一个函数作为参数传递给另一个函数,将这个函数称之为回调函数
1 #include
2
3 int add(int x,int y)
4 {
5 return x+y;
6 }7 int sub(int x,int y)
8 {
9 return x‐y;
10 }
11 int mux(int x,int y)
12 {
13 return x*y;
14 }
15 int dive(int x,int y)
16 {
17 return x/y;
18 }
19
20 int process(int (*p)(int ,int),int a,int b)
21 {
22 int ret;
23 ret = (*p)(a,b);
24 return ret;
25 }
26
27 int main(int argc, char *argv[])
28 {
29 int num;
30 num = process(add,2,3);
31 printf("num = %d\n",num);
32
33 num = process(sub,2,3);
34 printf("num = %d\n",num);
35
36 num = process(mux,2,3);
37 printf("num = %d\n",num);
38
39 num = process(dive,2,3);
40 printf("num = %d\n",num);
41
42 return 0;
43 }
第一组:
1、 int *a[10];
这是个指针数组,数组a中有10个整型的指针变量
a[0]~a[9]
2、int (*a)[10];
数组指针变量,它是个指针变量。它占4个字节,存地址编号。
它指向一个数组,它加1的话,指向下个数组。
3、 int **p;
这个是个指针的指针,保存指针变量的地址。
它经常用在保存指针的地址:
常见用法1:
int **p
int *q;
p=&q;
常见用法2:
int **p;
int *q[10];
分析:q是指针数组的名字,是指针数组的首地址,是q[0]的地址。
q[0]是个int *类型的指针。 所以q[0]指针变量的地址,是int **类型的
第二组:
1、int *f(void);
注意:*f没有用括号括起来
它是个函数的声明,声明的这个函数返回值为int *类型的。
2、int (*f)(void);
注意*f用括号括起来了,*修饰f说明,f是个指针变量。f是个函数指针变量,存放函数的地址,它指向的函数,必须有一个int型的返回值,没有参数。
1、空类型的指针(void *)
char * 类型的指针指向char型的数据
int * 类型的指针指向int型的数据
float* 类型的指针指向float型的数据
void * 难道是指向void型的数据吗?
不是,因为没有void类型的变量
回顾:对应类型的指针只能存放对应类型的数据地址
void* 通用指针,任何类型的指针都可以给void*类型的指针变量赋值。主要也是用在函数的参数和返回值的位置
int *p;
void *q;
q=p 是可以的,不用强制类型转换
举例子:
有个函数叫memset
void * memset(void *s,int c,size_t n);
这个函数的功能是将s指向的内存前n个字节,全部赋值为 c。
Memset可以设置字符数组、整型数组、浮点型数组的内容,所以第一个参数,就必须是个通用指针
它的返回值是s指向的内存的首地址,可能是不同类型的地址。所以返回值也得是通用指针
注意:void*类型的指针变量,也是个指针变量,在32为系统下,占4个字节
2、NULL
空指针:
char *p=NULL;
咱们可以认为p哪里都不指向,也可以认为p指向内存编号为0的存储单位。
在p的四个字节中,存放的是0x00 00 00 00
一般NULL用在给指针初始化。
int main(int argc, char *argv[])
argc:是一个int类型的变量,标识命令终端传入的参数的个数
argv:是一个指针数组,用于保存每一个命令终端传入的参数
1 #include
2 int main(int argc, char *argv[])
3 {
4 int i;
5 printf("argc=%d\n",argc);
6 for(i=0;i<argc;i++)
7 {
8 printf("argv[%d]=%s\n",i,argv[i]);
9 }
10
11 return 0;
12 }