深入探讨C++语言中参数传递问题

深入探讨C++语言中参数传递问题
深入探讨C++语言中参数传递问题

第23卷哈尔滨师范大学自然科学学报

V o.l 23,N o .42007

第4期

NATURAL SCIENCES J OURNAL OF HARBIN NORM AL UN I V ERS I TY

深入探讨C++语言中参数传递问题

刘志华

(哈尔滨广播电视大学)

摘要 C++语言程序设计中函数调用中的参数传递是非常重要的内容,不

同类型的参数传递的方式也不相同,在开发项目编写程序中,选择合适的参数进行传递,将使你的程序简洁清晰,运行效率提高.

关键词:C ++语言;函数调用;参数传递

收稿日期:2007-04-02

0 引言

函数是C++语言程序中的基本功能模块和执行单元,C ++语言程序的功能可以通过函数之间的调用来实现,C ++函数分为标准函数(库函数)和用户自定义函数,一个完整的C ++语言程序是由一个或多个函数组成的.C ++语言中的函数相当于其他语言中的子程序.

定义函数的目的是使用它,而函数的使用就是进行函数调用.调用一个函数就是暂时中断现有程序的运行,转去执行被调用函数,当被调用函数执行结束以后,在返回到中断处继续执行的过程.

当调用一个函数时,整个调用过程分为三步进行,第一步是参数传递;第二步是函数体执行,第三步是返回,即返回到函数调用表达式的位置.参数传递称为实虚结合,即实参向形参传递信息,使形参具有确切地含义(即具有对应的存储空间和初值).这种传递又分为两种不同情况,一种是向非引用参数传递;另一种是向引用参数传递.

形参表中的非引用参数包括普通类型的参数、指针类型的参数和数组类型的参数三种.实际

上可以把数组类型的参数归为指针类型的参数.

当形参为非引用参数时,实虚结合的过程为:

首先计算出实参表达式的值,接着给对应的形参变量分配一个存储空间,该空间的大小等于该形参类型的长度,然后把已求出的实参表达式的值存入到为形参变量分配的存储空间中,成为形参变量的初值.这种传递是把实参表达式的值传送给对应的形参变量,称这种传递方式为!按值传递?.

当形参为引用参数时,对应的实参通常是一个变量,实虚结合的过程为:把实参变量的地址传送给引用形参,成为引用形参的地址,也就是说使得引用形参是实参变量的一个引用(别名),引用形参所占用的存储空间就是实参变量所占用的存储空间.因此,在函数体中对引用形参的操作实际上就是对被引用的实参变量的操作.这种向引用参数传递信息的方式称为!引用传送?或!按址传送?.

1 简单变量作参数

简单变量包括整型、浮点型、字符型等,在调用一个函数时,将实参的值传给形参的方式来传递信息,形式参数和实在参数有各自不同的内存

空间,形式参数从实在参数那儿得到值,这种传递

方式称为!按值传递?,在这种传递方式下,在函数体中对形参的修改不会影响实在参数的值,因为它们使用各自的存储空间.

下面的例子是简单变量作参数,调用s w ap函数同时把x的值传送给形参a,把y的值传送给形参b,在函数体中对形参a和b的操作是与对应的实参x与y无关的,因为它们使用各自的存储空间.

#inc l u de

vo id s w ap(i n t a,int b)

{int te m p;te m p=a;a=b;b=te m p;}

vo id m a i n()

