一、指针与函数之间的关系
1、我们可以给一个函数传一个整型、字符型、浮点型的数据,也可以给函数传入一个地址。
2、函数的传参方式:复制传参(数值)、地址传参(地址值)
3、如果实参是一个普通变量,那么地址传参时,形参就需要使用一级指针;
如果实参是一个一级指针,那么地址传参时,形参就需要使用二级指针;
以此类推…
二、复制传参
#include <stdio.h>
//交换数值的一个函数
void fun(int a, int b)
{
int temp;
temp= a;
a= b;
b= temp;
printf("在函数中数值:a=%d,b=%d\n", a, b);
printf("在函数中地址:a=%p,b=%p\n", &a, &b);
}
//测试函数
void test()
{
int a=30, b=100;
printf("传参之前数值:a=%d,b=%d\n", a, b);
printf("传参之前地址:a=%p,b=%p\n", &a, &b);
fun(a, b);
printf("函数结束后数值:a=%d,b=%d\n", a, b);
printf("函数结束后地址:a=%p,b=%p\n", &a, &b);
}
int main()
{
test();
}
测试结果:
可以看到,形参a与实参a的地址是不同的,说明形参a与实参a就是两个独立开来的变量,因为作用域的原因,它们可以重名,但实际上是两个不同的变量。
因此,改变形参的值,并不会影响到实参的大小。
三、地址传参
1、传一维指针
#include <stdio.h>
//一个交换地址的函数
void fun2(int *p1, int *p2)
{
int temp;
temp= *p1;
*p1= *p2;
*p2= temp;
printf("在函数中数值:*p1=%d,*p2=%d\n", *p1, *p2);
printf("在函数中地址:p1=%p,p2=%p\n", &p1, &p2);
}
//测试函数
void test()
{
int a=30, b=100;
printf("传参之前数值:a=%d,b=%d\n", a, b);
printf("传参之前地址:a=%p,b=%p\n", &a, &b);
fun2(&a, &b);
printf("函数结束后数值:a=%d,b=%d\n", a, b);
printf("函数结束后地址:a=%p,b=%p\n", &a, &b);
}
int main()
{
test();
}
测试结果:
地址传参的分析方法与复制传参类似,可以看到形参的地址与实参的地址是一样的,说明实参就代表了形参,改变形参的值就是在改变实参的大小。
2、传字符串指针
#include <stdio.h>
void fun(char **q)
{
*q= "Oh my love";
}
int main()
{
char *p= "hello world";
fun(&p);
printf("%s", p); //输出字符串
}
这同样也是地址传参,因此会改变实参的数据值。
四、传一维数组(地址)
- 只要是传入一维数组的数组名,都是地址传参,会改变原来数组的数据值。
方式一:形参用一维数组,int p[]
;
方式二:形参用一维指针,int *p
。
#include <stdio.h>
void fun1(int *p) //也可以用int p[]
{
p[2]= 520;
printf("函数内:p[2]=%d\n", p[2]);
printf("函数内:p[3]=%d\n", *(p+3)); //相当于p[3]
}
int main()
{
int a[10]= {
10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
fun1(a);
printf("函数结束后:a[2]=%d\n", a[2]);
printf("函数结束后:a[3]=%d\n", a[3]);
}
因为传递的都是数组名,也就是地址传参了,所以修改形参的值就会改变实参的大小。
五、传二维数组(地址)
传递二维数组名都是地址传参,但是只有使用p[][]数组的方式才能改变原来数组的数据值。
#include <stdio.h>
void fun2(int (*p)[4]) //也可以用p[][4]
{
p[0][2]= 520; //这样不会改变原数组的数据
*(*(p+1)+2)= 120;
printf("函数内:p[0][2]=%d\n", p[0][2]);
printf("函数内:p[1][2]=%d\n", *(*(p+1)+2)); //相当于p[1][2]
}
int main()
{
int a[][3]= {
{
10, 9, 8},{
7, 6, 5}, {
4, 3, 2}};
fun2(a);
printf("函数结束后:a[0][2]=%d\n", a[0][2]);
printf("函数结束后:a[1][2]=%d\n", *(*(a+1)+2)); //相当于a[1][2]
}
六、传指针数组(地址)
指针数组本质上就是一个数组,数组里面的每一个元素都是一个函数指针,返回值类型(*函数指针变量名[函数指针的个数])(形参列表);
例如:int(*p[10])(int,int);
定义了一个函数指针数组,有10个元素p[0]~p[9],每个元素都是函数指针变量,指向的函数,必须有整型的返回值,两个整型参数。
测试代码:
void fun3(int **q)
{
int i;
printf("函数内输出:\n");
*(q)= "You";
*(q+1)= "are";
for(i=0; i<3; i++)
{
printf("%s\n", q[i]);
}
}
int main()
{
int i= 0;
char *p[3]= {
"I", "am", "here"};
fun3(p);
printf("函数结束后输出:\n");
for(i=0; i<3; i++)
{
printf("%s\n", p[i]);
}
}
测试结果:
七、指针函数和函数指针
指针函数:本质上就是一个函数,是一个返回指针的函数。
函数指针:本质上就是一个指针,是一个指向函数的指针。
详情请参见文章:
C语言指针深入透析(原来你一直没有搞懂C语言指针是因为没有理解其中的规律)