24.C语言函数传参详解


24.1.普通变量作为函数形参
(1)普通变量作为参数在函数传参时,形参和实参名字可以相同也可以不同,实际上都是用实参来替代相应的形参的。
(2)在字函数内部,形参的值等于实参。原因是函数调用时把实参的值赋值给了形参。这就是所谓的”传值调用”,相当于实参做右值,形参做左值。


24.2.数组和指针作为函数形参
(1)函数名作为形参传参时,实际传递是不是整个数组,而是数组的首元素的首地址。所以在子函数内部,传进来的数组名就等于是一个指向数组首元素首地址的指针。所以sizeof得到的是4。
(2)在子函数内传参得到的数组首元素首地址,和外面得到的数组首元素首地址是相同的。这就是所谓的”传址调用”,即调用子函数时传了地址,此时可通过传进去的地址访问实参。
(3)数组名作为函数形参时,[]里的数字可有可无。因为数组名做为形参传递的实际只是个指针,根本没有数组长度这个信息。
(4)指针作为函数形参时和数组作为函数形参是一样的.这就好像指针方式访问数组元素和数组方式访问数组元素的结果一样是一样的。


24.3.结构体变量作为函数形参
(1)结构体变量作为函数形参的时候,实际上和普通变量(类似于int之类的)传参时表现是一模一样的。所以说结构体变量其实也是普通变量而已。
(2)因为结构体一般都很大,所以如果直接用结构体变量进行传参,那么函数调用效率就会很低。(因为在函数传参的时候需要将实参赋值给形参,所以当传参的变量越大调用效率就会越低)。为提高效率,我们可改传变量的指针(地址)进去。
(3)结构体因为自身太大,所以传参应该用指针来传(但是程序员可以自己决定,你非要传结构体变量过去C语言也是允许的,只是效率低了)。


24.4.传值调用与传址调用
(1)传值调用描述的是这样一种现象:x和y作为实参,自己并没有真身进入swap1函数内部,而只是拷贝了一份自己的副本(副本具有和自己一样的值,但是是不同的变量)进入子函数swap1,然后我们在子函数swap1中交换的实际是副本而不是x、y真身。所以在swap1内部确实是交换了,但是到外部的x和y根本没有受影响。
(2)在swap2中x和y真的被改变了(但是x和y真身还是没有进入swap2函数内,而是swap2函数内部跑出来把外面的x和y真身改了)。实际上实参x和y永远无法真身进入子函数内部(进去的只能是一份拷贝),但是在swap2我们把x和y的地址传进去给子函数了,于是乎在子函数内可以通过指针解引用方式从函数内部访问到外部的x和y真身,从而改变x和y。
(3)结论:这个世界上根本没有传值和传址这两种方式,C语言本身函数调用时一直是传值的,只不过传的值可以是变量名,也可以是变量的指针。


24.5.函数形参和返回值
(1)函数名是一个符号,表示整个函数代码段的首地址,其实质是一个指针常量,所以在程序中使用到函数名时都是当地址用的,表示调用该函数。函数体是函数的关键,由一对{}括起来,包含很多句代码,函数体就是函数实际做的工作。形参列表和返回值,形参是函数的输入部分,返回值是函数的输出部分。
(2)若没有形参列表和返回值,函数也能对数据进行加工,使用全局变量即可。用全局变量来传参和用函数参数列表返回值来传参各有特点,在实践中都有使用。总的来说,函数参数传参用得较多,因为这样可实现模块化编程,而C语言中也是尽量减少使用全局变量。
(3)全局变量传参最大的好处就是省略了函数传参的开销,所以效率要高一些;但是实战中用的最多的还是函数传参,如果参数很多传参开销非常大,通常的做法是把很多参数打包成一个结构体,然后传结构体变量指针进去。


