为什么这么多标准 C 函数篡改参数而不是返回值?

Why do so many standard C functions tamper with parameters instead of returning values?

许多函数,如 strcat、strcpy 等,并不 return 实际值,而是更改其中一个参数(通常是缓冲区)。这当然会产生大量副作用。 只 return 一个新字符串不是更优雅吗?为什么不这样做?

示例:

char *copy_string(char *text, size_t length) {

    char *result = malloc(sizeof(char) * length);

    for (int i = 0; i < length; ++i) {
        result[i] = text[i];
    }

    return result;
}

int main() {
    char *copy = copy_string("Hello World", 12);
    // *result now lingers in memory and can not be freed?
}

我只能猜测它与内存泄漏有关,因为在函数内部分配了动态内存,您无法在内部释放它(因为您需要 return 指向它的指针)。

编辑:从答案看来,在 C 中使用参数而不是创建新变量是一种很好的做法。所以我应该以这样的方式构建我的功能?

编辑 2:我的示例代码会导致内存泄漏吗?或者*结果可以免费吗?

两个原因:

  • 正确设计的函数应该只关注它们指定的目的,而不是无关的事情,比如内存分配。
  • 制作字符串的硬拷贝会使函数变慢。

因此对于您的示例,如果需要硬拷贝,调用者应该 malloc 缓冲区然后调用 strcpy。将内存分配与算法分开。

最重要的是,良好的设计实践要求分配内存的模块也应该负责释放它。否则调用者可能甚至没有意识到该函数正在分配内存,并且会发生内存泄漏。如果调用者负责分配,那么很明显调用者也负责清理。


总体而言,C 标准库函数被设计为尽可能快,这意味着它们将努力满足调用者具有最低要求的情况。这种函数的典型示例是 malloc,它甚至不将分配的数据设置为零,因为这会花费额外的时间。相反,他们为此添加了一个附加功能 calloc

其他语言有不同的哲学,例如,他们会强制所有字符串处理函数的硬拷贝 ("immutable objects")。这使得函数更易于使用,也许代码也更易于阅读,但它的代价是程序速度较慢,需要更多内存。

这是C语言仍然被广泛用于开发的主要原因之一。它往往比任何其他语言(原始汇编语言除外)都更快、更高效。

Why do so many standard C functions tamper with parameters instead of returning values?

因为这通常是 C 库的用户想要的。

Many functions like strcat, strcpy and alike don't return the actual value but change one of the parameters (usually a buffer). This of course creates a boatload of side effects. Wouldn't it be far more elegant to just return a new string? Why isn't this done?

分配内存的效率不是很高,并且需要用户稍后释放()它们,这对用户来说是不必要的负担。效率和让用户做他们想做的事(即使他们想搬起石头砸自己的脚)是 C 哲学的一部分。 此外,还有syntax/implementation个问题。例如,如果 strcpy() 函数实际上是 returns 一个新分配的字符串,那么如何执行以下操作?

char arr[256] = "Hello";
strcpy(arr, "world");

因为 C 不允许您为数组赋值 (arr)。

基本上,你是在质疑C是这样的。对于这个问题,常见的答案是 "historical reasons".

回答您最初的问题:C 在设计时被定制为一种效率最高的语言。基本上,它只是一种更好的编写汇编代码的方法(设计它的人为此编写了自己的编译器)。

您所说的(经常使用参数而不是 return 代码)主要适用于字符串处理。大多数其他函数(例如那些处理数字的函数)按预期通过 return 代码工作。或者,如果他们必须 return 多个值,他们只会修改参数值。

今天 C 中的字符串处理被认为是 C 中的主要(如果不是主要的)弱点之一。但是这些函数是在考虑性能的情况下编写的,并且当时的机器可用(以及性能的意图)在调用者缓冲区上工作是选择的方式。

重新编辑 1:今天其他意图可能适用。性能通常不是限制因素。同样或重要的是可读性、健壮性、容易出错。通常,如前所述,如今 C 中的字符串处理通常被认为是过去的可怕遗迹。所以这基本上是你的选择,取决于你的意图。

回复您的编辑 2:是的,内存会泄漏。您需要调用 free(copy); 这与编辑 1 相关:容易出错 - 很容易忘记 free 并以这种方式造成泄漏(或尝试释放它两次或访问它释放后)。它可能更具可读性,也更容易出错(甚至比修改调用者缓冲区的笨拙的原始 C 方法更容易出错)。

一般来说,只要您有选择,我建议您使用支持 std-string 或类似语言的较新方言。