C语言 关于指针传递参数问题

一个问题是,我们想用一个函数来对函数外的变量v进行操作,比如,我想在函数里稍微改变一下这个变量v的值,我们应该怎么做呢?又或者一个常见的例子,我想利用swap()函数交换两个变量a,b的值,我们应该怎么做呢(好吧,博主是觉得这个问题十分经典)。

如果你真的理解C语言中【函数】这个工具的本质,我想你稍微仔细的思考一下,可能就不会来查看博主的这篇文章,对函数来说,它所传递的任何参数仅仅是原来参数的一个拷贝,所以,对任何企图通过void swap(int a,int b)来交换a,b值或者想通过void alter(int v)来改变v的值,都是徒劳的。

C语言里,改变值只能通过指针(地址)方式进行传递,或许你会说传递数组不是也可以改变值么,实际上,传递数组就是传递指针(或许对数组来说,这个指针有点特别)//注意:C里没有引用,C++里才有

我们先来看一下有趣的swap函数,它用于交换a,b两个变量

code case 1

#include <stdio.h>
void swap(int a,int b)
{
    int temp=a;
    a=b;
    b=temp;
}
int main()
{
    int a=4,b=5;
    swap(a,b);
    printf("a = %d ,b = %d\n",a,b);
    return 0;
}

不出意料的,我们会知道这段代码其实并不能得到我们想要的结果,它并不能交换两个变量a,和b,的值,这是为什么?

我们不妨修改这段代码,在main()和swap()里分别打印a和b的地址,看看到底发生了什么;我们修改代码如下:

#include <stdio.h>
void swap(int a,int b)
{
    printf("address in swap():%p %p\n",&a,&b);
    int temp=a;
    a=b;
    b=temp;
}
int main()
{
    int a=4,b=5;
    printf("address in main():%p %p\n",&a,&b);
    swap(a,b);
    printf("a = %d ,b = %d\n",a,b);
    return 0;
}

运行结果为:

address in main():0061FF2C 0061FF28
address in swap():0061FF10 0061FF14
a = 4 ,b = 5

显然,在两个函数里,它们的地址并不相同,这意味着,它们并不是相同的存储空间,改变swap里的值,实际上仅仅只改变了swap()里面的a和b的值罢了,一旦swap执行完,swap里的a和b的储存空间立即释放掉,对于main()里的a和b,没有半点影响。

那么在C语言里如何才能交换两个变量的值呢?
方法是通过指针传参,看下面的代码
code case 3
 

#include <stdio.h>
void swap(int *a,int *b)
{
    printf("address in swap():%p %p\n",a,b);
    int temp=*a;
    *a=*b;
    *b=temp;
}
int main()
{
    int a=4,b=5;
    printf("address in main():%p %p\n",&a,&b);
    swap(&a,&b);
    printf("a = %d ,b = %d\n",a,b);
    return 0;
}

运行结果为

address in main():0061FF2C 0061FF28
address in swap():0061FF2C 0061FF28
a = 5 ,b = 4

这样,就把a,b的值交换了!
等等,我们分析一下它的原理,它究竟做了哪些变化呢,在swap函数里,我们将a和b的地址给了swap函数,作为形参,在swap函数中,a和b是指向两个int 类型的指针,它们接受了main里面a和b的地址,也就是a=&a (in main());b=&b (in main());所以对*a实际上就是对a(in main())操作啦;
那么,聪明的你肯定能想到,在swap()函数里变量a和b的地址肯定和main里a和b的地址是不同的,swap里的a,b的地址是指针的地址(在swap里a,b是指针),而它们的值是在main()里面a和b的地址;
我们不妨打印一下swap里a,b和地址就明白了;
 

#include <stdio.h>
void swap(int *a,int *b)
{
    printf("address in swap(),the value of a and b:%p %p\n",a,b);
    printf("address in swap(),the address of a and b:%p %p\n",&a,&b);
    int temp=*a;
    *a=*b;
    *b=temp;
}
int main()
{
    int a=4,b=5;
    printf("address in main(),the address of a and b:%p %p\n",&a,&b);
    swap(&a,&b);
    printf("a = %d ,b = %d\n",a,b);
    return 0;
}

运行结果

address in main(),the address of a and b:0061FF2C 0061FF28
address in swap(),the value of a and b:0061FF2C 0061FF28
address in swap(),the address of a and b:0061FF10 0061FF14
a = 5 ,b = 4

问:该代码能否正确初始化一个链表头结点?

#include <stdio.h>
#include <malloc.h>
typedef struct LNode
{
    int data;
    struct LNode *next;
}LNode;
void InitLinkList(LNode *L)
{
    L=(LNode *)malloc(sizeof(LNode));
    L->data=0;
    L->next=NULL;
}
int main()
{
    LNode *L=NULL;
    InitLinkList(L);
    printf("%p\n",L);
    return 0;
}


我想,如果你能正确理解前面的几个例子,那么,你的答案一定回答的是NO,该InitLinkList并不能真正初始化一个链表头结点,在函数里我们的确是给L分配了内存,初始化了结点,但是,InitLinkList()里的L并不是main()里的L,虽然名称是一样的,但是InitLinks()的L是局部的(所以,其实你写成a,b,c,d都没关系),传进来的只是一个LNode*副本,这个副本和外面的L的内容是一样的,但是变量不是同一个,当这个子函数执行完后,main()里的L还是原来的L。
(注意!在InitLinkList函数中通过malloc分配的内存是通过堆来划分的,这意味着函数调用完毕后,内存不能自动释放,将会造成内存泄漏,并且,此代码中malloc申请的内存是悬浮的)

但是,在大多数时候,我们却的确是需要这样一个函数来为我们做这些事情,那么,应该怎么修改呢?

#include <stdio.h>
#include <malloc.h>
typedef struct LNode
{
    int data;
    struct LNode *next;
}LNode;
LNode * InitLinkList(LNode *L)
{
    L=(LNode *)malloc(sizeof(LNode));
    L->data=0;
    L->next=NULL;
    return L;
}
int main()
{
    LNode *L=NULL;
    L=InitLinkList(L);
    printf("%p\n",L);
    return 0;
}

或者我们可以使用二级指针

#include <stdio.h>
#include <malloc.h>
typedef struct LNode
{
    int data;
    struct LNode *next;
}LNode;
void InitLinkList(LNode **L)
{
    (*L)=(LNode *)malloc(sizeof(LNode));
    (*L)->data=0;
    (*L)->next=NULL;
}
int main()
{
    LNode *L=NULL;
    InitLinkList(&L);
    printf("%p\n",L);
    return 0;
}


 

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值