24.6.函数传参中使用const指针
(1)const用来修饰指针作函数传参,就是为了声明在函数内部不会改变该指针所指向的变量。所以给该函数传一个不可改变的指针(譬如char *p = “linux”; 字符串常量,放在代码段中,不可改变)后,一旦该函数内部试图更改它时编译器会提示错误;而一个未声明为const指针参数的函数,我们给该函数传一个不可更改的指针时,一旦该函数内部更改它后,编译器在编译过程中不会有提示,而是会在运行中导致段错误。
(2)一般来说,函数的输入部分就是函数参数,输出部分就是返回值。问题是函数的参数可以有很多个,而返回值只能有一个。则我们无法让一个函数返回多个值。
(3)现实编程中,一个函数需要返回多个值是非常普遍的,因此完全依赖于返回值是不靠谱的,通常的做法是用参数来做返回(在典型的linux风格函数中,返回值是不用来返回结果的,而是用来返回0或者负数用来表示程序执行结果是对还是错,是成功还是失败)。
(4)普遍做法,编程中函数的输入和输出都是靠函数参数的,返回值只是用来表示函数执行的结果是对(成功)还是错(失败)。如果这个参数是用来做输入的,就叫输入型参数;如果这个参数的目的是用来做输出的,就叫输出型参数。输出型参数就是用来让函数内部把数据输出到函数外部的。
(5)总结:函数传参如果传的是普通变量(不是指针)那肯定是输入型参数;若该函数形参是指针变量并且加了const,那么就表示这个参数是用来做输入型参数的;若该函数形参是指针变量并且还没加const,那么就表示这个参数是用来做输出型参数的。譬如C库函数中strcpy函数:char *strcpy(char *dest, const char *src);。


24.func_param
/*
 * 公司:XXXX
 * 作者:Rston
 * 博客:http://blog.csdn.net/rston
 * GitHub:https://github.com/rston
 * 项目:C语言函数传参详解
 * 功能:演示普通变量和结构体变量在函数中传参。
 */

#include <stdio.h>

struct A
{
    int e;
    char f;
};

void func2(struct A *b)
{
    printf("&b = %p.\n", &b);       // &b = 0xbffaafb0
    printf("b->e = %d.\n", b->e);   // b->e = 10.
    printf("b->f = %d.\n", b->f);   // b->f = 1.
}

void func1(int b)
{
    printf("b = %d.\n", b);         // &b = 0xbff2adf0.
    printf("&b = %p.\n", &b);       // b = 2.
}

int main(int argc, char **argv)
{
    int a = 2;
    func1(a);
    printf("&a = %p.\n", &a);       // &a = 0xbff2ae0c.

    struct A d = 
    {
        .e = 10,
        .f = 1,
    };
    func2(&d);
    printf("&d = %p.\n", &d);       // &d = 0xbffaafc4.
    printf("d.e = %d.\n", d.e);     // d.e = 10.
    printf("d.f = %d.\n", d.f);     // d.f = 1.

    return 0;
}

24.input_output_param
/*
 * 公司:XXXX
 * 作者:Rston
 * 博客:http://blog.csdn.net/rston
 * GitHub:https://github.com/rston
 * 项目:C语言函数传参详解
 * 功能:演示输入型参数和输出型参数。
 */
#include <stdio.h>

#if 0
void func1(const char *p);
#endif
void func2(char *p);

int main(int argc, char **argv)
{
    char *str = "linux";                // 字符串常量,定义在代码段中,不能更改
    char str_array[] = "linux";         // 字符数组变量,定义在数据段中,可以更改

    // 正常的传参
    #if 0
    func1(str);                         // 编译报错,便于及时发现错误
    printf("str = %s.\n", str);
    #endif
    func2(str_array);
    printf("str_array = %s.\n", str_array);

    // 错误的传参
    func2(str);                         // 编译木有任何提示,但运行出现段错误,这就比较郁闷了。
    printf("str_array = %s.\n", str_array);

    return 0;
}

#if 0
void func1(const char *p)
{                                       // 此处不应该改变*p的内容,所以编译报错,便于及时发现错误
    *p = 'a';                           // error: assignment of read-only location ‘*p’
}
#endif

void func2(char *p)
{
    *p = 'b';
}

  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值