高级语言C的形式参数

发布 2021-04-23 12:55:28 阅读 9064

#include

using namespace std;

void foo1(int arr[100])

cout <

void foo2(int (&arr)[100])

cout <

void main()

int a[100];

cout <

foo1(a);

foo2(a);

其运行结果如下:

in main function : 400

pass by pointer: 4

pass by reference: 400

这段**说明了,如果数组形参是数组名形式(或者指针形式,下文讨论)时,使用sizeof运算符,将得不到原来数组的长度;如果用传递原数组引用的方法,则没有问题。

这段**的确很难理解,因为这短短的十几行涉及到了形参与实参的关系、数组名和指针的关系、引用的意义、声名和表达式的关系这4大类问题,只要有1条理解不透、或者理解不正确,就理解不透上面的这段**。本文也就从这4个问题入手,把这4个问题首先解决掉,然后再**上面的这段**。虽然这样看来很是繁复,但是我认为从根上入手来理解、学习,是条似远实近的道路。

一、函数形参和实参的关系。

void foo(int a);

foo(10);

这里的a叫做形式参数(parameter),简称形参;这里的10叫做实际参数(argument),简称实参。形参和式参之间是什么关系呢?他们是赋值的关系,也就是说:

把实参传递给形参的过程,可以看作是把实参赋值给形参的过程。上面的例子中,实参10传递给形参a,就相当于a=10;这个赋值的过程。(因为数据类型多的很,无法举例子举全面,所以这里就不举例子了;如果觉得不好理解,就在vc中写个sample调试一下各种数据类型的情况,你就能够验证这个结论了。

)二、数组名和指针的关系。

这个问题是个历史性的问题了,在c语言中,数组名是当作指针来处理的。更确切的说,数组名就是指向数组首元素地址的指针,数组索引就是距数组首元素地址的偏移量。理解这一点很重要,很多数组应用的问题就是有此而起的。

这也就是为什么c语言中的数组是从0开始计数,因为这样它的索引就比较好对应到偏移量上。在c语言中,编译过程中遇到有数组名的表达式,都会把数组名替换成指针来处理;编译器甚至无法区分a[4]和4[a]的区别!*2 但是下面这一点需要注意:

int a[100];

int *b;

这两者并不等价,第一句话声明了数组a,并定义了这个数组,它有100个int型元素,sizeof(a)将得到整个数组所占的内存大小,是400;第二句话只是声明并定义了一个int型的指针,sizeof(b)将得到这个指针所占的内存大小,是4。所以说,虽然数组名在表达式中一般会当作指针来处理,但是数组名和指针还是有差距的,最起码有a==&a[0]但是sizeof(a)!=sizeof(a[0])。

并且在ansi c标准中,也明文规定:在函数参数的声明中,数组名北边一起当作指向该数组第一个元素的指针。所以,下面的几种书写形式是等效的:

void foo1(int arr[100]){

void foo2(int arr)

void foo3(int *arr){}

c++尽可能的全面兼容c语言,所以这一部分的语法相同。

三、引用的意义。

引用“是c++中引进的概念,c语言中没有。它的目的在于,在某些方面取代指针。如果你认为引用和指针并无大不同,肯定会为指针报不平,颇有一种“即生亮何生瑜”的感慨;但是,引用确实有新的特色,也确实在很多地方的表现和指针有所不同,本文就是一例。

使用引用,我们要把握这它最最最重要的一点,这也是它和指针最大的区别:引用一经定义,就和被它引用的变量紧紧地结合在一起,再不分开,对引用的任何操作都反映在它引用的变量上;而指针,只是访问它指向变量的另一种方式,两者虽有联系,但是并不像引用那样密不可分。:)

#include

using namespace std;

void main()

int a = 10;

int & a_ref = a;

int b = 20;

//int & b_ref ; error c2530: 'b_ref' :references must be initialized定义引用时就要初始化,说明引用跟它指向的元素密不可分。

int & b_ref = b;

int * p;

int * q;

//下面的结果证明了:引用一经定义,就不能再指向其他目标; /把一个引用b_ref赋值给另一个引用a_ref,其实就是把b赋值给了a.

cout <

a_ref = b_ref;

cout <

cout <

cout <

//即使对一个引用a_ref取地址,取得也是a的地址。已经“恶鬼附体”了:)

p = a;

q = a_ref;

cout <

cout <

//下面这段**展示了指针与引用的不同。

p = a;

q = b;

cout <

p = q;

cout <

cout <

system("pause");

下面是运行的结果,以供参考:

0012fed4 0012fed4

0012fed4 0012febc

0012febc 0012febc

四、声明和表达式的关系。

这里想说明的是,分析一个声明可以把它看作一个表达式,按照表达式中的运算符优先级顺序来声明。比如int (&arr)[100],你首先要找到声明器arr,那么&arr说明arr是一个引用。什么引用呢?

