搜档网
当前位置:搜档网 › 关于返回结构体的函数=

关于返回结构体的函数=

关于返回结构体的函数=
关于返回结构体的函数=

关于返回结构体的函数

【前言】写作本文,源于最近回复的《汇编中函数返回结构体的方法》一文。在网络上也已经有一些相关文章和相关问题,有的文章已经给出了一部分结果,但总体而言还缺少比较重要的结论。本文以分析VC6 编译器,32 位架构为主来重复性分析这个话题。

(一)不超过8 bytes 的小结构体可以通过EDX:EAX 返回。

本文的范例代码取材于《汇编中函数返回结构体的方法》一文,并在此基础上进行修改和试验。要研究的第一份代码如下,定义一个不超过8 bytes 的小结构体,不超过8 bytes 是因为这个结构体能够用EDX:EAX 容纳,我们之后将看到在release 编译时,编译器能够向返回普通基础类型那样进行返回。

#include

//不超过8 bytes 的“小结构体”

struct A

{

int a;

int b;

};

//返回结构体的函数

struct A add(int x, int y)

{

struct A t;

t.a = x * y;

return t;

}

int main()

{

struct A t = add(3, 4);

printf("t.a = %ld\n", t.a);

return0;

}

首先,我们需要解决一个常见困惑,就是要明确这段代码和下面的典型错误代码的区别:

char* get_buffer()

{

char buf[8];

return buf;

}

上面的get_buffer 返回的是栈上的临时变量空间,在函数返回后,其所在的空间也就被“回收/释放”了,也就是说函数返回的地址位于栈的增长方向上,是不稳定和不被保证的。

那么返回结构体的函数则不同,你可以发现返回结构体的函数是工作正常有效的。在add 函数中有一个

临时性结构体t,毫无疑问,t 将在add 函数返回时被释放,但由于t 被当做“值”进行返回,因此编译器将保证add 的返回值对于add 的调用者(caller)来说是有效的。

另外需要明确的一点是,我个人觉得,现实里这种返回结构体的方式比较少见,后面将会看到这样做会产生临时对象和多余拷贝过程,效率不高。常见方法是传递结构体指针。但作为语言上允许的方式,有必要弄清楚编译器如何实现这种方式,而要弄清楚这个问题,需要查看汇编代码。使用VC6 输入上述代码,下面分别给出其汇编代码。

(1)debug 版本,汇编代码如下。

.text:00401020add proc near ; CODE XREF: j_addj

.text:00401020

.text:00401020 var_48 = dword ptr -48h

.text:00401020 var_8 = dword ptr -8

.text:00401020 var_4 = dword ptr -4

.text:00401020 arg_0 = dword ptr 8

.text:00401020 arg_4 = dword ptr 0Ch

.text:00401020

.text:00401020push ebp

.text:00401021mov ebp, esp

.text:00401023sub esp, 48h

.text:00401026push ebx

.text:00401027push esi

.text:00401028push edi

.text:00401029lea edi, [ebp+var_48]

.text:0040102C mov ecx, 12h

.text:00401031mov eax, 0CCCCCCCCh

.text:00401036rep stosd

.text:00401038mov eax, [ebp+arg_0]

.text:0040103B imul eax, [ebp+arg_4]

.text:0040103F mov[ebp+var_8], eax

.text:00401042mov eax, [ebp+var_8]

.text:00401045mov edx, [ebp+var_4]

.text:00401048pop edi

.text:00401049pop esi

.text:0040104A pop ebx

.text:0040104B mov esp, ebp

.text:0040104D pop ebp

.text:0040104E retn

.text:0040104E add endp

.text:0040104E

.text:0040104E; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?

.text:0040104F dd 4 dup(0CCCCCCCCh)

.text:0040105F align 10h

.text:00401060

.text:00401060; 圹圹圹圹圹圹圹?S U B R O U T I N E 圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹? .text:00401060

.text:00401060; Attributes: bp-based frame

.text:00401060

.text:00401060 main proc near ; CODE XREF: j_mainj

.text:00401060

.text:00401060 var_50 = dword ptr -50h

.text:00401060 var_10 = dword ptr -10h

.text:00401060 var_C = dword ptr -0Ch

.text:00401060 var_8 = dword ptr -8

.text:00401060 var_4 = dword ptr -4

.text:00401060

.text:00401060push ebp

.text:00401061mov ebp, esp

.text:00401063sub esp, 50h

.text:00401066push ebx

.text:00401067push esi

.text:00401068push edi

.text:00401069lea edi, [ebp+var_50]

.text:0040106C mov ecx, 14h

.text:00401071mov eax, 0CCCCCCCCh

.text:00401076rep stosd

.text:00401078push4

.text:0040107A push3

.text:0040107C call j_add

.text:00401081add esp, 8

.text:00401084mov[ebp+var_10], eax

.text:00401087mov[ebp+var_C], edx

.text:0040108A mov eax, [ebp+var_10]

.text:0040108D mov[ebp+var_8], eax

.text:00401090mov ecx, [ebp+var_C]

.text:00401093mov[ebp+var_4], ecx

.text:00401096mov edx, [ebp+var_8]

.text:00401099push edx

.text:0040109A push offset ??_C@_0L@CMGB@t?4a?5?$DN?5?$CFld?6?$AA@ ; "t.a = %ld\n"

.text:0040109F call printf

.text:004010A4add esp, 8

.text:004010A7xor eax, eax

.text:004010A9pop edi

.text:004010AA pop esi

.text:004010AB pop ebx

.text:004010AC add esp, 50h

.text:004010AF cmp ebp, esp

.text:004010B1call__chkesp

.text:004010B6mov esp, ebp

.text:004010B8pop ebp

.text:004010B9retn

.text:004010B9 main endp

下面是实现方式的栈示意图:

总结:

(1.1)用edx:eax 传递返回值。调用方不需要在栈上向add 函数传递接受返回值的地址。

(2.2)debug 版本在调用方生成临时对象返回值,然后再把临时对象拷贝到main 临时变量所在地址。效率低。

(2)release 版本,汇编代码如下:

.text:00401000 sub_401000 proc near ; CODE XREF: sub_401020+7p

.text:00401000

.text:00401000 var_4 = dword ptr -4

.text:00401000 arg_0 = dword ptr 4

.text:00401000 arg_4 = dword ptr 8

.text:00401000

.text:00401000mov eax, [esp+arg_0] ; add 函数

.text:00401004mov edx, [esp+var_4]

.text:00401008sub esp, 8

.text:0040100B imul eax, [esp+8+arg_4]

.text:00401010add esp, 8

.text:00401013retn

.text:00401013 sub_401000 endp

.text:00401013

.text:00401013; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?

.text:00401014align 10h

.text:00401020

.text:00401020; 圹圹圹圹圹圹圹?S U B R O U T I N E 圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹? .text:00401020

.text:00401020

.text:00401020 sub_401020 proc near ; CODE XREF: start+AFp

.text:00401020

.text:00401020 var_4 = dword ptr -4

