前面说到了sizeof(),在其中就涉及到了形参传递,现在就来说一下这里面涉及到的一些问题。
int main()
{
int a=4;
printf("&a=%p\n",&a); 输出0xbf80d4cc
void func(a);
}
void func(int b)
{
printf("&b=%p\n",&b); 输出0xbf80d4b0
}
我们发现,实参与形参地址不同,说明是在两个内存空间中。b是由a赋值得到的,所以也就有了函数执行完成后销毁,开辟内存空间后销毁什么的,如上方法为传值调用,实参做右值,形参做左值。
再来看下面一个实例:
int main()
{
int a[5]={1,2,3,4,5};
printf("&a=%p\n",&a); 输出0x012ffad4
void func2(a);
}
void func2(int b[])
{
printf("&b=%p\n",&b); 输出0x012ffad4
}
我们可以看到,传入函数的实参其实是一个地址,传入后依旧使用的是同一片内存空间,像这样数组传递的参数实际传递的是首元素地址,也就是完成了地址传递。
为什么要对值传递和地址传递进行研究,其原因一方面是为了简化操作复杂性另一方面是为了节省内存空间。
struct A
{
char a;
int b;
}
int main()
{
struct A a={a=4,b=555};
sizeof(a); 输出8,内存对齐
printf("&a=%p",&a); 0xbfbad048
func2(a);
}
func2(struct A a)
{
sizeof(a); 输出8
printf("&a=%p",&a); 0xbfbad030
}
我们可以看到,当我们将结构体做值传递时,是一整个结构体去新的内存空间进行了新内存的开辟,也就是说此时我们内存中有两个数据相同的结构体,都开辟了8字节的内存空间,但结构体更大呢,这样就会影响代码运行效率,以及占用内存空间。所以我们可以使用值传递的方式进行结构体传递。
struct A
{
char a;
int b;
}
int main()
{
struct A a={a=4,b=555};
sizeof(a); 输出8,内存对齐
printf("&a=%p",&a); 0xbfda5508
func3(&a);
}
func2(struct A *a1)
{
sizeof(a1); 输出4 指针大小
sizeof(*a1); 输出8 结构体大小
printf("&a1=%p",&a1); 0xbfda54f0 存放指针的地址
printf("a1=%p",a1); 0xbfda5508 a1指向的地址,结构体起始地址
printf("a1.b=%d",a1->b); 输出555
}
看上面这个例子,就是使用地址传递,在同一块内存中完成了相应操作,唯一多出的就是一块存放a1指针的内存空间,在大结构体操作过程中由于不用copy老结构体到新内存空间,使用将会提高访问效率节省内存使用空间。
看了上面的示例,想必应该明白了二者的区别,也就明白了经典案例—swap两值交换的正确写法。
int main
{
int a=3,b=5;
swp1(a,b);
printf("a=%d,b=%d"a,b); 输出3,5 交换失效
swp2(&a,&b)
printf("a=%d,b=%d"a,b); 输出5,3 交换成功
}
void swp1(int a,int b) 值传递开辟新空间,在新空间中自己换着玩
{
int tmp=0;
tmp=a;
a=b;
b=tmp;
}
void swp2(int* a,int* b) 地址传递,在老空间内完成值的交互
{
int tmp=0;
tmp=*a;
*a=*b;
*b=tmp;
}
函数名本质上只是一个符号,表示了整个代码段的起始地址,也就是一个指针,所以在函数中使用函数名都是当地址进行调用。
形参为函数输入部分,返回值为输出部分,一个函数即使没有形参也能加工全局变量。
我们都知道,return只能返回一个值,所以一般用于返回操作执行判断,那么多个变量返回我们应该如何处理,答案是用函数的参数做返回值(指针)。
func(int a,int *p,const int *p2)
看到函数接口有三个参数,其中int a为输入形参数(原因是去新空间了),int *p为输出形参数,const int *p2是输入形参数(原因是const修饰后为常量不可改变,使用const修饰后一方面是避免值被修改,另一方面是提高代码可读性,将输入后在函数中不变的参数设为const)。