{int x=45;i n t y=12;

cout<

s w ap(x,y);

cout<

程序执行结果

调用函数前:x=45y=12

调用函数后:x=45y=12

2指针作参数

指针本身是一个变量,它的特殊之处在于它的值是一个地址,因而可以通过指针来间接访问另外一个内存地址,这个地址一般是另一个变量存放变量值的地方.当函数的形式参数是指针时,它的实在参数的值必须是一个地址.下面的例子是通过指针来交换两个变量的值.由于指针作形参,因此对应的形参和实参共用同一个内存单元,形参值的改变将影响实参值.

#inc l u de

vo id s w ap(i n t*a,int*b)

{int te m p;te m p=*a;*a=*b;*b=te m p;} vo id m a i n()

{int x=45;i n t y=12;

cout<

s w ap(&x,&y);

cout<

程序执行结果

调用函数前:x=45y=12

调用函数后:x=12y=453引用作参数

下面的例子是通过引用来交换两个变量的值.由于引用作形参,因此对应的形参和实参就是同一个内存单元,形参值的改变也将影响实参值. #inc l u de

vo id s w ap(i n t&a,int&b)

{int te m p;te m p=a;a=b;b=te m p;}

vo id m a i n()

{int x=45;i n t y=12;

cout<

s w ap(x,y);

cout<

程序执行结果

调用函数前:x=45y=12

调用函数后:x=12y=45

引用传送的好处是不需要为形参分配新的存储空间,从而节省存储,另外能够使对形参的操作反映到实参上,函数被调用结束返回后,能够从实参中得到函数对它的处理结果.有时,既为了使形参共享实参的存储空间,又不希望通过形参改变实参的值,则应当把该形参说明为常量引用,如: vo id f13(const in&t A,const Node*&B,char C);

在该函数执行时,只能读取引用形参A和B 的值,不能够修改它们的值.因为它们是对应实参的别名,所以,也可以说,只允许该函数使用A和B对应实参的值,不允许进行修改,从而杜绝了对实参进行的有意或无意的破坏.

4数组作参数

#inc l u de

const i n t N=8;

i n t sum(int a[],i n t n);

vo id m a i n()

{int b[N]={1,7,2,6,4,5,3,-2};

i n t m1=su m(b,8);i n t m2=sum(&b[2],

5);i n t m3=sum(b+3,3);

cout<

i n t sum(int a[],i n t n)

{int i,f=1;

64哈尔滨师范大学自然科学学报2007年

for(i =0;i

=a [i];//或写成f *

=

*

a ++;return f ;}该函数包含一个主函数和一个su m 函数,sum 函数的功能是求出一维整型数组a [n ]中所

有元素之积并返回.在主函数中第一次调用sum 函数时,把数组b 的首地址传送给a ,把数组b 的长度8传送给n ,执行函数体对数组a 的操作实际上就是对主函数中数组b 的操作,因为它们同时指向数组b 的存储空间;第二次调用sum 函数是把数组b 中b [2]元素的地址传送给a ,把整数5传送给n ,执行函数体对数组a [n ]的操作实际上是对数组b 中b [2]至b [6]之间元素的操作;第三次调用su m 函数是把数组b 中b [3]元素的地址传送给a,把整数3传送给n,执行函数体对数组a [n ]的操作实际上是对数组b 中b [3]至b [5]之间元素的操作.该程序的运行结果为:-10080 720 120

在函数定义的形参表中说明一个数组参数时,通常还需要说明一个整型参数,用它来接收由实参传送来的数组的长度,这样才能够使函数知道待处理元素的个数.

5 字符串作参数

#inc l u de

char *sss(char *sp ,char *

dp);vo id m a i n ()

{char a [15]="abcadecaxybc w ";char b [15];char *

c 1=sss(a,b );cout<

char *

sss(char

*

sp ,char *

dp)

{if(*

sp='\0)'{*

dp='\0;'re t u rn dp ;}int i =0,j ;for(char *

p=sp ;

*

p ;p++){//扫描sp 所指

字符串中的每个字符位置

for(j =0;j

if(*

p=dp[j ])break ;//当*

p 与dp[0]至dp[i -1]之间的任一元素相同则比较过程结束if(j >=i )dp[i ++]=*

p ;//若dp 数组的前i

个元素均不等于*p ,则把*

p 写入dp[i ]元素中}dp[i ]='\0;'//写入字符串结束符

return dp ;}

sss 函数的功能是把sp 所指向的字符串,去掉重复字符后拷贝到dp 所指向的字符数组中,并返回dp 指针.在主函数中第一次调用sss 函数时,分别以a 和b 作为实参,第二次调用时分别以a +4(即a[4]的地址)和b 作为实参.该程序运行后的输出结果为:

abcdexy w abcadecaxybc w abcdexy w

decaxybw abcadecaxybc w decaxybw

进行函数调用除了要把实参传递给形参外,系统还将自动把函数调用表达式执行后的位置(称为返回地址)传递给被调用的函数,使之保存起来,当函数执行结束后,将按照所保存的返回地址返回到原来位置,继续向下执行.

参 考 文 献

1 王萍.C ++面向对象程序设计[M ].北京:清华大学出版社,

2002.

2 徐孝凯.中央电大在线网上资源[J].北京:C++语言程序设计辅导,2004.

3 郑莉,董渊,等.C ++语言程序设计[M ].北京:清华大学出版社,1999.

PROBI NG I NTO THE QUESTI ON OF PARA M ETER TRANS M ISSI ON I N THE C ++LANGUAGE PROGRA MM I NG

L i u Zh i h ua

(H arb i n Rad i o and Televisi on Un ivers i ty)

ABSTRACT

In the C ++language prog ra mm i n g the function transfers the para m eter trans m issi o n is the count for m uch conten,t the d ifferent type para m eter trans m issi o n w ay is not sa m e ,in the deve l o pm ent pro ject co mp ila ti o n app lication pr ocedure ,chooses the appr opriate para m eter to carry on the trans m issi o n ,w ill cause your procedure succinctl y to be clear ,the operati n g effic i e ncy greatly w ill enhance .

K eyw ords :C ++language progra mm ing ;Function transfer ;Para m eter trans m ission

(责任编辑:王丹红)

65

第4期 深入探讨C ++语言中参数传递问题

参数传递方式

引用在函数参数传递中的作用 传递参数有三种方法:1,传递对象本身。2,传递指向对象的指针。3,传递对象的引用。 (1)传值方式 ①传给被调用函数的是整型、长整型、浮点型或双精度型变量。被调用的函数得定义相应的变量为形参。 ②传给被调用函数的是结构变量。被调用函数得定义结构变量为形参。 ③传给被调用函数的是结构变量的成员。被调用函数得定义与该成员同类的变量为形参。 #include "stdio.h" ?#include ?main( ) ?{ ?void swap(int pt1,int pt2); ?int a,b; ?scanf("%d, %d", &a,&b); ?swap(a,b); ?printf("\n%d,%d\n",a,b); ?} ?void swap(int pt1,int pt2) ?{int p; p=pt1; pt1=pt2; pt2=p; } ?

#include "stdio.h" void swapint(); int a,b; void main() { a = 5, b = 10; swapint(); printf("%d\n%d\n",a,b); } void swapint() { int temp; temp=a; a=b; b=temp; } (2)传址方式 ①传给被调用函数的是变量的地址。被调用函数得定义指针变量为形参。 ②传给被调用函数的是数组的地址即数组名。被调用的函数得定义数组或指针变量为形参。 ③传给被调用函数的是函数的地址即函数名称。被调用函数得定义指向函

数的指针变量为形参。④传给被调用函数的是结构的地址。被调用函数得定义结构指针为形参。 #include "stdio.h" ?#include ?main( ) ?{ ?void swap(int *pt1,int *pt2); ?int a,b,*p1,*p2; ?scanf("%d, %d", &a,&b); ?p1=&a;p2=&b; ?swap(p1,p2); ?printf("\n%d,%d\n",a,b); ?} ?void swap(int *pt1,int *pt2) ?{int p; p=*pt1; *pt1=*pt2; *pt2=p; } #include "stdio.h" void swapint(int *a,int *b); void main() { int a = 5, b = 10;

计算机二级C语言上机考试操作步骤及流程和注意事项

二级C语言上机考试操作步骤 一、进入考试系统 单击桌面上的“考试系统”图标,进入考试启动界面。“考试启动界面”如图1所示。 图1 二、输入考生信息 进入考试界面后,单击“开始登录”按钮即可进入考试环境进行“考生信息”的输入。“信息输入”如图2所示。 图2 考生输入号如“2427180018001800”后,单击“考号验证”按钮进行信息核实,系统会弹出如图3所示的对话框。 图3

在确定考生信息完全正确时,单击“是”按钮,进入答题界面,如下图所示,上方含有“程序填空题”、“程序修改题”、“程序设计题”三个按钮, 三、开始答题 本次考试共有50套题,每一套题的填空、改错和编程题的答案均附在后面。学生进入上述考试界面后,首先点击程序设计题,根据程序设计题从而明白是哪一套题的组合,然后开始正式答题。过程如下: 1.程序填空题 单击考试界面中的“程序填空题”按钮后,题目显示区将显示出题目对应的文字叙述信息。通过文字叙述可以了解到该题目的考试容。然后单击左上角“答题”菜单中的“启动Microsoft Visual C++”菜单项进入“Visual C++ 6.0”系统环境界面,如下图所示。 进入系统环境后,执行左上角的“文件|打开”命令,系统弱出“打开”对话框,选择“blank1.c”

程序文件,点击左下角的“打开”按钮,如图4所示。 图4 打开“blank1.c”程序文件后,开始填空。填空方法如下: (1)在程序中找到“******** found *******”标识位置。 (2)把“found ”标识位置下面的需要填空的“占位符”删除(需要连横线一起删除),将程序的答案 写在对应位置。例如下图所示。 原“blank1.c”程序图: 打开

C语言与汇编语言互相调用

浅谈C程序中调用汇编模块的方法 C语言是目前非常流行的一种编程语言,除具有高级语言使用方便灵活、数据处理能力强、编程简单等优点外,还可实现汇编语言的大部分功能,如可直接对硬件进行操作、生成的目标代码质量较高且执行的速度较快等。所以在工程上对硬件处理速度要求不很高的情况下,基本可以用C代替汇编语言,编写接口电路的控制软件。但C也不能完全取代汇编语言,如在一些对速度要求很高的实时控制系统中,以及对硬件的特殊控制方面,C有时也不能完全很好胜任,还需要汇编语言来编写。因为汇编语言目标代码更精练,对硬件直接控制能力更强和执行速度更快,但汇编语言编程烦难、表达能力差也显而易见。比较好的解决办法是C与汇编语言混合编程,即用C编写软件的调度程序、用户界面以及速度要求不高的控制部分,而用汇编语言对速度敏感部分提供最高速度的处理模块,供C调用。这种方法提供了最佳的软件设计方案,做到了兼顾速度效率高和灵活方便。由于本人的毕业设计需要C 程序中调用汇编模块的方法来提高ARM定点指令的执行速度,故对这方面进行了学习。学习心得如下: 对于C和汇编语言的接口主要有两个问题需要解决。 一、调用者与被调用者的参数传递 这种数据传递通过堆栈完成,在执行调用时从调用程序参数表中的最后一个参数开始,自动依次压入堆栈;将所有参数压入堆栈后,再自动将被调用程序执行结束后的返回地址(断点)压入堆栈,以使被调程序结束后能返回主调程序的正确位置而继续执行。例如一调用名为add汇编程序模块的主函数:main( ){...... add(dest,op1,op2,flages);......}。在此例中对主函数进行反汇编,主函数在调用add函数前自动组织的堆栈。 . . . lea 0xfffffffe8(%ebp),%eax #flages数组的首地址入栈 push %eax pushl 0xfffffff8(%ebp) #OP2入栈 pushl 0xfffffffc(%ebp) #OP1 入栈 pushl 0xfffffff0(%ebp) #dest地址入栈 call 0x80483f0 #调用add函数 . . 执行完add调用语句后,栈内数据结果如图一所示。 进入汇编子程序后,为了能正确获取主调程序并存入堆栈中的数据,被调的汇编子程序先后要做如下一些工作: 1、保存esp的副本 进入汇编子程序后,子程序中免不了要有压栈和出栈的操作,故ESP时刻在变化。为了能用ESP访问堆栈中的参数,安全办法是一进入子程序后,先为ESP制副本,以后对传递参数的访问都用副本进行。一般可用EBP保存ESP,如: push %ebp mov %ebp,%esp

C语言编程规范

编码规范 1. 头文件编码规范 (2) 2. 函数编写规范 (2) 3. 标识符命名与定义 (2) 3.1通用命名规则 (2) 3.2 变量命名规则 (3) 3.3函数命名规则 (3) 3.4 宏的命名规则 (3) 4. 变量 (3) 5. 宏、常量 (4) 6. 质量保证 (4) 7. 程序效率 (5) 8. 注释 (5) 9. 排版与格式 (6) 10. 表达式 (7) 11. 代码编辑、编译 (7) 12. 安全性 (7) 13. 可读性 (7) 14. 可测性 (7) 15. 单元测试 (8) 16. 可移植性 (8)

1. 头文件编码规范 1. 禁止头文件循环依赖。 2. .c/.h文件不要包含用不到的头文件。 3. 禁止在头文件中定义变量。 4. 同一产品统一包含头文件排列方式。(如功能块排序、文件名升序、稳定度排序。) 5. 只能通过包含头文件的方式使用其他.c提供的接口,禁止在.c中通过extern的方式使用外部函数接口、变量。 2. 函数编写规范 1. 一个函数仅完成一件功能。 2. 重复代码应该尽可能提炼成函数。 3.为简单功能编写函数 4.函数的返回值要清楚、明了,让使用者不容易忽视错误情况。 5. 避免函数过长,新增函数不超过100行(非空非注释行)。 6. 避免函数的代码块嵌套过深,新增函数的代码块嵌套不超过4层。 7. 可重入函数应避免使用全局变量和禁止使用static变量。 8. 设计高扇入,合理扇出(小于7)的函数。 9. 废弃代码(没有被调用的函数和变量)要及时注释(有助于更好理解程序)。 10. 对所调用函数的错误返回码要仔细、全面地处理。 11. 函数不变参数使用const。 12. 函数应避免使用全局变量、静态局部变量和I/O操作,不可避免的地方应集中使用。 13. 函数的参数个数不超过5个。 14. 减少或禁止函数本身或函数间的递归调用 3. 标识符命名与定义 3.1通用命名规则 1. 标识符的命名要清晰、明了,有明确含义,同时使用完整的单词或大家基本可以理解的缩写,避免使人产生误解。 2. 除了常见的通用缩写以外,不使用单词缩写,不得使用汉语拼音。 示例: argument 可缩写为arg buffer 可缩写为buff clock 可缩写为clk command 可缩写为cmd compare 可缩写为cmp configuration 可缩写为cfg device 可缩写为dev error 可缩写为err hexadecimal 可缩写为hex increment 可缩写为inc initialize 可缩写为init maximum 可缩写为max message 可缩写为msg minimum 可缩写为min parameter 可缩写为para

C#中方法的参数有四种类型

C#中方法的参数有四种类型 1. 值参数(不加任何修饰符,是默认的类型) 2. 引用型参数(以ref 修饰符声明) 3. 输出参数(以out 修饰符声明) 4. 数组型参数(以params 修饰符声明) 1. 值传递: 值类型是方法默认的参数类型,采用的是值拷贝的方式。也就是说,如果使用的是值类型,则可以在方法中更改该值,但当控制传递回调用过程时,不会保留更改的值。 使用值类型的例子如:(下面的Swap()未能实现交换的功能,因为控制传递回调用方时不保留更改的值) using System; class Test { static void Swap(int x, int y) { int temp = x; x = y; y = temp; } static void Main() { int i = 1, j = 2; Swap(i, j); Console.WriteLine("i = {0}, j = {1}", i, j); } } /* * 输出结果为: i=1, j=2 * 未能实现Swap()计划的功能 */ 2. 引用传递(ref类型) ref关键字使参数按引用传递。其效果是,当控制权传递回调用方法时,在方法中对参数所做的任何更改都将反映在该变量中。 2.1. 若要使用ref 参数,则方法定义和调用方法都必须显式使用ref关键字。 2.2. 传递到ref 参数的参数必须最先初始化。这与out 不同,out 的参数在传递之前不需要显式初始化。 2.3. 如果一个方法采用ref 或out 参数,而另一个方法不采用这两类参数,则可以进行重载。

相关实例如下: using System; class Test { static void Swap(ref int x, ref int y) { int temp = x; x = y; y = temp; } static void Main() { int i = 1, j = 2; Swap(ref i, ref j); Console.WriteLine("i = {0}, j = {1}", i, j); } } /* * 引用类型实现了Swap()计划的功能: * 输出为: * i = 2, j =1 */ 3. 输出类型(out类型) out 关键字会导致参数通过引用来传递。这与ref 关键字类似。 与ref 的不同之处: 3.1. ref 要求变量必须在传递之前进行初始化,out 参数传递的变量不需要在传递之前进行初始化。 3.2. 尽管作为out 参数传递的变量不需要在传递之前进行初始化,但需要在调用方法初始化以便在方法返回之前赋值。 示例如下: using System; class Test { static void Swap(out int x, out int y) { //在这里进行了i和j的初始化

C语言中要注意的一些问题

C语言中要注意的一些问题 下面列出了一些C语言中常见的问题和注意事项,跟考试无关,有些表达不是很严格,仅供辅助学习。 1、C语言中规定,每条语句结束时末尾加分号,而不是每行末尾加分号! 2、在用scanf和printf函数输入和输出时,一定要记得int跟%d对应,long跟%ld对应,float 和double跟%f对应,否则不能正确输入和输出数据。 3、使用scanf函数时,在双引号中最好只出现各种格式控制符,如”%d%d%f%c”,而不要 随意增加其它字符,以免画蛇添足,增加输入的麻烦。 4、通过scanf(“%s”,字符数组名)和gets(字符数组名)都可以输入一个字符串,并将 其存储在指定的字符数组中。区别在于,前者当输入字符中遇到空格就认为输入结束,后者则可以接收包含空格的字符串。 5、在数值运算时,要注意防止数据的溢出。特别是int类型数值范围较小,比较容易溢出。 一旦溢出,结果出错。 6、要注意各种运算符的优先级,需要时可以加()来保证运算顺序满足要求。 7、参加关系和逻辑运算的数据可以是任意类型和任意值,计算机将所有非零数理解为1 (真),而0则为假,然后进行运算。得到的结果只有两种,要么是1,要么是0。 8、整数的比较可以使用关系运算;浮点型数据在比较是否相等时,一般这样实现 fabs(表达式或变量)<=1e-6 而字符串比较必须通过strcmp函数实现。 9、strcpy(字符数组1,字符数组2或字符串常量)的功能相当于 字符数组1=字符数组2或字符串常量(程序中绝对不能写成这样!) 10、变量和数组在定义时若未赋初值(未初始化),则其内容是随机数(不确定)。数组 若是部分初始化,则未指定初值的数组元素自动赋值为0(也可以理解为字符’\0’) 11、for循环非常常用,要注意后面括号中三个表达式的执行顺序。若非必要,最好不 要省略三个表达式。若循环体多于一条语句,必须用{}将它们括起来,变成语句块。(对于while和do..while循环也是这样) 12、数组元素是一个挨一个放在内存一片连续的空间中。对于多维数组,则采用行主序 组织存放。比如二维数组arr[M][N],看作有arr[0], arr[1], …, arr[M-1]个一维数组,每个一维数组有N个元素 arr[0][0], arr[0][1], …, arr[0][N-1] ←第0行 arr[1][0], arr[1][1], …, arr[1][N-1] ←第1行 ...

汇编语言的过程调用与c语言的函数调用

姓名:孙贵森 学号: 汇编语言地过程调用,如果需要传递参数,一般有种方法,通过寄存器来“传递”,或是通过参数来传递.(还有将所有参数制成参数列表并压栈地传递方法,但较少用.)通过寄存器来“传递”,不是真正意义上地传递,其只不过是事先在几个有限地寄存器中设置相应地值后,再调用过程,过程再直接读取这些寄存器地内容.可想而知,此法犹如语言中地全局变量,极易感染.而如果通过参数来传递,又不得不面临手工维护堆栈框架( )地重担.堆栈框架动态地存放着参数、调用过程地返回地址、过程局部变量、过程内地压栈等内容,也是不好对付地.一般情况下,一个普通地过程可能如下编写:文档来自于网络搜索 , ..... 作为遵从调用约定()调用者,则需这样调用上述过程: ; ; ; , * ; 而如果遵从调用约定,则: , ...... , [ ] ; , [ ]; ...... * ; , , ; ...... , [ ]; , [ ]; , [ ; , [ ]; ...... , ; * ;

在被调用地过程内,分为种情况: . 无参数,也无局部变量 . 有参数 . 有局部变量 当无参数且无局部变量时,堆栈中只是保存语句地下一条语句地地址,可以很安全地返回.而当有参数,使用伪指令地接收参数地形式,则会自动生成正确地返回代码.而当有局部变量,使用伪指令来定义局部变量,也会自动地生成正确地返回代码.在将参数压栈时,仍需将其打包为位地,文档来自于网络搜索 ; , ; ; 另一选择是,将用作地变量声明为. ; ; 还有另一种方法,即,总是传递指针. ; (, ) , ; , , , , , [] , , [] 这种方法在保留了我们可以声明仅需地变量类型地同时,也确保位地方法正确压栈.语言中地每一个函数都是一个独立地代码块.一个函数地代码块是隐藏于函数内部地,不能被任何其它函数中地任何语句(除调用它地语句之外)所访问(例如,用语句跳转到另一个函数内部是不可能地).构成一个函数体地代码对程序地其它部分来说是隐蔽地,它既不能影响程序其它部分,也不受其它部分地影响.换言之,由于两个函数有不同地作用域,定义在

变量的作用域和生存期

变量的作用域局部变量和全局变量 在函数和复合语句内定义的变量,只在本函数或复合语句范围内有效(从定义点开始到函数或复合语句结束),他们称为内部变量或局部变量。 在函数之外定义的变量是外部变量,也称为全局变量(或全程变量)。 如果在一个函数中全局变量和局部变量同名,则在局部变量的作用范围内,外部变量被“屏蔽”,即他不起作用,此时局部变量是有效的。 全局变量的作用是增加函数间数据联系的渠道。 虽然全局变量有以上优点,但建议不必要时不要使用全局变量,因为全局变量在程序的全部执行过程中都占用存储单元,而不是仅在需要时才开辟单元。 在程序设计时,对模块的划分要求:内聚性强,与其他模块的耦合性弱,这样便于程序的移植,可读性强。 变量的生存期 变量的存储方式分为两种:静态存储方式和动态存储方式。 静态存储方式是指在程序与性能期间由系统在静态存储区分配存储空间的方式,在程序运行器件不释放;而动态存储方式则是在函数调用期间根据需要在动态存储区分配存储空间的方式。这就是变量的存储区别。 Auto----声明自动变量 函数中的形参和在函数中定义的变量都属于此类。在调用这些函数时,系统给这些变量分配存储空间,函数调用结束时就自动释放这些存储空间。因为这类局部变量称为自动变量(auto 变量)。关键字auto作为存储类别的声明。 Auto可省略 Static-----声明静态变量 希望函数中的变量的局部变量的值在函数调用结束后不消失而继续保留原值,即其占用的存储单元不释放,在下一次该函数调用时,该变量已有值,就是上一次函数调用结束时的值。这时就用关键字static指定该局部变量为“静态存储变量”。 对静态局部变量的说明 静态局部变量属于静态存储类别,在静态存储区内分配存储单元,在程序整个运行期间都不释放。而自动变量(即动态局部变量)属于动态存储类别,占胴体啊存储区空间而不占静态存储区空间,函数调用结束后即释放。 对静态局部变量是在编译时赋初值的,即只赋初值一次,在以后每次调用函数时不再重新赋初值而只是保留上次函数调用结束时的值。自动变量赋初值是在函数调用时进行的。 对静态局部变量来说,编译时自动赋初值0或空字符。而对自动变量来说,如果不赋值则他的值是一个不确定的值。 Registic--声明寄存器变量 这种变量一般不用,只需了解就可以了。 Extern-----声明外部变量的作用范围 如果一个程序中有两个文件,在两个文件中都要用到同一个外部变量Num,不能分别在两个文件中各自定义一个外部变量Num,否则在进行程序的连接时会出现“重复定义”的错误。正确的做法:在人一个文件中定义外部变量Num,而在另一个文件中用extern对Num作外部变量声明,即extern Num.

C++中函数调用时的三种参数传递方式

在C++中,参数传递的方式是“实虚结合”。 ?按值传递(pass by value) ?地址传递(pass by pointer) ?引用传递(pass by reference) 按值传递的过程为:首先计算出实参表达式的值,接着给对应的形参变量分配一个存储空间,该空间的大小等于该形参类型的,然后把以求出的实参表达式的值一一存入到形参变量分配的存储空间中,成为形参变量的初值,供被调用函数执行时使用。这种传递是把实参表达式的值传送给对应的形参变量,故称这种传递方式为“按值传递”。 使用这种方式,调用函数本省不对实参进行操作,也就是说,即使形参的值在函数中发生了变化,实参的值也完全不会受到影响,仍为调用前的值。 [cpp]view plaincopy 1./* 2. pass By value 3.*/ 4.#include https://www.360docs.net/doc/0115925820.html,ing namespace std; 6.void swap(int,int); 7.int main() 8.{ 9.int a = 3, b = 4; 10. cout << "a = " << a << ", b = " 11. << b << endl; 12. swap(a,b); 13. cout << "a = " << a << ", b = " 14. << b << endl; 15.return 0; 16.} 17.void swap(int x, int y) 18.{ 19.int t = x; 20. x = y; 21. y = t; 22.}

如果在函数定义时将形参说明成指针,对这样的函数进行调用时就需要指定地址值形式的实参。这时的参数传递方式就是地址传递方式。 地址传递与按值传递的不同在于,它把实参的存储地址传送给对应的形参,从而使得形参指针和实参指针指向同一个地址。因此,被调用函数中对形参指针所指向的地址中内容的任何改变都会影响到实参。 [cpp]view plaincopy 1.#include https://www.360docs.net/doc/0115925820.html,ing namespace std; 3.void swap(int*,int*); 4.int main() 5.{ 6.int a = 3, b = 4; 7. cout << "a = " << a << ", b = " 8. << b << endl; 9. swap(&a,&b); 10. cout << "a = " << a << ", b = " 11. << b << endl; 12. system("pause"); 13.return 0; 14.} 15.void swap(int *x,int *y) 16.{ 17.int t = *x; 18. *x = *y; 19. *y = t; 20.} 按值传递方式容易理解,但形参值的改变不能对实参产生影响。 地址传递方式虽然可以使得形参的改变对相应的实参有效,但如果在函数中反复利用指针进行间接访问,会使程序容易产生错误且难以阅读。

C语言的注意事项

C语言的注意事项 摘要: 在编写时需要注意的一些问题。 1、每个程序中一定包含main()函数,尽管C语言中对函数命名没有限制 2、printf函数永远不会自动换行,只能用\n来实现,回车键进行的换行在编译中会出现错误信息 3、在vs2008中编译.测试需要加system("pause");来暂停dos自动退出导致的printf 无法显示 4、所有自定义变量必须声明才能使用 5、每行只书写一条语句,在运算符两边加上一个空格,便于阅读 6、整数除法将自动舍位,不会进行四舍五入的操作 7、for(初始化部分;条件部分;增长部分)-比while更适用于初始化和增长步长都是单条语句情况下 8、使用#define名字替换文本对部分"幻数"赋予意义便于阅读#define结尾不需要;号结束 9、EOF(end of file)-表示没有字符输入时-定义在stdio.h头中EOF不等于\n换行等 10、!=的优先级大于=如果对判断中存在变量赋值时对赋值加以()e.g while((c=getchar())!=EOF) 11、getchar()用于用户输入直至键入回车 12、变量名以字母和数字组成,(下划线"_"被默认为字符,以下划线为首写字母的为库类变量名),变量常以小写字母开头.内部变量命前31位有效,外部名至少前6位保持唯一性.大小写在C语言中是区分的. 13、一个字符常量为一个整数,以''单引号括起.e.g'0'为48,与0没有任何关系 14、ANSI C语言的转义符:\a响铃符;\b回退符;\f换页符;\n换行符;\r回车符;\t横向制表符\v纵向制表符;\\反斜杠;\?问号;\'单引号;\"双引号;\ooo八进制数;\xhh十六进制数;'\0'表示0,即(null).通常以'\0'形式表示0以强调某些表达是的字符属性 15、'x'与"x"的区别:'x'表示一个整数,字母x在极其字符集中对应的数值."x"表示一个包含一个字符x以及一个结束符'\0'

变量的生命周期与作用域

作用域和生存周期是完全不同的两个概念。作用域可以看作是变量的一个有效范围,就像网游中的攻击范围一样;生存周期可以看成是一个变量能存在多久,能在那些时段存在,就像网游中的魔法持续时间……简单的以一个局部变量来举个例子:在main函数中声明了变量a,那么a的作用域就是main函数内部,脱离了main函数,a就无法使用了,main函数之外的函数或者方法,都无法去使用a。那么a的生存周期是指a在那些时候存在,具体到这个例子,a什么时候存在,要取决于main函数,或者说,main函数只要被调用,且调用没有完成,那么a就将存在。除此以外的情况,a都将被释放。生存周期也可以理解为从声明到释放的之间的时间。变量具体可以分为全局变量、静态全局变量、静态局部变量和局部变量。按存储区域分:全局变量、静态全局变量和静态局部变量都存放在内存的全局数据区,局部变量存放在内存的栈区按作用域分:全局变量在整个工程文件内都有效;静态全局变量只在定义它的文件内有效;静态局部变量只在定义它的函数内有效,只是程序仅分配一次内存,函数返回后,该变量不会消失;局部变量在定义它的函数内有效,但是函数返回后失效。全局变量和静态变量如果没有手工初始化,则由编译器初始化为0。局部变量的值不可知。 总的分为局部变量和全局变量:局部变量又可分为动态局部变量(没特殊声明的变量一般都为动态局部变量)和静态局部变量(用static关键字声明的变量如:static int a;);两者的区别在于:静态的局部变量生存期比动态的局部变量来的长,动态的局部变量的生存期为所定义的范围内,如在函数内定义的,函数结束,变量也跟着结束,变量的值不会保存下来。而静态变量的生存期为整个源程序(也可说是一个文件,不同环境不同称呼)。而两者的作用域是一样。只能在定义他的函数内起作用,离开了这个函数就不起作用了。全局变量:在函数之外定义的变量称为全局变量。全局变量可以为本文件中其他函所共用(作用域),它的有效范围(生存期)从定义变量开始到文件结束。如果在同一个源文件中,外部变量与局部变量同名,则在局部变量的作用范围内,外部变量被“屏蔽”,即全局变量不起作用。下面来看一个例子:#include"stdio.h"int d=1; //声明一个全局变量int fun(int p) { static int d=5; //定义一个静态局部变量d初值为5 //第二次调用时没有执行此行d=d+p; //此时局部变量d的值为9,(第一次调用)//第二次调用是局部变量d 的值为13,因为上一次执行完后d的值为9,printf("%d",d); //第一次输出为9,//第二次输出13}void main(){ int a=3; d=d+a; //此时d的值为4;a变量的值为3,全局变量d的值为1。for(i=0;i<2;i++) fun(d); //此处的d值为4,传送给形参p,再一次调用时还是将4传给开参p printf("d=%d",d); //输出d的值为4.此处的d为全局变量。} 看以上内容时,你先把程序看一篇,然后把会值代进去远算,每一次看注释时在同一行中只要看到第二个”//“时结束.第2个“//”为第二次调用时看的。以上内容有一点乱,但是希望可以帮助到你...88有什么不明白可以再问!答案补充 看程序时注释行不要选先看。本程序一共调用fun函数两次,两次实参的值都为4.

函数调用参数传递类型(java)的用法介绍.

函数调用参数传递类型(java)的用法介绍. java方法中传值和传引用的问题是个基本问题,但是也有很多人一时弄不清。 (一)基本数据类型:传值,方法不会改变实参的值。 public class TestFun { public static void testInt(int i){ i=5; } public static void main(String[] args) { int a=0 ; TestFun.testInt(a); System.out.println("a="+a); } } 程序执行结果:a=0 。 (二)对象类型参数:传引用,方法体内改变形参引用,不会改变实参的引用,但有可能改变实参对象的属性值。 举两个例子: (1)方法体内改变形参引用,但不会改变实参引用,实参值不变。 public class TestFun2 { public static void testStr(String str){ str="hello";//型参指向字符串“hello” } public static void main(String[] args) { String s="1" ;

TestFun2.testStr(s); System.out.println("s="+s); //实参s引用没变,值也不变 } } 执行结果打印:s=1 (2)方法体内,通过引用改变了实际参数对象的内容,注意是“内容”,引用还是不变的。 import java.util.HashMap; import java.util.Map; public class TestFun3 { public static void testMap(Map map){ map.put("key2","value2");//通过引用,改变了实参的内容 } public static void main(String[] args) { Map map = new HashMap(); map.put("key1", "value1"); new TestFun3().testMap(map); System.out.println("map size:"+map.size()); //map内容变化了 } } 执行结果,打印:map size:2 。可见在方法testMap()内改变了实参的内容。 (3)第二个例子是拿map举例的,还有经常涉及的是 StringBuffer : public class TestFun4 {

书写C语言程序的注意事项

书写C语言程序的注意事项 1、每一C程序必须有一个main( ),且也只能有一个main( ),程序始终是从main的第一个’{‘开 始执行的;如果遇到调用函数,流程就转向对应的被调用函数,执行被调用函数中的相应语句,直到遇到return语句或‘}’时,流程又返回到主调函数中的调用处,继续执行其后的后续语句,直到遇到主函数main( )后的最后一个‘}’为止,此时结束整个程序; 2、分号是C语句的结束符,而不是分隔符,除以下情形不用分号外,其余情形都必须用分号: 1)绝对不能用分号 a)命令行后不用,即程序最开头的#include “”和#define …… b)main( )后不用; c)子函数的函数首项后不用; d)‘{’后不用; e)‘}’后不用;但是在结构体、共用体、枚举类型后的‘}’后必须用;

f)switch( )后不用; g)case常量后不用; h)default后不用; 2)可用可不用分号的情形,用了虽然没有语法错误,但是一般达不到程序设计的目的。 i)if( )后 j)else后 k)while( )后 l)for( )后 3、C语言允许有注释语句,一旦被注释起来的语句,程序是不会执行的,其注释格式为: a)/*注释内容*/:可以放于一行进行注释,也可以C语句的行末进行注释,还可以将几行一起进行注释; b)//注释内容:只能注释一行 4、C语句中的格式符是以%开头,其后紧跟一个格式说明符,如: int:%d