.text:00401020

.text:00401020sub esp, 8; 相当于main 函数

.text:00401023push4

.text:00401025push3

.text:00401027call sub_401000

.text:0040102C add esp, 8

.text:0040102F mov[esp+8+var_4], edx

.text:00401033push eax

.text:00401034push offset aT_aLd ; "t.a = %ld\n"

.text:00401039call sub_401050

.text:0040103E xor eax, eax

.text:00401040add esp, 10h

.text:00401043retn

.text:00401043 sub_401020 endp

总结:

(2.1)同(1.1),用edx:eax 传递返回值,不需要传递接收返回值的地址。

(2.2)release 版本调用方没有临时对象,效率基本等同于传结构体指针。

(2.3)release 版本优化的太厉害,甚至都没有把返回值完整的拷贝到临时变量t (只拷贝了结构体中的成员t.b,t.a 的拷贝被认为没有存在价值而被优化掉了,因为t.a 的值存于eax),和高级语言有较大差别。

(二)超过8 bytes 的结构体,调用方需要提供用于接收返回值的地址。

如果是超过8 bytes 的结构体,EDX:EAX 将容纳不下,这时就需要调用方提供接受返回值的地址,即调用方在栈上分配临时对象,并把其地址通过栈传递给函数(先push 参数,最后push 用于设置返回值的结构体地址)。

把上述代码中的结构体定义增加一个int 成员即可令结构体超过8 bytes,即调整上述代码的struct 定义:

struct A

{

int a;

int b;

int c;

};

使用VC6 编译后产生的汇编代码如下:

debug 版本:

.text:00401020add proc near ; CODE XREF: j_addj

.text:00401020

.text:00401020 var_4C = dword ptr -4Ch

.text:00401020 var_C = dword ptr -0Ch

.text:00401020 var_8 = dword ptr -8

.text:00401020 var_4 = dword ptr -4

.text:00401020 arg_0 = dword ptr 8

.text:00401020 arg_4 = dword ptr 0Ch

.text:00401020 arg_8 = dword ptr 10h

.text:00401020

.text:00401020push ebp

.text:00401021mov ebp, esp

.text:00401023sub esp, 4Ch

.text:00401026push ebx

.text:00401027push esi

.text:00401028push edi

.text:00401029lea edi, [ebp+var_4C]

.text:0040102C mov ecx, 13h

.text:00401031mov eax, 0CCCCCCCCh

.text:00401036rep stosd

.text:00401038mov eax, [ebp+arg_4]

.text:0040103B imul eax, [ebp+arg_8]

.text:0040103F mov[ebp+var_C], eax

.text:00401042mov ecx, [ebp+arg_0]

.text:00401045mov edx, [ebp+var_C]

.text:00401048mov[ecx], edx

.text:0040104A mov eax, [ebp+var_8]

.text:0040104D mov[ecx+4], eax

.text:00401050mov edx, [ebp+var_4]

.text:00401053mov[ecx+8], edx

.text:00401056mov eax, [ebp+arg_0]

.text:00401059pop edi

.text:0040105A pop esi

.text:0040105B pop ebx

.text:0040105C mov esp, ebp

.text:0040105E pop ebp

.text:0040105F retn

.text:0040105F add endp

.text:0040105F

.text:00401060

.text:00401060; 圹圹圹圹圹圹圹?S U B R O U T I N E 圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹? .text:00401060

.text:00401060; Attributes: bp-based frame

.text:00401060

.text:00401060 main proc near ; CODE XREF: j_mainj

.text:00401060

.text:00401060 var_64 = dword ptr -64h

.text:00401060 var_24 = dword ptr -24h

.text:00401060 var_18 = dword ptr -18h

.text:00401060 var_14 = dword ptr -14h

.text:00401060 var_10 = dword ptr -10h

.text:00401060 var_C = dword ptr -0Ch

.text:00401060 var_8 = dword ptr -8

.text:00401060 var_4 = dword ptr -4

.text:00401060

.text:00401060push ebp

.text:00401061mov ebp, esp

.text:00401063sub esp, 64h

.text:00401066push ebx

.text:00401067push esi

.text:00401068push edi

.text:00401069lea edi, [ebp+var_64]

.text:0040106C mov ecx, 19h

.text:00401071mov eax, 0CCCCCCCCh

.text:00401076rep stosd

.text:00401078push4

.text:0040107A push3

.text:0040107C lea eax, [ebp+var_24]

.text:0040107F push eax

.text:00401080call j_add

.text:00401085add esp, 0Ch

.text:00401088mov ecx, [eax]

.text:0040108A mov[ebp+var_18], ecx

.text:0040108D mov edx, [eax+4]

.text:00401090mov[ebp+var_14], edx

.text:00401093mov eax, [eax+8]

.text:00401096mov[ebp+var_10], eax

.text:00401099mov ecx, [ebp+var_18]

.text:0040109C mov[ebp+var_C], ecx

.text:0040109F mov edx, [ebp+var_14]

.text:004010A2mov[ebp+var_8], edx

.text:004010A5mov eax, [ebp+var_10]

.text:004010A8mov[ebp+var_4], eax

.text:004010AB mov ecx, [ebp+var_C]

.text:004010AE push ecx

.text:004010AF push offset ??_C@_0L@CMGB@t?4a?5?$DN?5?$CFld?6?$AA@ ; "t.a = %ld\n"

.text:004010B4call printf

.text:004010B9add esp, 8

.text:004010BC xor eax, eax

.text:004010BE pop edi

.text:004010BF pop esi

.text:004010C0pop ebx

.text:004010C1add esp, 64h

.text:004010C4cmp ebp, esp

.text:004010C6call__chkesp

.text:004010CB mov esp, ebp

.text:004010CD pop ebp

.text:004010CE retn

.text:004010CE main endp

release 版本:

.text:00401000 sub_401000 proc near ; CODE XREF: sub_401030+Cp

.text:00401000

.text:00401000 var_8 = dword ptr -8

.text:00401000 var_4 = dword ptr -4

.text:00401000 arg_0 = dword ptr 4

.text:00401000 arg_4 = dword ptr 8

.text:00401000 arg_8 = dword ptr 0Ch

.text:00401000

.text:00401000mov ecx, [esp+arg_4]

.text:00401004mov eax, [esp+arg_0]

.text:00401008sub esp, 0Ch

.text:0040100B imul ecx, [esp+0Ch+arg_8]

.text:00401010mov edx, eax

.text:00401012mov[edx], ecx

.text:00401014mov ecx, [esp+0Ch+var_8]

.text:00401018mov[edx+4], ecx

.text:0040101B mov ecx, [esp+0Ch+var_4]

.text:0040101F mov[edx+8], ecx

.text:00401022add esp, 0Ch

.text:00401025retn

.text:00401025 sub_401000 endp

.text:00401025

.text:00401025; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?

.text:00401026align 10h

.text:00401030

.text:00401030; 圹圹圹圹圹圹圹?S U B R O U T I N E 圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹? .text:00401030

.text:00401030

