程序媛大神教你学C语言编程——函数参数的按值传递机制

C语言是面向过程的,而C++是面向对象的

C和C++的区别:

C是一个结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到输出(或实现过程(事务)控制)。

C++,首要考虑的是如何构造一个对象模型,让这个模型能够契合与之对应的问题域,这样就可以通过获取对象的状态信息得到输出或实现过程(事务)控制。 所以C与C++的最大区别在于它们的用于解决问题的思想方法不一样。之所以说C++比C更先进,是因为“ 设计这个概念已经被融入到C++之中 ”。

C与C++的最大区别:在于它们的用于解决问题的思想方法不一样。之所以说C++比C更先进,是因为“ 设计这个概念已经被融入到C++之中 ”,而就语言本身而言,在C中更多的是算法的概念。那么是不是C就不重要了,错!算法是程序设计的基础,好的设计如果没有好的算法,一样不行。而且,“C加上好的设计”也能写出非常好的东西。

很多小伙伴都老是会碰到疑问,其实还是基础没打扎实,这些题如果你不看答案你能知道多少呢?如果还有很多不知道就证明基础没打扎实,如果你还在入门纠结,如果你还在苦恼怎么入门!小编推荐一个学C语言/C++的学习裙【 六九九,四七零,五九六 】,无论你是大牛还是小白,是想转行还是想入行都可以来了解一起进步一起学习!裙内有开发工具,很多干货和技术资料分享!

函数参数的按值传递机制:

给函数传递参数时,参数值不会直接传递给函数,而是先生成参数值的一份副本,存储在内存中的栈上,再把这个副本用于函数语句里,而不是使用初始值,我们来用一个程序更清楚的说明这一点:

#include

void swap(int a,int b)

{

int temp;

temp = a;

a = b;

b = temp;

}

void main()

{

int a = 1;

int b = 2;

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

swap(a,b);

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

}

程序运行结果:

a=1,b=2

a=1,b=2

可能有人会不解,为什么我们在调用了swap(a,b)函数后,a、b的值并没有发生交换,这就是我们说的按值传递机制,我们在把a、b作为参数传递给swap函数时,这里其实是生成了a、b的副本再供swap函数使用的,所以就算是a、b的副本发生了交换,a、b本身也还是原来的值。我们在这里要达到我们想要的功能,就要使用指针,修改如下:

#include

void swap(int *a,int *b)

{

int temp;

temp = *a;

*a = *b;

*b = temp;

}

void main()

{

int a = 1;

int b = 2;

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

swap(&a,&b);

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

}

我们只是把函数swap里的所有a、b的部分分别换成了*a、*b,同时我们传递了&a和&b给swap函数,这样就能得到预期结果了。我们来看看我们到底做了何种修改,首先我们把a、b的地址(&a和&b)传递给swap函数,尽管这里还是会拷贝a、b地址的副本(我们假定是指针*pa和*pb)传递给swap,但是这两个副本pa和pb仍然是指向a、b的指针,它们和&a、&b是不同的指针,但是却指向了同一个地址,所以当我们使用“*指针”这种形式时就可以改变该地址内存放的值了。

可能有人要问C语言为什么要采用这种方式来传递参数呢,直接使用原来的实参不好吗?你想一个问题,假设C不使用按值传递,就拿上面的swap函数举例,那么当我们使用swap(1,2)这样调用函数时会发生什么?我们难道要把常量1和2进行直接交换吗,让1=2,2=1?这绝对不行。提一下在Pascal编程语言中,为了不给调用者的变量带来影响,在定义函数的时候,会特别地指定参数为变量参数,如果试图给参数为变量的函数传递常量,编译器就会报错。但是C语言讲究的是简单实用,所以它不可能背负常量修改这样的危险,也没有采用Pascal那样麻烦的方式来在语法上做文章,所以就使用了这种按值传递的方式来传递参数。

函数声明:

可能有的人会碰到这种情况,当他把上面的代码写成

void main(){...}

void swap{...}

这种顺序时程序就会报错,这时候就是编译器没有正确识别swap函数导致的,这就涉及到函数声明了。函数声明是一个定义函数基本特性的语句,它定义了函数的名字、返回值类型和每个参数的类型。事实上,可以将它编写为与函数头一模一样,只是要在尾部加一个分号表示语句结束。函数声明也叫作函数原型,因为它提供了函数的所有外部规范。函数原型能使编译器在使用这个函数的地方创建适当的指令,检查是否正确地使用它。在程序中包含头文件时,这个头文件就会在程序中为库函数添加函数原型。例如,头文件中就含有printf和scanf的函数原型,这个文件我们在C-Free 5mingwinclude目录下就可以找到。所以,如果你想把swap的定义放在main函数后面,可以像下面这样使用函数声明:

