C 子例程参数数量的最佳实践

发布于 2024-09-08 14:45:25 字数 105 浏览 5 评论 0原文

我最近开始开发用 C 编写的 API。我看到一些子例程需要 8 个(八)个参数,对我来说,在调用该特定子例程时传递 8 个参数看起来很丑陋且麻烦。我想知道是否可以实施一些更容易接受和更清洁的方法。

I've recently started working on developing APIs written in C. I see some subroutines which expect 8(Eight) parameters and to me it looks ugly and cumbersome passing 8 parameters while calling that particular subroutine. I was wondering if something more acceptable and cleaner way could be implemented .

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

浅紫色的梦幻 2024-09-15 14:45:25

如果多个参数可以在逻辑上分组在一起,您可以考虑创建一个包含它们的结构,并简单地将该结构作为参数传递。例如,您可以传递一个 POINT 结构,而不是传递两个坐标值 xy

但是,如果这样的分组不适用,那么如果您确实需要它们,则任何数量的参数都应该没问题,尽管这可能表明您的函数做得有点太多,并且您可以将工作分散到更多但更小的函数上。

If a number of arguments can be logically grouped together you may consider creating a structure containing them and simply pass that structure as an argument. For example instead of passing two coordinate values x and y you could pass a POINT structure instead.

But if such a grouping isn't applicable, then any number of arguments should be fine if you really need them, although it might be a sign that your function does a little too much and that you could spread work over more, but smaller functions.

濫情▎り 2024-09-15 14:45:25

函数调用中的大量参数通常会显示出设计问题。有一些方法可以明显减少参数的数量,例如创建传递的结构而不是单个变量或具有全局变量。我建议您不要做任何这些事情,并关注设计。那里没有快速或简单的修复,但必须维护代码的人会为此感谢您。

Large numbers of arguments in a function call are usually showing up a design problem. There are ways of apparently reducing the number of parameters, by such means as creating structures which are passed instead of individual variables or having global variables. I'd recommend you DON'T do either of these things, and attend to the design. No quick or easy fix there, but the people who have to maintain the code will thank you for it.

心房的律动 2024-09-15 14:45:25

是的,8 几乎可以肯定太多了。

这里有一些老式的软件工程术语供您参考。内聚力和耦合度。 内聚是子例程自身结合在一起的程度,而耦合 是指例程之间的接口有多干净(或者例程的自给自足程度)。

对于联轴器,一般越松越好。仅通过参数进行接口(“数据耦合”)是良好的低耦合,而使用全局变量(“公共耦合”)是非常高的耦合。当你有大量参数时,通常的情况是有人试图用一层薄薄的数据耦合来隐藏它们的共同耦合。其糟糕的设计和油漆工作。

内聚力越高(内聚力越大)越好。任何修改八个不同事物的例程也很可能会遭受低内聚性的困扰。我必须查看代码才能确定,但​​我愿意打赌,很难用简短的句子清楚地解释该例程的作用。看不见的东西,我猜它在时间上是有凝聚力的(只是一堆需要大致在同一时间完成的事情)。

Yes, 8 is almost certianly too much.

Here's some old-school software engineering terms for you. Cohesion and coupling. Cohesion is how well a subroutine holds together on its own, and coupling is how clean the interfaces between your routines are (or how self-sufficient your routines are).

With coupling, generally the looser the better. Interfacing only through parameters ("data coupling") is good low coupling, while using global variables ("common coupling") is very high coupling. When you have a high number of parameters, what is usually the case is that someone has tried to hide their common coupling with a thin veneer of data coupling. Its bad design with a paint job.

With cohesion, the higher (more cohesive) the better. Any routine that modifies eight different things is also quite likey to suffer from low cohesion. I'd have to see the code to see for sure, but I'd be willing to bet that it would be very difficult to clearly explain what that routine does in a short sentence. Sight unseen, I'd guess it is temporally cohesive (just a bunch of stuff that needs to be done at roughly the same time).

徒留西风 2024-09-15 14:45:25

8可能是一个合适的数字。或者这 8 个中的许多人都应该属于一个适当的类作为成员,那么你可以传递该类的单个实例......仅通过这种高级讨论很难判断。

编辑:在 C 中,类与本例中的结构类似。

8 could be a proper number. or it could be that many of those 8 should all belong to a proper class as members, then you could pass a single instance of the class... hard to tell just by this kind of high level discussion.

edit: in c - classes would be similar to structures in this case.

傻比既视感 2024-09-15 14:45:25

我也建议使用结构。
也许您想重新考虑您的 API 设计。

请记住,您的 API 将由开发人员使用,并且很难使用 8 参数函数调用。

I suggest the usage of structures too.
Maybe you want to rethink your APIs design.

Remember that your APIs will be used by developers, and it would be hard to use an 8 parameter function call.

一腔孤↑勇 2024-09-15 14:45:25

某些 API(如 pthreads)使用的一种模式是“属性对象”,它被传递到函数中,而不是一堆离散的参数。这些属性对象是不透明的结构,具有创建、销毁、修改和查询它们的功能。总而言之,这比简单地将 10 个参数转储到函数需要更多的代码,但它是一种更健壮的方法。有时,一些额外的代码可以使您的代码更容易理解,这是值得付出努力的。