.text:00401030 sub_401030 proc near ; CODE XREF: start+AFp

.text:00401030

.text:00401030 var_14 = dword ptr -14h

.text:00401030 var_10 = dword ptr -10h

.text:00401030 var_C = dword ptr -0Ch

.text:00401030

.text:00401030sub esp, 18h

.text:00401033push4

.text:00401035lea eax, [esp+1Ch+var_C]

.text:00401039push3

.text:0040103B push eax

.text:0040103C call sub_401000

.text:00401041mov ecx, eax

.text:00401043add esp, 0Ch

.text:00401046mov eax, [ecx]

.text:00401048push eax

.text:00401049push offset aT_aLd ; "t.a = %ld\n"

.text:0040104E mov edx, [ecx+4]

.text:00401051mov[esp+20h+var_14], edx

.text:00401055mov ecx, [ecx+8]

.text:00401058mov[esp+20h+var_10], ecx

.text:0040105C call sub_401070

.text:00401061xor eax, eax

.text:00401063add esp, 20h

.text:00401066retn

.text:00401066 sub_401030 endp

上述两种编译结果,实现的模型基本相同。因此在这里以debug版本代码为主,一并分析,其栈示意图如下,下图左侧为debug 版本,右侧是release 版本:

总结:

(1)当结构体超过8 bytes,不能用EDX:EAX 传递,这时调用方在栈上保留有一个用于填充返回值的结构体,其地址在入栈参数后push 到栈上。函数将会根据这个地址,把返回值设置到这个地址。

(2)在main 函数中,debug 版本比release 版本还多了一个临时对象,效率低。而release 版本中只有返回值和临时变量t,效率略高于debug。但两者模型基本一致,总体效率低于传结构体指针。

(3)release 版本同样优化比较厉害,main 函数中对t 的赋值是不完整的,因为编译器认为没有必要,只要满足代码等效即可。

最后我们总结针对较大结构体(超过8 bytes)时,返回结构体的函数的实现方式的基本模型:

(1)调用方在栈上分配用于接收返回值的临时结构体,并把地址通过栈传递给函数。

(2)函数根据返回值的地址,设置返回值。

(3)调用方根据需要,把返回值再赋值给需要的临时变量。

(4)返回时,eax 存储的是返回值的那个地址。

因此,从上面的过程可以看到,由于存在临时对象和拷贝操作,其效率比传递结构体指针的函数低。

由于不管debug 还是release,对于“大结构体”都会在栈上传递返回值的地址,所以我们可以通过下面的代码,来测试出这样的结论:函数add 的返回值(临时结构体)的地址和main 中的变量t 的地址是不同的。

原理是,第一个形参的栈顶方向的相邻元素就是返回值的地址,因此用一个指针指向第一个形参,然后向栈顶移动一格,取出其值,就是返回值的地址。

#include

struct A

{

int a;

int b;

int c;

};

struct A add(int x, int y)

{

struct A t;

int* p = &x;

p--;

printf("address of return struct: %08X\n", *p);

t.a = x * y;

return t;

}

int main(int argc, char* argv[])

{

struct A t = add(3, 4);

struct A *p1 = &t;

printf("address of t in main: %p\n", &t);

return0;

}

上面的代码中,有一点需要注意,返回值的地址和t 的地址的关系是依赖编译器的,也就是说,没有任何保证,两者之间是否相邻以及它们之间的大小关系。但你可以通过尝试移动上面的指针p1,试图将p1 指向返回值,但这并不是一个简单容易的事情(因为编译器的行为效果是尽量避免让这个返回值被其他指针指到)。

C语言实现函数返回多值

C语言实现函数返回多值 笔者从事C语言教学多年,在教学中学生们常常会问到如何编写具有多个返回值的C语言函数。编写有多个返回值的函数是所有C语言教材里均没有提到的知识点,但在实际教学与应用的过程中我们都有可能会遇到这样的问题。有学生也尝试了不少方法:如把多个需要返回的值作相应的处理后变成一个可以用语句返回的数据,再在主调函数中拆开返回的数据使之变成几个值;或者return 把需要返回多个值的一个函数分开几个函数去实现多个值的返回。这些方法虽然最终都能实现返回要求的多个值,但从程序算法的合理性与最优化方面去考虑,显然不理想。我们知道C语言函数的返回值是通过函数中的return语句来实现的,可是每调用一次函数,return语句只能返回一个值。那么当我们希望从一个函数中返回多个值时,用什么方法去实现比较合理呢,在教学过程中,我建议学生跳出对return语句的定势思维,一步步引导学生通过几种间接方式实现多个返回值的C语言函数。以下是笔者在教学过程中引导学生采用的三种不同方法编写多个返回值的C语言函数。 2方法1:利用全局变量 分析:全局变量作为C语言的一个知识点,虽然我们都了解它的特点,但 于全局变量的作用域是从定义变量开始在实际教学过程中应用得并不是很多。由 直到程序结束,而对于编写有多个返回值的C语言函数,我们可以考虑把要返回的多个值定义成全局变量。当函数被调用时,全局变量被更改,我们再把更改后的全局变量值应用于主调函数中。函数被调用后被更改后的全局变量值即为函数的数个返回值。下面以一个实例演示该方法的应用。

实例1:编写函数求3个数中的最大值与最小值。 方法:把最大值、最小值分别定义成2个全局变量max、min,在用户自定义函数中把求出来的最大值与最小值分别赋给全局变量max、min。函数调用完毕后全局变量的max、min值即保存了函数要求返回的值。程序参考代码如下: #include "stdio.h" #include "conio.h" int max,min;/*定义两个全局变量用于保存函数返回值*/ void max_min(int a,int b,int c) /*定义求最大最小值的函数*/ {max=min=a; /*初始化最大最小值*/ if(max if(max if(min>b)min=b; if(min>c)min=c; } main() {int x,y,z; printf(" 请输入3个整数:\n"); scanf("%d,%d,%d",&x,&y,&z); max_min(x,y,z) ;/*调用求最大值与最小值的函数*/ printf("三个数中的最大值为:%d;最小值为:%d",max,min);/*输出最大值与最小值*/ getch(); } 调试结果如下: 请输入3个整数: 5,-6,2

结构体和类的比较

