Pointers on C——10 Structures and Unions.12

10.4 Structures as Function Arguments


A structure variable is a scalar and can be used wherever any other scalar can be used.Thus it is legal to pass a structure as an argument to a function, but this technique is often inappropriate.

结构变量是一个标量,它可以用于其他标量可以使用的任何场合。因此,把结构作为参数传递给一个函数是合法的,但这种做法往往并不适宜。


The following code fragments are from a program written to operate an electronic cash register. Here is the declaration for a structure that contains information about an individual transaction.

下面的代码段取自一个程序,该程序用于操作电子现金收入记录机。下面是一个结构的声明,它包含单笔交易的信息。


typedef struct {

char product[PRODUCT_SIZE];

int quantity;

float unit_price;

float total_amount;

} Transaction;


When a transaction occurs, there are many steps involved, one of which is printing the receipt. Letʹs look at some different ways to perform this task.

当交易发生时,需要涉及很多步骤,其中之一就是打印收据。让我们看看怎样用几种不同的方法来完成


void

print_receipt( Transaction trans )

{

printf( "%s\n", trans.product );

printf( "%d @ %.2f total %.2f\n", trans.quantity,

trans.unit_price, trans.total_amount );

}


If current_trans is a Transaction structure, we could call the function like this:

如果current_trans 是一个Transaction 结构,我们可以像下面这样调用函数:


print_receipt( current_trans );



This approach produces the correct result, but it is inefficient because the call‐by‐value argument passing of C requires that a copy of the argument be given to the function.If PRODUCT_SIZE is 20 and we are using a machine with four‐byte integers and floats,this particular structure occupies 32 bytes. To pass it as an argument, 32 bytes must be copied onto the stack and then discarded later.

这个方法能够产生正确的结果,但它的效率很低,因为C 语言的参数传值调用方式要求把参数的一份拷贝传递给函数。如果PRODUCT_SIZE 为20 ,而且在我们使用的机器上整型和浮点型都占4 个字节,那么这个结构将占据32 个字节的空间。要想把它作为参数进行传递,我们必须把32 个字节复制到堆找中,以后再丢弃。


Compare the previous function with this one:

把前面那个函数和下面这个进行比较:


void

print_receipt( Transaction *trans )

{

printf( "%s\n", trans->product );

printf( "%d @ %.2f total %.2f\n", trans->quantity,

trans->unit_price, trans->total_amount );

}


which would be called in this manner:

这个函数可以像下面这样进行调用:


print_receipt( &current_trans );


Here, a pointer to the structure is passed. The pointer is smaller than the entire structure and therefore more efficient to push on the stack. The price paid for passing a pointer is that we must use indirection in the function to access the members of the structure. The bigger the structure, the more efficient it is to pass a pointer to it.

这次传递给函数的是一个指向结构的指针。指针比整个结构要小得多,所以把它压到堆栈上效率能提高很多。传递指针另外需要付出的代价是我们必须在函数中使用间接访问来访问结构的成员。结构越大,把指向它的指针传递给函数的效率就越高。


On many machines, you can improve the efficiency of the pointer version by declaring the parameter to be a register variable. On some machines, this declaration requires an extra instruction at the beginning of the function to copy the argument from the stack (where it was passed) to the register in which it will be used. But if the function performs indirection on the pointer more than two or three times, then the savings realized in the indirections will be greater than the cost of the additional instruction.

在许多机器中,你可以把参数声明为寄存器变量,从而进一步提高指针传递方案的效率。在有些机器上,这种声明在函数的起始部分还需要二条额外的指令,用于把堆栈中的参数(参数先传递给堆栈)复制到寄存器,供函数使用。但是,如果函数对这个指针的间接访问次数超过两三次,那么使用这种方法所节省的时间将远远高于一条额外指令所花费的时间。


A drawback of passing a pointer is that the function is now able to modify the values in the calling programʹs structure variable. If it is not supposed to do this you can use the const keyword in the function to prevent such modifications. Here is what the function prototype looks like with these two changes:

向函数传递指针的缺陷在于函数现在可以对调用程序的结构变量进行修改。如果我们不希望如此,可以在函数中使用const 关键字来防止这类修改。经过这两个修改之后,现在函数的原型将如下所示:


void print_receipt( register Transaction const *trans );


