精华内容
下载资源
问答
  • C语言函数传参

    2017-08-07 15:19:53
    最近在做一些笔试面试之类的题目,今天遇到一个关于C语言函数传参的问题,感觉自己的概念还是有点模糊,本篇文章再学一下关于函数传参。这是遇到的一个题目:(高质量C++编程指南) #include #include #include ...

    最近在做一些笔试面试之类的题目,今天遇到一个关于C语言函数传参的问题,感觉自己的概念还是有点模糊,本篇文章再学一下关于函数传参。

    这是遇到的一个题目:(高质量C++编程指南)

    
    #include <stdlib.h>
    #include <stdio.h
    #include <string.h>
    
    void GetMemory(char *p, int num)
    {
        p = (char *)malloc(sizeof(char) * num);
    }
    
    int main()
    {
        char *str = NULL;
        GetMemory(str, 100);
        strcpy(str, "hello");
        printf("%s\n", str);
        return 0;
    }
    

    首先malloc没有free,然后我们传过去的指针并没有被分配内存,由以下结果可见:

    使用内存检测工具valgrind的结果:

    这里写图片描述

    下面具体分析一下这个程序:

    当str传过去的时候,我们相当于执行了一个这样的操作:p = str,即将我们的str赋给p,然后我们给p动态分配一块内存,那么问题来了,p得到了内存,那么和我们的str有毛关系呀?!我们的str还是所谓的NULL。。。因此我们必须要传我们这个指针的地址过去嘛:

    然后我们修改这个程序:

    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    void GetMemory(char **p, int num)
    {
        *p = (char *)malloc(sizeof(char) * num);
    }
    
    int main()
    {
        char *str = NULL;
        GetMemory(&str, 100);
        strcpy(str, "hello");
        printf("%s\n", str);
        free(str);
        return 0;
    }

    检测结果:

    这里写图片描述

    下面就谈一谈我们的函数传参的几种方式:

    1. 值传递
    2. 地址传递
    3. 引用传递

    值传递

    void Exchg1(int x, int y)   //形参
    {
       int tmp;
       tmp = x;
       x = y;
       y = tmp;
       printf("x = %d, y = %d\n", x, y);
    }
    main()
    {
       int a = 4,b = 6;
       Exchg1(a, b);   //实参
       printf("a = %d, b = %d\n", a, b);
       return(0);
    }

    本段代码的结果显而易见,不可能将a,b的值交换出来。

    我们和开始一样,人为替它加上几行代码,就清楚多了:

    x = a;
    y = b;

    那么这里还是那个问题,无论x,y干嘛,都与a,b毫无瓜葛。如果我们要用值传递,必须要用返回值返回我们的改变的结果。

    地址传递

    void Exchg2(int *px, int *py)
    {
       int tmp = *px;
       *px = *py;
       *py = tmp;
       printf("*px = %d, *py = %d.\n", *px, *py);
    }
    main()
    {
       int a = 4;
       int b = 6;
       Exchg2(&a, &b);
       printf("a = %d, b = %d.\n”, a, b);
       return(0);
    }

    如果不通过返回值来传递参数,那么我们就要传递实参的地址,因此交换的函数的形参应该改为指针,指针存储地址嘛,我们再人为加几行代码试试看:

    px = &a;
    py = &b;

    这里px和py即是a和b的地址,因此我们对这俩地址操作,即是对a,b进行操作。

    引用传递

    void Exchg3(int &x, int &y) /* 注意定义处的形式参数的格式与值传递不同 */
    {
       int tmp = x;x = y;
       y = tmp;
       printf("x = %d, y = %d.\n", x, y);
    }
    main()
    {
       int a = 4;
       int b = 6;
       Exchg3(a, b); /*注意:这里调用方式与值传递一样*/
       printf("a = %d, b = %d.\n”, a, b);
    }

    这里的取地址符其实在C++中是引用,人为操作一下:

    &x = a;
    &y = b;

    这个操作称为引用,x、y分别引用了 a、b变量。这里仅适用于C++。

    参考博客:http://c.biancheng.net/cpp/html/494.html

    展开全文
  • c语言函数传参

    千次阅读 2018-05-24 13:53:14
    每个函数都解决一个小问题,当我们编写好函数后,就把精力转移到,函数之间的逻辑关系上。不必再纠结每一条语句,这样大大提高了编程的效率。 当需要实现相同功能时,可以将函数多次调用,提升代码的复用性。 同时在...

    引入:

    • 自定义函数的使用,使整个程序模块化。
    • 每个函数都解决一个小问题,当我们编写好函数后,就把精力转移到,函数之间的逻辑关系上。不必再纠结每一条语句,这样大大提高了编程的效率。
    • 当需要实现相同功能时,可以将函数多次调用,提升代码的复用性。
    • 同时在程序出现问题时,可以快速定位,解决问题。

    不过对于函数的参数调用时常会出现问题。


    1.使用中出现的两种情况

    • 主函数的值没有被修改(变量的传递)
    #include<stdio.h>
    int p(int a)
    {
        a=20;
        return a;
    }
    
    int main(void)
    {
        int a;
        a=10;
        p(a);
        printf("%d",a);//这里的a值没有被修改,依然是10
    
        return 0;
    }

    这里的自定义函数p,接收一个int类型的参数,并定义为a。
    虽然自定义函数和主函数的变量名,都为a,但是这里的a是两个变量。
    虽然函数也将a返回了,但是主函数并没有接收,忽略了p的返回值。
    所以主函数的a依然是10

    • 主函数的值被修改了(指针的传递)
    #include<stdio.h>
    void p(int *w)
    {
        *w=20;
    }
    
    int main(void)
    {
        int a;
        int *s = &a;
        p(s);
        printf("%d",a);
    
        return 0;
    }

    这里的自定义函数p,接收一个int类型的指针,命名为w。
    虽然这里的指针w,和主函数的指针s,名称不一样,但是他们两个都指向了内存中a的地址。
    通过指针的解引用,在内存中修改相关数值。
    主函数再次访问a时,就已经是修改过的值,即使自定义函数没有返回值,a的值已经改变。
    所以主函数的打印的结果为20


    2.从内存的角度分析上面的两种情况

    • 程序运行过程当中,所有的变量都是在内存中存储的
    • 当自定义函数创建时,定义的形参,都会分配新的空间(即使参数名称相同)
    • 新空间存储的内容,为上一级函数传入的内容。

      • 传入变量值时,新空间记录传入值。

        主函数实参地址存:10 / 自定义形参地址存:10
        两个地址存的数值相同,但两者没有联系,修改形参的值,实参值并不会修改

      • 传入变量指针时,新空间记录指针地址

        主函数变量地址存:10
        主函数指针指向:变量地址 自定义指针指向:变量参地址
        主函数指针的地址,和自定义函数指针的地址虽然不同
        但两者都指向了需要修改的内容,通过指针修改内存的值。


    3.不同的应用场景

    函数传递参数

    • 一般用于数字计算,不使用指针,防止无意间修改内存的值
    • 思路清晰,更好理解

    函数传递指针

    • 需要修改多个上级函数的值
    • 需要跨函数修改内容(两个函数之间没有调用关系)
    • 结构体等内存较大的传递,使用指针缩短传递时间

    4.函数参数的几种错误

    自定义函数创建的变量,返回指针参数

    #include<stdio.h>
    int *creat(void)
    {
        int a;
        int *p = &a;//应该为:static int *p = &a;
        a=20;
        return p;
    }
    
    int main(void)
    {
        int *place;
        place=creat();
        printf("%d",*place);
    
        return 0;
    }

    上面的程序运行时会出现错误,因为自定函数中创建的参数,存储期只有自定义函数运行的这段时间,当自定义函数结束后,自定义函数中声明的变量会被释放。自定义函数返回的指针所指向的地址也就没有意义了。这是用主函数解引用指针会出现错误。

    • 修改建议:
      • 返回变量值
      • 使用 static 将自定义函数存储在静态内存中,保证在程序退出前不被清除

    字符串变量名作为参数,原字符串会被修改

    #include<stdio.h>
    void pass(char *pa)
    {
        pa[1]='a';
        printf("%s",pa);
    }
    
    int main(void)
    {
        char list[10]="Humphrey";
        pass(list);
        printf("%s",list);
    
        return 0;
    }

    上面程序,自定义函数虽然没有返回值,但主函数中list被无意间修改了。
    这是由于字符串是以指针传入函数,自定义函数是在原始地址上进行修改。
    主函数再次访问就会出现修改过的结果。

    • 修改建议:在传入函数前新建字符串副本,将副本传入函数,通过对副本的操作,实现功能。这样就不会出现原始字符串被修改的情况。

    修改后的代码:

    #include<stdio.h>
    #include<string.h>
    void pass(char *pa)
    {
        pa[1]='a';
        printf("%s",pa);
    }
    
    int main(void)
    {
        char list[10]="Humphrey";
        char mid[10];
        strcpy(mid,list);
        pass(mid);
        printf("%s",list);
    
        return 0;
    }
    展开全文
  • C语言函数传参汇总

    千次阅读 多人点赞 2016-06-19 19:11:04
    关于C语言函数传参现在汇总于下: 数据原型 应用 调用格式 声明格式 int arr[3] 等价传递 foo(arr,3) foo(int *p,int n) int arr[3][4] 等价传递 foo(arr,3) foo(int(*p)[4],int n) int *p 等价传递 ...

    关于C语言函数传参现在汇总于下:

    数据原型 应用 调用格式 声明格式
    int arr[3] 等价传递 foo(arr,3) foo(int *p,int n)
    int arr[3][4] 等价传递 foo(arr,3) foo(int(*p)[4],int n)
    int *p 等价传递 foo(p) foo(int*p)
    int *p= NULL 初始化 foo(&p) foo(int**p)
    int **p 等价传递 foo(p) foo(int **p)
    int **P=NULL 初始化 foo(&p) foo(int ***p)
    char *argv[4] 等价传递 foo(argv,4) foo(char **p,int n)
    char (*p)[4] 等价传递 foo(p) foo(int(*p)[4])
    void func(int ,int) 回调传递 foo(func) void foo(void (*p)(int ,int))
    展开全文
  • 如果问C语言函数传参方式,大多数人答案是值传递和地址传递。值传递不改变原参的值,而地址传递(指针)传递会改变原参的值。 但实际上所有的传参方式有且仅有一种,就是值传递。值传递和地址传递都是通过拷贝形参...

    一 背景

    如果问C语言函数传参方式,大多数人答案是值传递和地址传递。值传递不改变原参的值,而地址传递(指针)传递会改变原参的值。

    但实际上所有的传参方式有且仅有一种,就是值传递。值传递和地址传递都是通过拷贝实参的值,值传递比较好理解,直接拷贝实参,而地址传递拷贝的值是指针变量所指向变量的地址。

    说到指针变量,一瞬间要想到3个概念。1 指针变量所指向变量的值;   2 指针变量所指向变量的地址; 3 指针变量本身自己所占的内存地址。

    int a = 2;
    int *p;
    p = &a;
    

    (对应上述代码: *p 对应第1个概念, p 对应第2个概念, &p 对应第3个概念; )

    二 值传递

    void swap(int a , int b){
    int temp = a;
           a = b;
           b = temp;
    }
    
    int main(void){
    
    int a = 4, b =10;
    printf("a=%d,b=%d",a,b);
    swap(a,b);
    printf("a=%d,b=%d",a,b);
    return 0;
    }

    这种方式形参拷贝了实参,并不能达到交换的目的。 

    三 地址传递

    第一种方式:

    void swap(int *a , int *b){
    int *temp = a;
           a = b;
           b = temp;
    }
    
    int main(void){
    
    int a = 4, b =10;
    printf("a=%d,b=%d",a,b);
    swap(&a,&b);
    printf("a=%d,b=%d",a,b);
    return 0;
    }

    上述这种方法虽然传入的是指针变量,但是由于函数传递过程中是值传递,传入的也是a和b的地址,只对地址进行了交换。所有也不能对实参的数进行交换。

    另外一种方式交换:

    void swap(int *a , int *b){
    int temp = *a;
           *a = *b;
           *b = temp;
    }
    
    int main(void){
    
    int a = 4, b =10;
    printf("a=%d,b=%d",a,b);
    swap(&a,&b);
    printf("a=%d,b=%d",a,b);
    return 0;
    }

    这种方式能交换成功,思考为什么呢?关键在于传递地址后,进行了取内容(*)操作,此时指针变量指向了原来的值,即从较低级的运算 到较高级的运算操作,就达到了交换实参的目的(笔者这样说这样方便理解和记忆)。现在回想值传递和第一种地址传递,就是因为它们缺少了这一跳转操作,无法完成改变实参。

     

    四 进一步思考

    在学习初始化链表操作时,往往有2种方法比较常用

    直接返回:

    #include<stdio.h> 
    #include<stdlib.h>
    
    
    struct node{
    int data;
    struct node *next;
    }; 
    typedef struct node* linkList;
    
    
    //直接返回
    linkList create(void){
            linkList p;
            p = (linkList )malloc(sizeof(node));
            p->data = 10;
            p->next = NULL;
    return p;
    }
    
    
    int  main(void){
    linkList head = create();
    printf("head->data=%d",head->data);
    
    return 0;
    }
    
    //输出: head->data= 10 

    通过传入地址:

    #include<stdio.h> 
    #include<stdlib.h>
    
    
    struct node{
    int data;
    struct node *next;
    }; 
    typedef struct node* linkList;
    
    
    
    
    //传入链表方式1
    create(linkList *p){
          ( *p) = (linkList )malloc(sizeof(node));
           ( *p)->data = 10;
            ( *p)->next = NULL;
    
    }
    
    // 下面这个方法是无法初始化的
    //传入链表 方式2
    void create2(linkList p){
          p = (linkList )malloc(sizeof(node));
           p->data = 10;
            p->next = NULL;
    
    }
    
    
    
    
    
    
    int  main(void){
    linkList  head = NULL;//注意 head是指针,传入是head是地址,为什么要传入&head,地址的地址
    create(&head);  //方式1有用
    //create2(head); //方式2无用
    printf("head->data=%d",head->data);
    
    return 0;
    }
    
    //输出: head->data= 10 

    思考为什么2个方式都是传入地址,但方式1有用,但是方式2无用呢。

    表象就是笔者前面所述,没有从较低级的运算 到较高级的运算操作,只是平级操作,达不到影响实参的目的。

    其实质就是:函数传参都是值传递,方式2也只是改变形参(拷贝)的内容,没有影响到实参内容。而加了(*p)操作后,此时相当于不是平级操作,跳到实参,达到了改变实参目的。

     

    五 总结

    以后看到值传递或者指针传递,要想到本质是值传递,都是拷贝实参内容,普通变量是拷贝的是变量值,而指针变量是所指向变量的地址。进行平级操作不能改变实参的值,而要想真正改变实参的值,需要(*)取内容跳级操作,才能影响到。对于想影响普通变量,需要一级指针;对于想影响一级指针(即前面笔者表述初始化链表的第2种方式),需要使用二级指针(地址的地址)。在交换时,都使用了跳转操作。

    展开全文
  • 结构体的指针和C语言函数传参

    千次阅读 2018-09-24 18:58:33
    首先从函数传参做起笔记,c语言函数传参的本质都是传值。我的大学教材上举例为传值和传地址; 我借用百度上的一些回答作为引用: (1)传值,就是把你的变量的值传递给函数的形式参数,实际就是用变量的值来新生...
  • c语言函数传参详解

    2017-10-22 12:50:00
    24.1.普通变量作为函数形参 (1)普通变量作为参数在函数传参时,形参和实参名字可以相同也可以不同,实际上都是用实参来替代相应的形参的。 (2)在字函数内部,形参的值等于实参。原因是函数调用时把实参的值赋值给了...
  • 24.C语言函数传参详解

    千次阅读 2016-03-22 16:03:24
    24.1.普通变量作为函数形参 24.2.数组和指针作为函数形参 24.3.结构体变量作为函数形参 24.4.传值调用与传址调用 24.5.函数形参和返回值 24.6.函数传参中使用const指针
  • ubuntu下C语言函数传参

    2018-11-18 17:34:36
    在进行C语言编程时,有时候需要使用命令行传参,下面对其进行一个分析。 int main(int argc,const char * agrgv[])   int argc 命令行参数个数(包含文件名)   const char* argv[] 指针数组:将命令行参数首...
  • C语言函数是必不可少的,所以传参问题必须要懂,实际运用的时候也会很方便 函数的形参在函数被调用的时候才会被分配地址空间的,而且使用后会被收回的。 传值 传址 指针传递 引用传递 传值 这个是很简单的,...
  • 文章目录前言代码:版本1分析原因执行`char *pData = 0;...今天同事问了一个问题:在函数参数中传递指针的指针,很常用的一个场景,重新梳理一下记录于此,以后如果有类似的问题直接发这篇小总结就可以了。
  • c语言函数传参时候的类型强制装换

    千次阅读 2018-10-02 16:44:11
    由于函数调用的参数是表达式,所以在参数传递给函数时候也可能进行类型转换。在没有函数原型的情况下,char与short类型都将被转换为int类型,float类型将被转换为double类型,因此,即使调用函数的参数为char或者...
  • C语言函数参数传递的三种方式 (1)传值,就是把你的变量的值传递给函数的形式参数,实际就是用变量的值来新生成一个形式参数,因而在函数里对形参的改变不会影响到函数外的变量的值。 (2)传址,就是传变量的...
  • 下面是实现二叉树线索化的简单算法,我想通过纯C代码实现指针参数的引用传递,使得函数中的修改得以保存,请问InTread()内的参数应该写成什么样的形式?注:下面的代码可能需要进一步修正;ThreadTree为结构体指针 ...
  • 详细说明C语言函数传参方式,值传递和地址传递

    千次阅读 多人点赞 2019-05-27 14:26:31
    /* b就是实参 */ } 总结一句话:形参是函数定义的时候用的,实参是调用函数的时候用的 函数的参数都是形参,只有在函数调用的时候系统才会为形参分配空间和地址,形参和实参不会是同一个内存地址 函数传参的值传递...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 14,801
精华内容 5,920
关键字:

c语言函数传参

c语言 订阅