结构是一种用关键字struct声明的自定义数据类型。与类相似,也可以包含构造函数,常数,字段,方法,属性,索引器,运算符和嵌套类型等,不过,结构是值类型。 1.结构的构造函数和类的构造函数不同。 2. a.结构不能包含显式的无参数构造函数。结构成员讲自动初始化为它们的默认值。 b.结构不能包含以下形式的初始值设定类:base(argument-list); 2.对于结构中的实例字段成员,不能在声明时赋值初始化。 3.声明了结构类型后,可以使用new运算符创建构造对象,也可以不使用new关键字。如果不使用new,那么在初始化所有字段之前,字段将保持未赋值状态且对象不可用。 4.结构不支持继承,即一个结构不能从另一个结构或类继承,而且不能作为一个类的基类。但是,结构从基类OBJECT继承。结构也可以实现接口。 5.什么时候用结构呢?结构使用简单,并且很有用,但是要牢记:结构在堆栈中创建,是值类型,而类是引用类型。每当需要一种经常使用的类型,而且大多数情况下该类型只是一些数据时,使用结构能比使用类获得更佳性能。 结构是值类型,所以会影响性能,但根据使用结构的方式,这种影响可能是正面的,也可能是负面的。正面的影响是为结构分配内存时,速度非常快,因为它们将内联或者保存在堆栈中。在结构超出了作用域被删除时,速度也很快。另一方面,只要把结构作为参数来传递或者把一个结构赋给另一个结构(例如A=B,其中A和B是结构),结构的所有内容就被复制,而对于类,则只复制引用。这样,就会有性能损失,根据结构的大小,性能损失也不同。注意,结构主要用于小的数据结构。但当把结构作为参数传递给方法时,就应把它作为ref参数传递,以避免性能损失——此时只传递了结构在内存中的地址,这样传递速度就与在类中的传递速度一样快了。另一方面,如果这样做,就必须注意被调用的方法可以改变结构的值。 class和struct有且仅有一个区别,那就是对于class说明的类成员,函数也好,变量也好,如果没有指定类型,缺省是private限定的。而对于struct,则是public的。 结构体数组效率比类数组效率高(不需要装箱合拆箱)。结构体集合(如Hashtable)效率比类集合效率低。集合的元素是引用类型,所以结构体必须进行装箱和拆箱处理。所以类在大的集合中更有效率。

关于返回结构体的函数

(一)不超过8 bytes 的小结构体可以通过EDX:EAX 返回。 本文的范例代码取材于《汇编中函数返回结构体的方法》一文,并在此基础上进行修改和试验。要研究的第一份代码如下,定义一个不超过8 bytes 的小结构体,不超过8 bytes 是因为这个结构体能够用EDX:EAX 容纳,我们之后将看到在release 编译时,编译器能够向返回普通基础类型那样进行返回。 #include //不超过 8 bytes 的“小结构体” struct A { int a; int b; }; //返回结构体的函数 struct A add(int x, int y) { struct A t; t.a = x * y; return t; } int main() { struct A t = add(3, 4); printf("t.a = %ld\n", t.a); return0; } 首先,我们需要解决一个常见困惑,就是要明确这段代码和下面的典型错误代码的区别:char* get_buffer() { char buf[8];

return buf; } 上面的get_buffer 返回的是栈上的临时变量空间,在函数返回后,其所在的空间也就被“回收/释放”了,也就是说函数返回的地址位于栈的增长方向上,是不稳定和不被保证的。 那么返回结构体的函数则不同,你可以发现返回结构体的函数是工作正常有效的。在add 函数中有一个临时性结构体t,毫无疑问,t 将在add 函数返回时被释放,但由于t 被当做“值”进行返回,因此编译器将保证add 的返回值对于add 的调用者(caller)来说是有效的。 另外需要明确的一点是,我个人觉得,现实里这种返回结构体的方式比较少见,后面将会看到这样做会产生临时对象和多余拷贝过程,效率不高。常见方法是传递结构体指针。但作为语言上允许的方式,有必要弄清楚编译器如何实现这种方式,而要弄清楚这个问题,需要查看汇编代码。使用VC6 输入上述代码,下面分别给出其汇编代码。 (1)debug 版本,汇编代码如下。 small_struct_debug 下面是实现方式的栈示意图:

函数参数返回值总结

函数的参数、返回值总结 (一)参数 ◆函数分: 有参函数:函数名(实参列表) 无参函数:函数名() ◆有参函数调用语句中的实参应与被调函数中的形参在个数、类型、顺序上一致。 ◆参数传递时,实参向形参一一对应进行单向的值传递。值:可是数值(变量或数 组元素)或数值的地址值(指针或数组名)。 (二)返回值 函数的返回值即为函数调用后的结果,可有如下返回结果的方法: (1)通过return语句返回一个值; (2)利用地址做参数返回一个或多个值; (3)利用全局变量返回一个或多个值。 (三)例 1、170页实验内容(1):打印由正三角和倒三角组成的图形。 有一个参数,无返回值。实参向形参传递一个数值。 #include /* 有一个参数,无返回值的函数,打印正三角 */ void f1(int n) /* 形参只能是变量,用来接收实参传来的数值 */ { int i,j,k; for(k=1;k<=n;k++) {for(i=1;i<=10-k;i++) printf(" "); for(j=1;j<=k;j++) printf(" *"); printf("\n");} } /* 有一个参数,无返回值的函数,打印倒三角*/ void f2(int n) {int i,j,k; for(k=n;k>=1;k--) {for(i=1;i<=10-k;i++) printf(" "); for(j=1;j<=k;j++) printf(" *"); /*双引号内应为“空格加半角星号”*/ printf("\n");} } main() { int n; scanf("%d",&n);

函数、指针与结构体练习题_参考答案

函数 (一)选择题 1.以下正确的说法是_________. 建立函数的目的之一是a)提高程序的执行效率 b)提高程序的可读性 c)减少程序的篇幅 d)减少程序文件所占存 2.以下正确的函数原型声明形式是________. a)double fun(int x,int y) b)double fun(int x; int y) c)double fun(int x, int y); d)double fun(int x,y); 3.C语言规定,简单变量做实参时,它和对应形参之间的数据传递方式为______. A)地址传递 B)单向值传递 C)由实参传给形参,再由形参传回给实参 D)由用户指定传递方式 4.C语言允许函数值类型缺省定义,此时该函数值隐含的类型是______. a)float b)int c)long d)double 5.已有以下数组定义和f函数调用语句,则在f函数的说明中,对形参数组array 的错误定义方式为________. int a[3][4]; f(a); a)f(int array[][6])

b)f(int array[3][]) c)f(int array[][4]) d)f(int array[2][5]) 6.以下程序的正确运行结果是_________. #include void num() { extern int x,y;int a=15,b=10; x=a-b; y=a+b; } int x,y; main() { int a=7,b=5; x=a+b; y=a-b; num(); printf("%d,%d\n",x,y); } a)12,2 b)不确定c)5,25 d)1,12 7.以下正确的描述是____________. a)C语言的预处理功能是指完成宏替换和包含文件的调用 b)预处理指令只能位于C源程序文件的首部 c)凡是C源程序中行首以"#"标识的控制行都是预处理指令 d)C语言的编译预处理就是对源程序进行初步的语法检查 8.在"文件包含"预处理语句的使用形式中,当#include后面的文件名用< >(尖括号)括起时,找寻被包含文件的方式是_______. a)仅仅搜索当前目录 b)仅仅搜索源程序所在目录

POLL返回值详解