char:%c float:%f long:%ld unsigned:%u; double:%f,%e,%lf,%le; 八进制数:%o 十六进制数:%x; 如果要在屏幕上输出一个%,则必须用两个%来输出; 5、C程序中用到的所有变量,都必须“先定义,后使用”,且“先有值,后运算”; 6、scanf( )和printf( )中的双引号中的格式符必须从左到右与其后的输入项、输出项类型匹配、个 数相等。 7、scanf( )中的输入项前应该跟地址符&,而格式符%s对应的输入项除外; 如:int a,b;

汇编语言编程规范

软件设计更多地是一种工程,而不是一种个人艺术。如果不统一编程规范,最终写出的程序,其可读性将较差,这不仅给代码的理解带来障碍,增加维护阶段的工作量,同时不规范的代码隐含错误的可能性也比较大。 分析表明,编码阶段产生的错误当中,语法错误大概占20%左右,而由于未严格检查软件逻辑导致的错误、函数(模块)之间接口错误及由于代码可理解度低导致优化维护阶段对代码的错误修改引起的错误则占了一半以上。 可见,提高软件质量必须降低编码阶段的错误率。如何有效降低编码阶段的错误呢?这需要制定详细的软件编程规范,并培训每一位程序员,最终的结果可以把编码阶段的错误降至10%左右,同时也降低了程序的测试费用,效果相当显著。 本文从代码的可维护性(可读性、可理解性、可修改性)、代码逻辑与效率、函数(模块)接口、可测试性四个方面阐述了软件编程规范,规范分成规则和建议两种,其中规则部分为强制执行项目,而建议部分则不作强制,可根据习惯取舍。 1.排版 规则1 程序块使用缩进方式,函数和标号使用空格缩进,程序段混合使用TAB和空格缩进。缩进的目的是使程序结构清晰,便于阅读和理解。 默认宽度应为8个空格,由于Word中为4个空格,为示范清晰,此处用2个代替(下同)。 例如: MOV R1, #00H MOV R2, #00H MOV PMR, #PMRNORMAL MOV DPS, #FLAGDPTR MOV DPTR, #ADDREEPROM read1kloop: read1kpage: INC R1