Letʹs move on to another step in processing a transaction; computing the total amount due. You would expect that the function compute_total_amount would modify the total_amount member of the structure. There are three ways to accomplish this task. Letʹs look at the least efficient way first. The following function 

让我们前进一个步骤,对交易进行处理:计算应该支付的总额。你希望函数comput_total_amount能够修改结构的total_amount 成员。要完成这项任务有三种方法,首先让我们来看一下效率最低的那种。下面这个函数


typedef struct {

char product[PRODUCT_SIZE];

int quantity;

float unit_price;

float total_amount;

} Transaction;


Transaction

compute_total_amount( Transaction trans )

{

trans.total_amount =

trans.quantity * trans.unit_price;

return trans;

}


would be called in this manner:

可以用下面这种形式进行调用:


current_trans = compute_total_amount( current_trans );


A copy of the structure is passed as an argument and modified. Then a copy of the modified structure is returned, so the structure is copied twice.

结构的一份拷贝作为参数传递给函数并被修改。然后一份修改后的结构拷贝从函数返回,所以这个结构被复制了两次。


A slightly better method is to return only the modified value rather than the entire structure. This approach is used by the second function.

一个稍微好点的方法是只返回修改后的值,而不是整个结构。第2 个函数使用的就是这种方法。


float

compute_total_amount( Transaction trans )

{

return trans.quantity * trans.unit_price;

}


However, this function must be invoked in this manner

但是,这个函数必须以下面这种方式进行调用:


current_trans.total_amount =

compute_total_amount( current_trans );


This version is better than returning the entire structure, but the technique only works when a single value is to be computed. If we wanted the function to modify two or more members of the structure, this approach fails. Besides, there is still the overhead of passing the structure as an argument. Worse, it requires that the calling program have knowledge of the contents of the structure, specifically, the name of the total field.

这个方案比返回整个结构的那个方案强,但这个技巧只适用于计算单个值的情况。如果我们要求函数修改结构的两个或更多成员,这种方法就无能为力了。另外,它仍然存在把整个结构作为参数进行传递这个开销。更糟的是,它要求调用程序知道结构的内容,尤其是总金额字段的名字。


The third approach, passing a pointer, is better:

第3 种方法是传递一个指针,这个方案显然要好得多:


void

compute_total_amount( register Transaction *trans )

{

trans->total_amount =

trans->quantity * trans->unit_price;

}


This function is called like this:

这个函数按照下面的方式进行调用:


compute_total_amount( &current_trans );


Now, the total_amount field in the callerʹs structure is modified directly; there is no need to pass the entire structure into the function or to copy the modified structure as the return value. This version is more efficient than either of the other two functions.In addition, the caller no longer needs to know about the internals of the structure, so modularity is also improved.

现在,调用程序的结构的字段total_amount 被直接修改,它并不需要把整个结构作为参数传递给函数,也不需要把整个修改过的结构作为返回值返回。这个版本比前两个版本效率高得多。另外,调用程序无需知道结构的内容,所以也提高了程序的模块化程度。


When should you pass a structure, rather than a pointer, as an argument to a function? Rarely. Only when a structure is extremely small (the size of a pointer, or smaller) is it as efficient to pass the structure as it is to pass a pointer to it. For most structures, it is more efficient to pass a pointer. If you want the function to be able to modify any of the structureʹs members, a pointer is also preferred.

什么时候你应该向函数传递一个结构而不是一个指向结构的指针呢?很少有这种情况。只有当一个结构特别的小(长度和指针相同或更小)时,结构传递方案的效率才不会输给指针传递方案。但对于绝大多数结构,传递指针显然效率更高。如果你希望函数修改结构的任何成员,也应该使用指针传递方案。


With very early K&R C compilers, you couldnʹt pass structures as arguments—the compiler simply did not allow it. Later K&R compilers did allow structure arguments.However, these compilers did not support const, so the only way to prevent a function from modifying a structure argument was to pass a copy of the structure.

在非常早期的K&R C 编译器中,你无法把结构作为参数传递给函数——编译器就是不允许这样做。后期的K&RC 编译器允许传递结构参数。但是,这些编译器都不支持const ,所以防止程序修改结构参数的唯一办法就是向函数传递一份结构的拷贝。

上一章 Pointers on C——10 Structures and Unions.11

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值