POLL返回值详解 和select() 函数一样,poll() 函数也可以用于执行多路复用I/O 。但poll() 与slect()相比,用起来更加直观容易。使用该函数,需要包含#include 文件,实际上最终包含的是文件,poll.h 里的内容也就是#include 。 函数的原型: 引用 #include extern int poll (struct pollfd*__fds,nfds_t__nfds,int__timeout); poll() 没有像select() 构建fd_set 结构体的3 个数组( 针对每个条件分别有一个数组:可读性、可写性和错误条件) ,然后检查从0 到nfds 每个文件描述符。 第一个参数pollfd 结构体定义如下: 引用 /* Data structure describing a polling request. */ struct pollfd { int fd; /* poll 的文件描述符. */ short int events; /* fd 上感兴趣的事件(等待的事件). */ short int revents; /* fd 上实际发生的事件. */ }; fd成员表示感兴趣的,且打开了的文件描述符; events成员是位掩码,用于指定针对这个文件描述符感兴趣的事件;revents成员是位掩码,用于指定当poll 返回时,在该文件描述符上已经发生了哪些事情。 events 和revents 结合下列常数值(宏)指定即将唤醒的事件或调查已结束的poll() 函数被唤醒的原因,这些宏常数如下: ?POLLIN events 中使用该宏常数,能够在折本文件的可读情况下,结束poll() 函数。相反,revents 上使用该宏常数,在检查poll() 函数结束后,可依此判断设备文件是否处于可读状态(即使消息长度是0)。 ?POLLPRI 在events 域中使用该宏常数,能够在设备文件的高优先级数据读取状态下,结束poll() 函数。相反,revents 上使用该宏常数,在检查poll() 函数结束后,

C语言结构体(struct)常见使用方法

C语言结构体(struct)常见使用方法 基本定义:结构体,通俗讲就像是打包封装,把一些有共同特征(比如同属于某一类事物的属性,往往是某种业务相关属性的聚合)的变量封装在内部,通过一定方法访问修改内部变量。 结构体定义: 第一种:只有结构体定义 [cpp]view plain copy 1.struct stuff{ 2.char job[20]; 3.int age; 4.float height; 5.}; 第二种:附加该结构体类型的“结构体变量”的初始化的结构体定义 [cpp]view plain copy 1.//直接带变量名Huqinwei 2.struct stuff{ 3.char job[20]; 4.int age; 5.float height; 6.}Huqinwei; 也许初期看不习惯容易困惑,其实这就相当于: [cpp]view plain copy 1.struct stuff{ 2.char job[20]; 3.int age;

4.float height; 5.}; 6.struct stuff Huqinwei; 第三种:如果该结构体你只用一个变量Huqinwei,而不再需要用 [cpp]view plain copy 1.struct stuff yourname; 去定义第二个变量。 那么,附加变量初始化的结构体定义还可进一步简化出第三种: [cpp]view plain copy 1.struct{ 2.char job[20]; 3.int age; 4.float height; 5.}Huqinwei; 把结构体名称去掉,这样更简洁,不过也不能定义其他同结构体变量了——至少我现在没掌握这种方法。 结构体变量及其内部成员变量的定义及访问: 绕口吧?要分清结构体变量和结构体内部成员变量的概念。 就像刚才的第二种提到的,结构体变量的声明可以用: [cpp]view plain copy 1.struct stuff yourname; 其成员变量的定义可以随声明进行: [cpp]view plain copy 1.struct stuff Huqinwei = {"manager",30,185}; 也可以考虑结构体之间的赋值: [cpp]view plain copy

C语言题库第8章 结构体和共同体

第八章结构体和共同体 一、单项选择 1. 若有以下定义: struct link { int data; struct link *next; }a,b,c,*p,*q; 且变量a和b之间已有如下图所示的链表结构,若指针p指向a,指针q指向c。 则能把c插入到a和b之间形成新的链表的语句是( C ) 2. 若有以下程序段: int a=1,b=2,c=3; struct dent { int n ; int *m ; } s[3] = {{101,&a},{102,&b},{103,&c}}; struct dent *p=s ; 则以下表达式中值为2的是( D )。 3. 下面程序的运行结果是( D )。 #iunclude int main ( ) { struct complx { int x; int y ;

}cnum[2]={1,3,2,7} ; printf(“%d\n”,cnum[0].y/cnum[0].x*cnum[1].x) ; return 0; } 二、程序设计 1. /*学生的记录由学号和成绩组成,N名学生的数据已在主函数中放入结构体数组s 中,请编写函数fun, 它的功能是:按分数的高低排列学生的记录,高分在前。注意:部分源程序给出如下。 请勿改动main函数和其他函数中的任何内容,仅在函数fun的花括号中填入所编写的若干语句。 试题程序: */ #include #define N 16 typedef struct { char num[10]; int s ; }STREC; void fun (STREC a[]) { /*********Begin*********/ /*********End**********/ } int main () { FILE *wf,*in; STREC s[N]={{ "GA005",85},{"GA003",76},{"GA002",69},{"GA004",85}, {"GA001",91},{"GA007",72},{"GA008",64},{"GA006",87}, {"GA015",85},{"GA013",91},{"GA012",64},{"GA014",91}, {"GA011",66},{"GA017",64},{"GA018",64},{"GA016",72}}; int i; fun(s); printf("The data after sorted :\n"); for (i=0; i

11讲_JavaScript事件分析

Company name WEB 前端开发技术 HTML JavaScript CSS WEB 前端开发技术 第11章JavaScript 事件分析 计算机科学与技术系

Web前端开发技术主要内容 计算机科学与技术系 ?掌握事件、事件类型的概念 ?掌握事件处理的机制 ?掌握事件名称与句柄的关系 ?学会编写各类的事件响应程序

计算机科学与技术系 Web前端开发技术11.1 事件编程 事件编程:让用户不仅能够浏览页面中的内容,而且还可以和页面元素进行交互。 事件-事件是可以被JavaScript侦测到的行为(ACTION)。 事件源Window Form Mouse key 事件 单击事件 双击事件 事件句柄 Onclick ondblclick 编写事件 处理代码

Web 前端开发技术事件驱动案例导入 计算机科学与技术系 事件处理 你好!这是一个简单事件处理程序!

计算机科学与技术系 Web 前端开发技术 11.1 事件编程(续) 1.网页访问中常见的事件 鼠标单击:例如单击button 、选中checkbo x 和radio 等元素;鼠标进入、悬浮或退出页面的某个热点:例如鼠标停在一个图片上方或者进入table 的范围; 键盘按键:当按下按键或释放按键时;页面或图像载入:例如页面body 被加载时;在表单中选取输入框或改变输入框中文本的内容:例如选中或修改了文本框中的内容;确认表单:例如当准备提交表单的内容。 事件类型:1.鼠标事件2.键盘事件3.浏览器事件

C语言函数不可以返回数组却可以返回结构体这是为什么呢

C语言函数不可以返回数组却可以返回结构体这是为什么呢 最近有读者问我,为什么C语言函数可以返回结构体,却不可以返回数组。有这样的问题并不奇怪,因为C语言数组和结构体本质上都是管理一块内存,那为何编译器要区别对待二者呢? C语言函数为什么不能返回数组? 在C语言程序开发中,我们不可以编写下面这样的代码: 这其实就是不能在C语言函数中返回数组。但是如果将数组定义在结构体里面,就可以将其返回了,例如下面这段C语言代码,请看:

