C语言函数传递指针参数的问题详解

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

voidswap(inta,intb)

{

    inttemp=a;

    a=b;

    b=temp;

}

intmain()

{

    inta=4,b=5;

    swap(a,b);

    printf("a = %d ,b = %d\n",a,b);

    return0;

}

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

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

code case 2

#include

voidswap(inta,intb)

{

    printf("address in swap():%p %p\n",&a,&b);

    inttemp=a;

    a=b;

    b=temp;

}

intmain()

{

    inta=4,b=5;

    printf("address in main():%p %p\n",&a,&b);

    swap(a,b);

    printf("a = %d ,b = %d\n",a,b);

    return0;

}

它的运行结果为:

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语言/C++的学习裙【 六二七,零一二,四六四 】,无论你是大牛还是小白,是想转行还是想入行都可以来了解一起进步一起学习!裙内有开发工具,很多干货和技术资料分享!

那么在C语言里如何才能交换两个变量的值呢?

方法是通过指针传参,看下面的代码

code case 3

#include

voidswap(int*a,int*b)

{

    printf("address in swap():%p %p\n",a,b);

    inttemp=*a;

    *a=*b;

    *b=temp;

}

intmain()

{

    inta=4,b=5;

    printf("address in main():%p %p\n",&a,&b);

    swap(&a,&b);

    printf("a = %d ,b = %d\n",a,b);

    return0;

}

运行结果为:

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和地址就明白了;

code case 4

#include

voidswap(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);

    inttemp=*a;

    *a=*b;

    *b=temp;

}

intmain()

{

    inta=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);

    return0;

}

运行结果:

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

通过结果我们知道,在swap里,指针a和b的值和main()里的a和b的地址是一样的,那么对*a进行的各种赋值实际上就是对main()里的a的各种操作,它们代表同一储存空间的的值;但是对swap里的a,和b的地址,和main里的是不一样的,这是显然的,a只是一个容纳&a地址的变量罢了,它是swap里重新分配的一块内存,并且,它的类型和main里的a,b类型完全不同,它是一个指针类型;

用比喻的方法来讲,在多行书架上,每行各自放了一本书,现在我想把a,b这两本书交换一下位置,我想让你帮我交换一下,你要怎么做呢?好了,我告诉你a书在第一行书架上,b在第四行书架上,现在,你可以做了吧,你首先会取出第一行的a书,将其拿出放在左手,然后用右手取出位于第四行的b书,放在第一行上,再将位于左手上的a书放到第四行上,至此,交换完成。

仔细想想,在这个过程中你不就相当于充当了swap这个函数的作用吗?我告诉了你a,b的地址,你真正交换了它

当然,我想探讨的并不是只有这些,在文章一开始,我就引入了这样的话题,我们先看一下这段代码问题:

#include

#include

typedef struct LNode

{

    intdata;

    struct LNode *next;

}LNode;

voidInitLinkList(LNode *L)

{

    L=(LNode *)malloc(sizeof(LNode));

    L->data=0;

    L->next=NULL;

}

intmain()

{

    LNode *L=NULL;

    InitLinkList(L);

    printf("%p\n",L);

    return0;

}

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

我想,如果你能正确理解前面的几个例子,那么,你的答案一定回答的是NO,该InitLinkList并不能真正初始化一个链表头结点,在函数里我们的确是给L分配了内存,初始化了结点,但是,InitLinkList()里的L并不是main()里的L,虽然名称是一样的,但是InitLinks()的L是局部的(所以,其实你写成a,b,c,d都没关系),传进来的只是一个LNode*副本,这个副本和外面的L的内容是一样的,但是变量不是同一个,当这个子函数执行完后,main()里的L还是原来的L。

(注意!在InitLinkList函数中通过malloc分配的内存是通过堆来划分的,这意味着函数调用完毕后,内存不能自动释放,将会造成内存泄漏,并且,此代码中malloc申请的内存是悬浮的)

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

修改代码如下

#include

#include

typedef struct LNode

{

    intdata;

    struct LNode *next;

}LNode;

LNode * InitLinkList(LNode *L)

{

    L=(LNode *)malloc(sizeof(LNode));

    L->data=0;

    L->next=NULL;

    returnL;

}

intmain()

{

    LNode *L=NULL;

    L=InitLinkList(L);

    printf("%p\n",L);

    return0;

}

运行结果

?

1006D1588

改过后的InitLinkList初始化了头结点,并把生成节点的地址传递给上一层的main中的L,所以得到了正确的结果

(实际上,写成InitLinkList(LNode *L)可能不是一种必要的方式,完全可以写成void,这两者是等价的)

对比交换a,b值那样,我们也可以这样改

#include

#include

typedef struct LNode

{

    intdata;

    struct LNode *next;

}LNode;

voidInitLinkList(LNode **L)

{

    (*L)=(LNode *)malloc(sizeof(LNode));

    (*L)->data=0;

    (*L)->next=NULL;

}

intmain()

{

    LNode *L=NULL;

    InitLinkList(&L);

    printf("%p\n",L);

    return0;

}

运行结果

?

1006B1588

(注:采用此种方式是及其复杂的,因为这是一个二级指针,会增加理解难度,所幸的是,c++中的引用,可以避免这个问题)

想一下,为什么,

在swap(int *a,int *b)中,a,b是指针变量,它们自身有个地址&a,&b,a,b的内容是存放传递过来的变量地址(也即是main()里a,b的地址),用*a和*b就是取当前所存放地址的值,也就是说,a,b指向main里a,b的内存块地址,*a和*b相当于是直接对main()里的a,b其操作,所以能到达交换(改变值)的目的是显然的。

在这里main里的L是一个指针类型,本身就指向某块内存,而L这个变量的地址作为内容传递给initLinkList()函数,那么子函数里面的L的内容不就是main()里L地址么,那么,*L不就是main里L的内容么,也就是说,对*L操作就是对main()里的L进行操作。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,794评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,050评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,587评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,861评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,901评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,898评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,832评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,617评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,077评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,349评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,483评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,199评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,824评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,442评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,632评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,474评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,393评论 2 352

推荐阅读更多精彩内容