使用函数参数作为输入和输出

时间:2015-08-18 15:33:43

标签: c etiquette

我发现自己使用输入和输出的函数参数,并想知道我做的事情是否会在以后咬我。

在此示例中,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 };
}

2 个答案:

答案 0 :(得分:3)

对于被调用函数没有返回值的问题的原始版本,你得到了三个类似的答案,大致上都说"是的,但是......":

Jonathan Leffler said

  

它"好的"只要记录在案,但实际上并不是首选的操作方式。为什么不让函数返回使用的长度,并将缓冲区长度参数保留为常规uint32_t(或者size_t,这样您可以将sizeof(buffer)传递给函数?

DevSolar said

  

语法上可以,但我个人更喜欢返回代码。如果那是不可能的,我会想要一个专用的输出参数,特别是如果它的传递参考。 (我可能不会注意并继续假设我的buffer_len仍然保持原始值。)

BeyelerStudios said

  

只要你没有用完你的返回值,我更喜欢收到结果,而不是每次使用函数时都必须定义一个变量(或者我想输入一个表达式)。

一致意见非常明显。

然后更新了问题,表明函数的返回值不是返回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 ..)差异等等。

因此,对于那些令我头痛的事情,我确实倾向于避免作为一种模式,但正如我所说的没有特别错误。