#include

void swap(int *,int *);

//也可以写成void swap(int *a,int *b);

void main()

{

int a = 1;

int b = 2;

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

swap(&a,&b);

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

}

void swap(int *a,int *b)

{

int temp;

temp = *a;

*a = *b;

*b = temp;

}

函数指针:

可能有人不理解我为什么要把函数和数组放在一章讲,主要是因为它们都和指针有莫大的关系,前面我们说了数组名就是一个特殊的指针,只是要注意sizeof(数组名)和&数组名两种特殊情况。其实函数名也是一种特殊的指针,我们的程序被编译器翻译成汇编语言时,函数会被翻译成为一段相对独立的汇编代码,函数名就是该段代码的首地址,每个函数的最后都会有一个ret或retn汇编指令,其实它就对应我们函数的return语句,不要以为有的函数没有return语句,其实只是因为该函数是返回空(void)写做return;所以就把return省略掉了。稍微修改下上面的程序:

小编推荐一个学C语言/C++的学习裙【 六九九,四七零,五九六 】,无论你是大牛还是小白,是想转行还是想入行都可以来了解一起进步一起学习!裙内有开发工具,很多干货和技术资料分享!

,当我们使用void (*p)(int*,int*) = swap;这一句时其实就是定义了一个函数指针p,我们让指针p的值赋值为swap函数名(在我们这里是地址0x00401014),然后我们可以发现我们可以使用p(&a,&b)这样的方式来调用函数了,就和使用swap(a,b)完全一样,。当然我们还可以在最后再加上两句:

可以看到我们居然可以使用((void(*)(int*,int*))0x00401014)(&a,&b);这样的方式来通过地址0x00401014调用函数,(void(*)(int*,int*)其实就是定义了一个函数指针,(*)表示它是一个函数,void表示该函数返回值为空,(int*,int*)表示函数有两个int*类型的参数,然后我们使用它把地址0x00401014进行强制类型转换,这样我们的地址0x00401014就会被当成一个函数来使用了,要特别注意里面的括号,不要省略了,不然会报错的。

前面说了数组名作为指针有两种特殊的情况,那么函数名是不是也有sizeof(函数名)和&函数名这两种特殊情况呢,多说无益,当你不确定一件事时,就用代码把它打印出来:

这是我在C-Free5上面的打印结果,在dev-c++也差不多,但是在vc6上面就会报错,原因就是sizeof后面跟函数名会被vc6默认是违法操作,因为函数的代码数量是不确定的,所以函数的大小也不能确定,尽量不要出现sizeof(add)这样的操作,C-Free5和dev-c++则采用了相对比较温和的方式来处理;而"函数名=&函数名"这条确是成立的。

下面我们再来看看数组作为函数参数的情况下,我们使用sizeof会发生什么变化:

小编推荐一个学C语言/C++的学习裙【 六九九,四七零,五九六 】,无论你是大牛还是小白,是想转行还是想入行都可以来了解一起进步一起学习!裙内有开发工具,很多干货和技术资料分享!

可以看到我们在main函数里调用sizeof(a)=40,而在作为参数传递给array函数后,再次使用sizeof(a)时大小就只剩下4了,这是为什么呢,因为数组名是一个特殊的指针,当它作为参数传递给函数时,这时候就会拷贝a的副本传递给函数,这个副本就是一个大小为4字节的指针,所以我们在函数调用里得到参数数组a的大小为4。如果我们要得到真实的数组大小,我们通常会采用额外的处理方式,一种就是我们前面在写压缩和解压函数时用到的,我们把数组名和数组大小都作为参数传递给函数;另一种是我们在数组的即为加上一个特殊的符号表示数组结束,比如字符串的结尾就是一个'',我们只要使用循环读取元素一直到结束符就可以得到数组大小,当然也可以用其它的符号表示结束,比如-1,这都是你根据实际情况自己定义的。

今天就到这里,欲知后事如何且听下回分解(手动滑稽)~

这些是C/C++能做的

服务器开发工程师、人工智能、云计算工程师、信息安全(黑客反黑客)、大数据 、数据平台、嵌入式工程师、流媒体服务器、数据控解、图像处理、音频视频开发工程师、游戏服务器、分布式系统、游戏辅助等

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

推荐阅读更多精彩内容