MOVX A, @DPTR MOV SBUF, A JNB TI, $ CLR TI INC DPTR CJNE R1, #20H, read1kpage INC R2 MOV R1, #00H CPL WDI CJNE R2, #20H, read1kloop ;END OF EEPROM 规则2 在指令的操作数之间的,使用空格进行间隔,采用这种松散方式编写代码的目的是使代码更加清晰。 例如: CJNE R2, #20H, read1kloop ;END OF EEPROM 规则3 一行最多写一条语句。 规则4 变量定义时,保持对齐。便于阅读和检查内存的使用情况。 例如: RegLEDLOSS EQU 30H ; VARIABLE ; TESTLED==RegLEDLOSS.0 RegLEDRA EQU 31H ; VARIABLE

计算机二级考试C语言答题注意事项

计算机二级考试C语言答题注意事项 基本原则是计划好做题的时间,不要急燥,保持稳定的答题速度,应将熟悉的、会做的、容易的先做。 选择题和操作题在进行的过程中,都一定要看清题目,审好题,弄清题目要考核的知识点,能够迅速回忆相关的知识,综合运用平时上课老师所教授的做题的方法和技巧,进行答题。 10分 例如:以下C语言用户标识符中,不合法的是 A)_1B)AaBc C)a_b D)a—b 这个题目是考核的是标识符的命名规则的问题,我们知道C中合法的标识符必须以字母、下划线开头,由字母、数字、下划线组成,据此应该选择D。 再如:有以下程序 #include main() {int a=1,b=0; if(!a) b++; else if(a==0)if(a)b+=2; else b+=3; printf(”%d\n”,b); } 程序运行后的输出结果是 A)0B)1C)2D)3 这个题目考核选择结构的控制流程。题目采用的是else~if结构,实际上是if的嵌套。不管怎样,始终牢记这种嵌套的if语句的执行流程:从上向下逐一对if后的表达式进行检测。当某一个表达式的值为非0时,就执行与此有关子句中的语句,其余部分不执行,直接越过去。如果所有表达式的值都为0,则执行最后的else子句。 当执行以上程序时,首先定义a、b并赋初值1、0,然后进入if语句。当从上向下逐一检测时,!a 的值为0,则越过b++,进入else 后的if语句,检测到a= =0的值为0,则越过