在看括号外面,说明了这一个数组,100说明这个数组有100个元素,前面的int说明了这个数组的每个元素都是int型的。所以,这个声明的意思就是:arr就是指向具有100个int型元素的数组的引用。

如果你觉得这种理解很晦涩,那你就不妨用typedef来简化声明中的复杂的运算符优先级关系,比如下面的形式就很好理解,其效果是和最初的那个例子是一样的:

#include

using namespace std;

typedef int intarr[100]; 这个,这个。也可以用表达式来理解,有点“gnu is not unix“的味道是吧?

void foo(intarr &arr) /noh,这样看就很明白了,就是传了个引用进去。

cout <

void main()

intarr a用类型别名来定义a

intarr &a_ref=a; /用类型别名来定义引用a_ref

cout <

foo(a);

system("pause");

==大结局===

吐沫星乱飞了半天,大家感觉还好吧,快结束了,大家再忍耐一下。看看下面这段程序:

#include

using namespace std;

void main()

int a[100];

int * pa = a;

int (&a_ref)[100] =a;

cout <

cout <

cout <

system("pause");

怎么样,是不是对输出结果感到很自然呢?如果是,那就好办了。我总结一下就下课哈!

^_数组名在表达式中,往往被当作是指向首元素a[0]地址的指针,但是在sizeof(a)中,返回的结果是数组a占用内存的大小;pa是指向a的指针,他也指向a[0],但是sizeof(pa)中,返回结果是pa这个指针所占内存空间的大小,之所以这样,因为pa这个指针和数组a的结合不够紧密,属于访问数组a的第二被选方案;a_ref这个引用,就是对数组a的引用,就像“恶鬼附体”一样,一旦附体附上了,你怎么也甩不掉它,对它的任何操作,全部都反映在a上。在看本文最初的那个例子,比这个例子所增加的操作就是函数实参到形参的传递,我们在上面说过了,从实参到形参的传递可以看作是把实参赋值给形参。所以本文最初的那个例子,其实际的操作过程就和本文最后的这个例子是一样的。

所以,并非函数把数组给“降阶”了,而是它原原本本就该这样,千万不必奇怪。 :p

意犹未尽,在ps一段:在c语言中,没有引用,是怎么解决这种问题呢。下面是常用的几种作法:

1.传递数组的时候,在增加一个参数,用来记录数组的元素个数或者长度。main(int argc, char **args)就是这种做法;这种方法还可以防止溢出,安全性比较高。

2.在数组的最后一个有效元素后面作一个标志,指明数组已经结束了。c语言中用char数组表示字符串,传给相关的字符串函数,用的就是这种做法。

这种方法保证了c的所谓字符串是无限长度的,因为用一个变量表示数组的长度的话,终归会受到这个变量类型的限制,比方说这个变量是unsigned byte型的,那么字符串长度就不能超过256,否则这个变量就溢出了。

3.对于多维数组,通常的方法是在最后一个有效维后面做一行标志,比如a[3][3]=,如果我的程序用不到-1,我可以拿-1来填充最后一行,作为标志。这样在函数内部检测到某一维的元素都是-1,就说明到底了。

方法是灵活多变的,关键看人怎么用了。c老爹dennis ritchie曾经说过:c诡异离奇,缺陷重重,却获得了巨大的成功。

注1:本文将不再引用“降阶”这个术语,原因是我认为这个“降阶”的概念有种把类似2维数组压扁到1维的意思,其实本文讨论的并不是这个问题,本文讨论的是数组形参传递过程中数组长度损失的问题(这么说也不准确,还是看文中的讨论吧)。

注2:c语言的编译器遇到数组元素arr[i],就会替换成*(arr+i)的形式。

高级语言C

第一章概述。程序 用某种计算机能够理解并执行的计算机语言描述解决问题的方法步骤。机器语言 采用计算机直接识别和执行的二进制 来表示的程序设计语言。汇编语言 采用文字方式表示的程序设计语言,其中大部分指令和机器语言中指令十一一对应的。高级语言 是描述算法过程方便 同时脱离了对机型的要求,能在任何计算机...

作业,高级语言,c语言

类型 sizeof 数组下标圆括号成员选择 对象 成员选择 指针 除乘余数 取模 数组名 常量表达式 表达式 函数名 形参。对象。成员名对象指针 成员名。表达式 数据类型 表达式 变量名 变量名 变量名 变量名 指针变量 变量名!表达式 表达式sizeof 表达式 表达式 表达式表达式 表达式整型表...

c语言高级编程

c高级编程 责任编辑 admin 更新日期 2005 8 6 深入了解c语言 函数的参数传递和函数使用参数的方法 tangl 99 原作 关键字 c语言,汇编,生成,编译器 c语言生成的 在执行效率上比其它高级语言都高。现在让我们来看看c语言生成的 具体是什么样子的。当你看完本文对于c语言的了解一定...