再次,有关此模式的一个很好的示例,请参阅 pthreads API。我写了 a几个月前,我写了一篇关于 pthreads API 设计的长文,这是我在其中讨论的方面之一。

One pattern used by some APIs (like pthreads) is "attribute objects" that are passed into functions instead of a bunch of discrete arguments. These attribute objects are opaque structures, with functions for creating, destroying, modifying and querying them. All in all this takes more code than simply dumping 10 arguments to a function, but it's a more robust approach. Sometimes a bit of extra code that can make your code much more understandable is worth the effort.

Once again, for a good example of this pattern, see the pthreads API. I wrote a lengthy article on the design of the pthreads API a couple of months ago, and this is one of the aspects I addressed in it.

面犯桃花 2024-09-15 14:45:25

不是从丑/不丑的角度而是从性能的角度来考虑这个问题似乎也很有趣。

我知道有一些 x86 调用约定 可以使用寄存器传递两个第一个参数和堆栈对于所有其他参数。因此,我认为,如果使用这种类型的调用约定,并且在函数总体需要超过 2 个参数的情况下始终使用指向结构的指针来传递参数,则函数调用可能会更快。在 Itanium 上,寄存器始终用于将参数传递给函数。

我认为这可能值得测试。

It also seems to be interesting to consider this question not from the standpoint of ugliness/not ugliness but from the point of performance.

I know that there are some x86 calling conventions that can use registers for passing two first arguments and stack for all other arguments. So I think that if one use this type of calling convention and always use a pointer to structure to pass arguments in situations when a function needs more then 2 parameters in overall the function call might be faster. On Itanium registers are always used for passing parameters to a function.

I think it might be worth testing.

旧瑾黎汐 2024-09-15 14:45:25

如果 API 对于这么多参数显得很麻烦,请使用指向结构的指针来传递以某种方式相关的参数。

在没有亲眼目睹的情况下,我不会评判你的设计。有些合法的函数需要大量参数,例如:

  1. 具有大量多项式系数的滤波器。
  2. 颜色空间使用子像素掩码和 LUT 索引进行变换。
  3. n 维不规则多边形的几何算术。

还有一些非常糟糕的设计导致参数原型很大。如果您寻求更多切题的回应,请分享有关您的设计的更多信息。

If the API seems cumbersome with that many parameters, use a pointer to a structure to pass parameters that are related in some way.

I'm not going to judge your design without seeing it firsthand. There are legitimate functions that require a large number of parameters, for example:

  1. Filters with a large number of polynomial coefficients.
  2. Color space transforms with sub-pixel masks and LUT indices.
  3. Geometric arithmetic for n-dimensional irregular polygons.

There are also some very poor designs that lead to large parameter prototypes. Share more information about your design if you seek more germane responses.

靑春怀旧 2024-09-15 14:45:25

您可以做的另一件事是将一些参数转换为状态。 OpenGL 函数的参数较少,因为您有如下调用:

glBindFramebuffer( .. ) ;
glVertexAttribPointer( .. ) ;
glBindTexture( .. ) ;
glBindBuffer( .. ) ;
glBindVertexArray( .. ) ;

// the actual draw call
glDrawArrays( .. ) ;

所有这些(glBind* 类型调用)都代表“更改状态”,所有这些都会影响下一次绘制调用。想象一下带有 20 个参数的绘制调用......绝对难以管理!

用于绘图的旧 Windows C api 也有状态,存储在“不透明指针”对象(HDC's、HWND's..)内。不透明指针基本上是 C 语言创建无法直接访问的私有数据成员的方法。例如,在 Windows 绘图 API 中,您可以通过 createDC。您可以通过以下方式设置 DC 的内部值
SetDC* 函数,例如 设置DCBrushColor

现在您已经为 DC 设置了颜色和所有内容,您可以使用 Rectangle 函数绘制到 DC 中。您传递了 HDC 作为第一个参数,其中包含有关使用哪种颜色画笔等的信息。Rectangle 然后只需要 5 个参数,即 hdc 、x、y、宽度和高度。

Another thing you can do is convert some of the parameters to state. OpenGL functions have fewer parameters because you have calls like:

glBindFramebuffer( .. ) ;
glVertexAttribPointer( .. ) ;
glBindTexture( .. ) ;
glBindBuffer( .. ) ;
glBindVertexArray( .. ) ;

// the actual draw call
glDrawArrays( .. ) ;

All of these (glBind* type calls) represent "changing state", all of which will affect the next draw call. Imagine a draw call with 20-something arguments.. absolutely unmanageable!

The old Windows C api for drawing also had state, stored inside "opaque pointer" objects (HDC's, HWND's..). An opaque pointer is basically C's way of making private data members that you can't directly access. So for example, in the Windows drawing API, you would create an HDC opaque pointer via createDC. You could set the DC's internal values via
the SetDC* functions, for example SetDCBrushColor.

Now that you have a DC set up with a color and everything, you could use the Rectangle function to draw into the DC. You passed an HDC as the first parameter, which contained information about what color brush to use etc. Rectangle then only takes 5 parameters, the hdc, x,y,width and height.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文