if(a)b+=2;语句,因为前面所有if子句中的表达式的值都为0,因此执行最后else子句中的语句b+=3,求出b为3,然后退出if结构,接着输出b的值,所以选择D。 (3)选择题(21)-(40),这20个都有一定难度,涉及C后面的章节,如函数、数组、指针、字符串、结构体、预处理等内容,有部分都会给定出一段代码,根据代码做选择,因此,需要读懂C的简单代码然后做了选择。 例如:有以下程序(函数fun只对下标为偶数的元素进行操作) # include<stdio.h> void fun(int *a;int n) {int i,j,k,t; for (i=0;i<n-1;i+=2) {k=i; for(j=i;ja[k])k=j; t=a[i];a[i]=a[k];a[k]=t; }} main() {int aa「10」={1,2,3,4,5,6,7},i; fun(aa,7); for(i=0;i<7;i++)printf(”%d,”,aa[i])); printf(”\n”); } 程序运行后的输出结果是 A)7,2,5,4,3,6,1B)1,6,3,4,5,2,7 C)7,6,5,4,3,2,1D)1,7,3,5,6;2,1 通过对代码的阅读,采用模拟运行的方法,可知此题的算法是将数组元素中偶数下标的元素进行从大到小的排序,它使用的是选择排序法。因为数组的初始值为a下标:0 1 2 3 4 5 6 所以很快就可以选择出答案是A。 (4)填空题仔细读题、审题,理解考题考核知识点,回忆相关知识。牵涉到程序填空的题目,也需读得懂C程序,还需判断出题意图,弄清程序功能,找到编程的思路,才能填好空要。难度较高。 如:以下程序的功能是:借助指针变量找出数组元素中最大值所在的位置并输出该最大值。请在输出语句中填写代表最大值的输出项。 #include <stdio.h> main() {int a〔10],*p,*s; for(p=a;p-a<10;p++)scanf(”%d”,p); for(p=a,s=a;p-a<10;p++)if(*p>*s)s=p; printf("max=%d\n”,【14】); } 这个题目是利用指针对数组元素求最大值的算法。此题采用的算法是使s指针总是指向