结构体 s 只有一个数组成员 arr,显然,函数可以返回结构体,即使结构体只有一个数组成员,这是为什么呢? C语言没有严格意义上的“数组类型” 基本上,C语言中的数据结构可以分为两类,第一类数据结构可以被赋值,而第二类数据结构不可以被赋值,数组属于第二类数据结构。 除了数组,还有其他第二类数据结构吗?我想基本上没有了,除非把函数算上。 与函数不能返回数组密切相关的事实是,C语言没有严格意义上的“数组类型”。可能从C语言代码角度来看,似乎有数组类型的变量,但是如果尝试将该变量像其他变量一样使用,得到的实际上是指向数组第一个元素的指针。例如下面这段C语言代码: char a[10], b[10]; a = b; 这并不能把数组 b 的内容拷贝给数组 a,实际上,上面两行C语言代码相当于下面这一行: a = &b[0]; 显然,左边是数组 a,而右边其实是一个指针。即使数组在某种程度上可以看作能够被赋值,但我们有很大几率得到类型不匹配,例如下面这段C语言代码: a = f(); 这里假设 f() 是一个返回数组的函数,它的核心C语言代码如下: char ret[10]; /* ... fill ... */ return ret; 不过按照前面所说的,其实上面的返回语句相当于下面这一句:

事件处理函数中返回值

事件处理函数中返回值 事件处理函数返回值其实指当事件发生时,浏览器会执行默认的操作,而当事件处理函数会返回一个结果,而当这个结果为true时,浏览器会继续执行默认操作,否则会停止执行。如果还是不懂的话,我们看一下下面这个实例: 当点击超链接标签时,如果check()的值为true,那么浏览器会跳转到abc.html页面中去,如果check()的值为false,点击超链接标签就不会跳转 这里return其实是对事件对象中的returnValue属性值的设置,而该属性就决定了该事件操作是否继续操作,当retrunValue为true时则继续操作,为false时则中断操作。 然而直接执行函数check,不使用return返回将不会对eturnvalue进行设置所以会默认地继续执行操作,比如如下实例 上面的实例就是不管check()的结果是true还是fasle,浏览器都会跳转到abc.html 页面中去。所以必须使用return返回。 事件处理函数返回值在表单中也存在这种情况,如下图

判断用户名是否为空,如果为空就不提交表单,否则就提交表单...跟上面理解是一样的。 讲到这里有很多同学在这里还能理解,但是呢,换个地方,换个事件绑定方式就不能理解了。 比如:在DOM对象上绑定事件: 很多人不能理解的是:在html元素上绑定事件时,return用了两次,才能阻止表单的提交,为什么在DOM对象上绑定事件时只用了一次return就能阻止表单提交,这里我们就要看看为什么了。 我们看看直接打印btn.onclick的结果,发现我们在html元素上绑定的事件处理函数fn是出现在DOM对象上事件处理函数的里面。 所以onclick=”return fn()”等价于 btn.onclick=function(){ return fn() },而fn()的结果true/false就决定表单是否提交。 总结:事件函数返回值; 如果返回true或者不返回,浏览器执行默认操作; 如果返回false,阻止浏览器默认操作。

函数与结构体

函数与结构体 写出下面程序的执行结果。 #include struct tree { int x; int y; } t; voidfunc(struct tree t) { t.x = 10; t.y = 20; } main() { t.x = 1; t.y = 2; func(t); printf("%d %d\n", t.x, t.y); } 为了加深对结构的理解,下面编写几个用于对点和矩形进行操作的函数 把结构体传递给函数的方式有三种:一是分别传送各个结构成员,二是传送整个结构,三是传送指向结构的指针。 一 /* makepoint: 通过x、y值确定一个点*/ struct point makepoint(int x, int y) { struct point temp; temp.x = x; temp.y = y; return temp; } 注意,变元和结构成员同名不会引起冲突,事实上,重名强调了两者之间的关系。 现在可以用makepoint动态初始化任意结构,也可以向函数提供结构类型变元: structrect screen; struct point middle; struct point makepoint(int, int); screen.pt1 = makepoint(0, 0); screen.pt2 = makepoint(XMAX, YMAX); middle = makepoint((screen.pt1.x + screen.pt2.x) / 2, (screen.pt1.y + screen.pt2.y) / 2);

回调函数的理解

什么是回调函数 精妙比喻:回调函数还真有点像您随身带的BP机:告诉别人号码,在它有事情时Call您 回调用于层间协作,上层将本层函数安装在下层,这个函数就是回调,而下层在一定条件下触发回调,例如作为一个驱动,是一个底层,他在收到一个数据时,除了完成本层的处理工作外,还将进行回调,将这个数据交给上层应用层来做进一步处理,这在分层的数据通信中很普遍。 其实回调和API非常接近,他们的共性都是跨层调用的函数。但区别是API是低层提供给高层的调用,一般这个函数对高层都是已知的;而回调正好相反,他是高层提供给底层的调用,对于低层他是未知的,必须由高层进行安装,这个安装函数其实就是一个低层提供的API,安装后低层不知道这个回调的名字,但它通过一个函数指针来保存这个回调,在需要调用时,只需引用这个函数指针和相关的参数指针。 其实:回调就是该函数写在高层,低层通过一个函数指针保存这个函数,在某个事件的触发下,低层通过该函数指针调用高层那个函数。 Callback Function 什么是回调函数? 回调函数是应用程序提供给Windows系统DLL或其它DLL调用的函数,一般用于截获消息、获取系统信息或处理异步事件。应用程序把回调函数的地址指针告诉DLL,而DLL在适当的时候会调用该函数。回调函数必须遵守事先规定好的参数格式和传递方式,否则DLL一调用它就会引起程序或系统的崩溃。通常情况下,回调函数采用标准WindowsAPI的调用方式,即__stdcall,当然,DLL编制者可以自己定义调用方式,但客户程序也必须遵守相同的规定。在__stdcall方式下,函数的参数按从右到左的顺序压入堆栈,除了明确指明是指针或引用外,参数都按值传递,函数返回之前自己负责把参数从堆栈中弹出。 理解回调函数! 程序在调用一个函数(function)时(通常指api).相当于程序(program)呼叫(Call)了一个函数(function)关系表示如下: call(调用) program --------------------→dll 程序在调用一个函数时,将自己的函数的地址作为参数传递给程序调用的函数时(那么这个自己的函数称回调函数).需要回调函数的DLL 函数往往是一些必须重复执行某些操作的函数.关系表示如下: call(调用) program --------------------→dll ↑¦ ¦_______________________________¦ callback(回调)

结构体在函数中的应用

