我发现自己使用输入和输出的函数参数,并想知道我做的事情是否会在以后咬我。
在此示例中,buffer_len
是一个参数。 foo
使用它来确定缓冲区的大小,并告诉调用者main
,缓冲区已用完了多少。
#define MAX_BUFFER_LENGTH 16
char buffer[MAX_BUFFER_LENGTH] = {0};
void main(void)
{
uint32_t buffer_len = MAX_BUFFER_LENGTH;
printf("BEFORE: Max buffer length = %u", buffer_len);
foo(buffer, &buffer_len);
printf("BEFORE: Buffer length used = %u", buffer_len);
}
void foo(char *buffer, uint32_t *buffer_len)
{
/* Remember max buffer length */
uint32_t buffer_len_max = *buffer_len;
uint32_t buffer_len_left = buffer_len_max;
/* Add things to the buffer, decreasing the buffer_len_left
in the process */
...
/* Return the length of the buffer used up to the caller */
*buffer_len = buffer_len_max - buffer_len_left;
}
这是一件好事吗?
编辑:
感谢您的回复,但我更倾向于为实际功能结果保留foo
的返回值(这对于更大的功能更有意义)。从长远来看,这样的事情会更加无痛吗?
typedef struct
{
char *data_ptr;
uint32_t length_used;
uint32_t length_max;
} buffer_t;
#define ACTUAL_BUFFER_LENGTH 16
char actual_buffer[ACTUAL_BUFFER_LENGTH] = {0};
void main(void)
{
buffer_t my_buffer = { .data_ptr = &actual_buffer[0],
.length_used = 0,
.length_max = ACTUAL_BUFFER_LENGTH };
}
答案 0 :(得分:3)
对于被调用函数没有返回值的问题的原始版本,你得到了三个类似的答案,大致上都说"是的,但是......":
它"好的"只要记录在案,但实际上并不是首选的操作方式。为什么不让函数返回使用的长度,并将缓冲区长度参数保留为常规
uint32_t
(或者size_t
,这样您可以将sizeof(buffer)
传递给函数?
语法上可以,但我个人更喜欢返回代码。如果那是不可能的,我会想要一个专用的输出参数,特别是如果它的传递参考。 (我可能不会注意并继续假设我的buffer_len仍然保持原始值。)
只要你没有用完你的返回值,我更喜欢收到结果,而不是每次使用函数时都必须定义一个变量(或者我想输入一个表达式)。
一致意见非常明显。
然后更新了问题,表明函数的返回值不是返回void
,而是用于其他目的。这完全改变了评估。
如果你的真实函数要返回一个值,则不要显示
void
函数。这样做完全改变了答案。如果你需要返回多个值,那么输入输出参数就可以了(甚至是必要的 -getchar()
是一个反例),尽管纯参数和单独的纯输出参数可能更好。使用结构也可以。
也许我应该解释一下反例'一点点。 getchar()
函数返回一个指示失败或char
值的值。这会给初学者带来许多陷阱(因为getchar()
会返回int
,而不是名称所暗示的char
。如果函数是:
bool get_char(char *c);
如果它读取字符则返回true
,如果失败则返回false
,并将字符值分配给c
。它可以像:
char c;
while (get_char(&c))
…use character just read…
这是函数需要返回两个值的情况。
回到问题中建议的修订代码,使用结构的代码。
这根本不是一个坏主意;将一组值打包到一个结构中通常是明智的。如果被调用的函数必须计算它将返回的某个值,那么它会很有意义,但它也会修改缓冲区数组并需要报告其中的条目数(以及知道它有多少空间)使用)。在这里,保持'空间可用'与使用的空间分开'绝对是可取的;更容易看到发生了什么比进入'进入'参数,通知函数入口处有多少空间,并报告退出时使用了多少空间。即使它报告退出时仍有多少空间,也很难使用。
返回到原始诊断:是的,输入输出参数在技术上是合法的,可以使其起作用,但不像单独的值一样易于使用。
旁注:void main(void)
不是撰写main()
的标准方式 - 请参阅What should main()
return in C and C++?了解完整故事。
答案 1 :(得分:1)
使用相同的缓冲区进行输入和输出没有任何问题,但它可能会限制其他地方的功能实用程序。例如,如果您想将它与两个不同的值一起使用,该怎么办? (出于某种原因,作为功能的用户,我需要原始保存)。在你提供的例子中,在函数中获取两个参数然后只传递两次相同的指针是没有害处的。然后你已经完成了两个用途,它可能简化了功能代码。
对于像数组这样的更复杂的数据类型,以及上面的相同问题,你需要确保你的函数不需要更大的输出,或者它是否缩小你memset的缓冲区(0 ..)差异等等。
因此,对于那些令我头痛的事情,我确实倾向于避免作为一种模式,但正如我所说的没有特别错误。