arm汇编语言调用C函数之参数传递

arm汇编语言调用C函数之参数传递 于ARM体系来说,不同语言撰写的函数之间相互调用(mix calls)遵循的是 ATPCS(ARM-Thumb Procedure Call Standard),ATPCS 主要是定义了函数呼叫时参数的传递规则以及如何从函数返回,关于ATPCS的详细内容可以查看ADS1.2 Online Books ——Developer Guide的2.1节。这篇文档要讲的是汇编代码中对C函数调用时如何进行参数的传递以及如何从C函数正确返回。 不同于x86的参数传递规则,ATPCS建议函数的形参不超过4个,如果形参个数少于或等于4,则形参由R0,R1,R2,R3四个寄存器进行传递;若形参个数大于4,大于4的部分必须通过堆栈进行传递。 我们先讨论一下形参个数为4的情况. 实例1: test_asm_args.asm //-------------------------------------------------------------------------------- IMPORT test_c_args ;声明test_c_args函数 AREA TEST_ASM, CODE, READONLY EXPORT test_asm_args test_asm_args STR lr, [sp, #-4]! ;保存当前lr ldr r0,=0x10 ;参数 1 ldr r1,=0x20 ;参数 2

ldr r2,=0x30 ;参数 3 ldr r3,=0x40 ;参数 4 bl test_c_args ;调用C函数 LDR pc, [sp], #4 ;将lr装进pc(返回main函数) END test_c_args.c //-------------------------------------------------------------------------------- void test_c_args(int a,int b,int c,int d) { printk("test_c_args:\n"); printk("%0x %0x %0x %0x\n",a,b,c,d); } main.c //-------------------------------------------------------------------------------- int main() { test_asm_args(); for(;;); } 程序从main函数开始执行,main调用了test_asm_args,test_asm_args 调用了test_c_args,最后从test_asm_args返回main。代码分别使用了