结构体在函数中的应用 前天在编写一段代码时突然对结构体在函数中的用法有些模糊了,经过复习,基本弄清了这些知识,特总结如下: 一、结构体与函数参数 结构体作函数参数可分为传值与传指针。 1.传值时结构体参数会被拷贝一份,在函数体内修改结构体参数成员的值实际上是修改调用参数的一个临时拷贝的成员的值,这不会影响到调用参数。在这种情况下,由于涉及到结构体参数的拷贝,程序空间及时间效率都会受到影响,所以这种方法基本不用。 例如: 2.传指针时直接将结构体的首地址传递给函数体,在函数体中通过指针引用结构体成员,可以对结构体参数成员的值造成实际影响。这种用法效率高,经常采用。 例如: 二、结构体与函数返回值 对于某些版本的C语言编译器,返回值仅能为基本数据类型如int、char以及指针,因此结构体作为一种组合数据类型,不能以值的方式返回,而在有些版本的C编译器中又可以直接返回结构体变量,在C++中也是可以直接返回结构体变量的。 直接返回结构体变量示例如下;

以指针方式返回结构体示例如下: 关于结构体,看内核又遇到了,关于赋值中存在·的奇怪用法,在网上没有找到答案,却把以前一直弄的比较模糊的对齐问题给翻出来了。如下为转发内容: 齐原则总结:(在没有#pragma pack宏的情况下): 原则1:数据成员对齐规则:结构(struct或联合union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。 原则2:结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部

WSAAsyncSelect()函数详解

WSAAsyncSelect()函数详解 WSAAsyncSelect() 简述: 通知套接口有请求事件发生. #include int PASCAL FAR WSAAsyncSelect ( SOCKET s, HWND hWnd, unsigned int wMsg, long lEvent ); s 标识一个需要事件通知的套接口的描述符. hWnd 标识一个在网络事件发生时需要接收消息的窗口句柄. wMsg 在网络事件发生时要接收的消息. lEvent 位屏蔽码,用于指明应用程序感兴趣的网络事件集合. 注释: 本函数用来请求Windows Sockets DLL为窗口句柄发一条消息-无论它何时检测到由lEvent参数指明的网络事件.要发送的消息由wMsg参数标明.被通知的套接口由s标识. 本函数自动将套接口设置为非阻塞模式. lEvent参数由下表中列出的值组成. 值意义 FD_READ 欲接收读准备好的通知. FD_WRITE 欲接收写准备好的通知. FD_OOB 欲接收带边数据到达的通知. FD_ACCEPT 欲接收将要连接的通知. FD_CONNECT 欲接收已连接好的通知. FD_CLOSE 欲接收套接口关闭的通知. 启动一个WSAAsyncSelect()将使为同一个套接口启动的所有先前的WSAAsyncSelect()作废. 例如,要接收读写通知,应用程序必须同时用FD_READ 和FD_WRITE调用WSAAsyncSelect(),如下: rc = WSAAsyncSelect(s, hWnd, wMsg, FD_READ|FD_WRITE); 对不同的事件区分不同的消息是不可能的.下面的代码将不会工作;第二个调用将会使第一次调用的作用失效,只有FD_WRITE会通过wMsg2消息通知到. rc = WSAAsyncSelect(s, hWnd, wMsg1, FD_READ); rc = WSAAsyncSelect(s, hWnd, wMsg2, FD_WRITE); 如果要取消所有的通知,也就是指出Windows Sockets的实现不再在套接口上发送任何和网络事件相关的消息,则lEvent应置为0. rc = WSAAsyncSelect(s, hWnd, 0, 0);

如何编写有多个返回值的C语言函数

如何编写有多个返回值的C语言函数 1引言 笔者从事C语言教学多年,在教学中学生们常常会问到如何编写具有多个返回值的C 语言函数。编写有多个返回值的函数是所有C语言教材里均没有提到的知识点,但在实际教学与应用的过程中我们都有可能会遇到这样的问题。有学生也尝试了不少方法:如把多个需要返回的值作相应的处理后变成一个可以用return语句返回的数据,再在主调函数中拆开返回的数据使之变成几个值;或者把需要返回多个值的一个函数分开几个函数去实现多个值的返回。这些方法虽然最终都能实现返回要求的多个值,但从程序算法的合理性与最优化方面去考虑,显然不理想。我们知道C语言函数的返回值是通过函数中的return语句来实现的,可是每调用一次函数,return语句只能返回一个值。那么当我们希望从一个函数中返回多个值时,用什么方法去实现比较合理呢?在教学过程中,我建议学生跳出对return语句的定势思维,一步步引导学生通过几种间接方式实现多个返回值的C语言函数。以下是笔者在教学过程中引导学生采用的三种不同方法编写多个返回值的C语言函数。 2方法1:利用全局变量 分析:全局变量作为C语言的一个知识点,虽然我们都了解它的特点,但在实际教学过程中应用得并不是很多。由于全局变量的作用域是从定义变量开始直到程序结束,而对于编写有多个返回值的C语言函数,我们可以考虑把要返回的多个值定义成全局变量。当函数被调用时,全局变量被更改,我们再把更改后的全局变量值应用于主调函数中。函数被调用后被更改后的全局变量值即为函数的数个返回值。下面以一个实例演示该方法的应用。 实例1:编写函数求3个数中的最大值与最小值。 方法:把最大值、最小值分别定义成2个全局变量max、min,在用户自定义函数中把求出来的最大值与最小值分别赋给全局变量max、min。函数调用完毕后全局变量的max、min 值即保存了函数要求返回的值。程序参考代码如下: #include "stdio.h" #include "conio.h" int max,min;/*定义两个全局变量用于保存函数返回值*/ void max_min(int a,int b,int c) /*定义求最大最小值的函数*/ {max=min=a; /*初始化最大最小值*/ if(max if(max if(min>b)min=b; if(min>c)min=c; } main() {int x,y,z; printf(" 请输入3个整数:\n"); scanf("%d,%d,%d",&x,&y,&z); max_min(x,y,z) ;/*调用求最大值与最小值的函数*/ printf("三个数中的最大值为:%d;最小值为:%d",max,min);/*输出最大值与最小值*/ getch(); } 调试结果如下:

C语言函数说明与返回值(11、12)

在学习C语言函数以前,我们需要了解什么是模块化程序设计方法。 人们在求解一个复杂问题时,通常采用的是逐步分解、分而治之的方法,也就是把一个大问题分解成若干个比较容易求解的小问题,然后分别求解。程序员在设计一个复杂的应用程序时,往往也是把整个程序划分为若干功能较为单一的程序模块,然后分别予以实现,最后再把所有的程序模块像搭积木一样装配起来,这种在程序设计中分而治之的策略,被称为模块化程序设计方法。 在C语言中,函数是程序的基本组成单位,因此可以很方便地用函数作为程序模块来实现C 语言程序。 利用函数,不仅可以实现程序的模块化,程序设计得简单和直观,提高了程序的易读性和可维护性,而且还可以把程序中普通用到的一些计算或操作编成通用的函数,以供随时调用,这样可以大大地减轻程序员的代码工作量。 函数是C语言的基本构件,是所有程序活动的舞台。函数的一般形式是: type-specifier function_name(parameter list) parameter declarations { body of the function } 类型说明符定义了函数中return语句返回值的类型,该返回值可以是任何有效类型。如果没有类型说明符出现,函数返回一个整型值。参数表是一个用逗号分隔的变量表,当函数被调用时这些变量接收调用参数的值。一个函数可以没有参数,这时函数表是空的。但即使没有参数,括号仍然是必须要有的。参数说明段定义了其中参数的类型。 当一个函数没有明确说明类型时,C语言的编译程序自动将整型(i n t)作为这个函数的缺省类型,缺省类型适用于很大一部分函数。当有必要返回其它类型数据时,需要分两步处理: 首先,必须给函数以明确的类型说明符;其次,函数类型的说明必须处于对它的首次调用之前。只有这样,C编译程序才能为返回非整型的值的函数生成正确代码。 4.1.1函数的类型说明 可将函数说明为返回任何一种合法的C语言数据类型。 类型说明符告诉编译程序它返回什么类型的数据。这个信息对于程序能否正确运行关系极大,因为不同的数据有不同的长度和内部表示。 返回非整型数据的函数被使用之前,必须把它的类型向程序的其余部分说明。若不这样做,C语言的编译程序就认为函数是返回整型数据的函数,调用点又在函数类型说明之前,编译程序就会对调用生成错误代码。为了防止上述问题的出现,必须使用一个特别的说明语句,通知编译程序这个函数返回什么值。下例示出了这种方法。

C语言程序设计 结构体和共用体(12.2.15)--实验9结构体数组作函数参数

实验9 结构体数组作函数参数 【实验任务】学生成绩管理系统V5.0 某班有最多不超过30人(具体人数由键盘输入)参加期末考试,考试科目为数学(MT)、英语(EN)和物理(PH)。参考例12.7,定义结构体类型,用结构体数组作函数参数,编程实现如下菜单驱动的学生成绩管理系统: (1)录入每个学生的学号、姓名和各科考试成绩; (2)计算每门课程的总分和平均分; (3)计算每个学生的总分和平均分; (4)按每个学生的总分由高到低排出名次表; (5)按每个学生的总分由低到高排出名次表; (6)按学号由小到大排出成绩表; (7)按姓名的字典顺序排出成绩表; (8)按学号查询学生排名及其考试成绩; (9)按姓名查询学生排名及其考试成绩; (10)按优秀(90~100)、良好(80~89)、中等(70~79)、及格(60~69)、不及格(0~59)5个类别,对每门课程分别统计每个类别的人数以及所占的百分比; (11)输出每个学生的学号、姓名、各科考试成绩,以及每门课程的总分和平均分。 要求程序运行后先显示如下菜单,并提示用户输入选项: 1.Append record 2.Caculate total and average score of every course 3.Caculate total and average score of every student 4.Sort in descending order by total score of every student 5.Sort in ascending order by total score of every student 6.Sort in ascending order by number 7.Sort in dictionary order by name 8.Search by number 9.Search by name 10.Statistic analysis for every course 11.List record 0.Exit Please enter your choice: 然后,根据用户输入的选项执行相应的操作。 题 【思考】 动态单链结构数组编实现 (1)考例 参12.9~例12.11,用向表代替体,程上述程序。 础删记录插记录会动态链与结构数组(2)在(1)基上,增加“除”和“入”的功能,体表体的优 不同点和缺点。 【实验目的】 在实验“学生成绩管理系统V4.0”的基础上,通过增加任务要求,熟悉结构体类型、结构体数组作函数参数、模块化程序设计方法,体会应用结构体类型代替普通的数组类型实现数据库管理的优越性。

BCB第九讲事件和事件处理函数

事件和事件处理函数 在上一讲中我们主要学习了如何设计用户界面,了解了向窗体中添加元件、设置元件属性和调整元件的布局的方法,到目前为止,音乐收藏小程序MP3Collect 的用户界面已经初具雏形了,但是界面上的元件还不能响应用户的输入,因此在本讲中,我们就来为元件添加事件响应函数,让程序真正活起来。 添加记录 MP3Collect 主要的用途是管理音乐文件,我们首先就来实现添加记录的功能。按照我们的设想,添加记录的功能是由用户按下“添加”按钮时启动的,那么怎样才知道用户按下了这个按钮呢?这就引出了今天要介绍的元件事件及其事件处理函数的问题。 讲座前面曾经强调过,CBuilder 开发的应用程序是事件驱动的,用户的操作、系统环境变化以及应用程序之间的相互作用都会触发一些事件,CBuilder 提供的元件可以对这些事件进行响应,每一种元件都有特定的事件集合,该集合与它所实现的功能有关。 选中“添加”命令按钮后,调出它在对象浏览器中的事件页 (图9-1),查看它的事件集合,我们可以发现一个简单的命令按 钮能够响应十多个事件,每种事件的名称都具有“OnXXXX ”的 格式,其中前缀“On ”表示这是事件的名称,而“XXXX ”则代 表了事件的性质。多数事件的名称都很好理解,如果想进一步了 解各个事件的详细含义,可以按F1键查看联机帮助,例如通过联 机帮助我们可以了解到:当用户按下命令按钮时,就会触发 OnClick 事件,因此我们需要响应OnClick 事件,从而实现添加记 录的功能。 所谓响应事件,是指为元件的某个事件指定一个且只能指定 一个处理函数,当该事件发生时,程序会自动执行该函数。 我们在对象浏览器中选中“添加”按钮对象的OnClick 事件, 并在事件名称右侧的编辑框中输入事件处理函数的名称 “BtnAddSongClick ”(可根据需要自行修改),然后按回车键, CBuilder 集成开发环境就会在TMainForm 文件中自动添加一个空的事件处理函数: void __fastcall TMainForm::BtnAddSongClick(TObject *Sender) 除了上述方法之外,还有好几种可以更方便地添加事件处理函数的方法。一种方法是双击事件名称右侧的空白输入框,便能自动添加一个事件处理函数,自动添加的函数的名称是由元件名称和事件名称组合而来的。另一种方法是直接双击放在窗体上的元件对象本身,CBuilder 就会自动为该元件添加缺省事件的处理函数,CBuilder 的每一种元件都有其缺省的事件。 下面我们来给TMainForm::BtnAddSongClick 添加程序代码,完成添加记录的功能。 每条MP3音乐记录由三个部分组成:“文件名称”、“歌曲名称”和“歌手名称”,其中“文件名称”是MP3歌曲文件所在的路径全名,它是每条记录必须有的内容,而“歌曲名称”和“歌手名称”允许为空字符串,即没有内容。上述三部分信息需要用户在三个编辑框中输入,因此在进行添加记录操作时,程序需要首先读取三个编辑框的内容,并检查其中的“文件名称”是否为空,如果为空,则提醒用户该项目不能为空,并中断添加记录的操作,如果不为空,则可以添加该记录。下面就是这部分代码的内容: void __fastcall TMainForm::BtnAddSongClick(TObject *Sender) 图9-1:事件页

相关主题