c语言注意事项

*变量赋值时候不能连写成:int a=b=c=5或者int a,b,c=5;都存在语法错误!但是可以写成int a=5,b=5,c=5; 或者写成:int a,b,c; a=b=c=5;也是合法的。 *%d表示输出的结果为整型数,全部为整数的运算; %f表示输出结果为实数型数,过程中必须有实数型数参与运算。 *c语言中的各种类型的语句: 条件判断语句:if语句、switch语句 循环执行语句:do while语句、while语句、for语句 转向语句:break语句、goto语句、continue语句、return语句 复合语句; *所谓数据的输入、输出都是以计算机为主题而言的,并且数据的输入和输出都是由内部库函数完成的,因此都是函数语句。 *stdio.h做头文件,表示的是包含标准的输入输出函数。 *表达式语句:【表达式;】计算表达式的值 *函数调用语句:【函数名(实际参数);】调用函数体并把实际参数赋给函数定义中的形式参数,然后执行被调用函数中的语句,求取函数值。 字符数据的输入输出: 1.putchar字符输出函数,在显示器上输出显示单个字符。 形式:putchar(字符变量); 使用时必须要包含stdio.h头文件。 2.getchar键盘输入函数,从键盘输入一个字符,赋给字符变量。 形式:getchar(); 例如:char c; 定义字符c c=getchar(); 通过键盘输入数据,给c赋值 putchar(c); 输出字符c的值 注意:输入的数据只能按一位数据处理,多余一位的数子,只接受输入的第一位字符。使用时必须要包含stdio.h头文件。 3.printf为格式输出函数,末字母f为format 格式之意,按用户指定的格式输出 显示数据。使用时可以不用声明stdio.h的头文件。 一般形式:printf(“格式控制字符串”,要输出的列表名): %d:表示输出的是十进制整型输出,带符号的 %ld:表示十进制长整型输出,带符号的 %u:表示输出的是十进制整型输出,无符号的 %o:表示八进制整型输出,无符号 %x,%X或者%0x,%0X:表示输出十六进制数,无符号 %f:表示输出小数点形式的数据 %s:表示输出字符串

相关文档
最新文档