繁体   English   中英

返回字符串C的函数

[英]Function that returns an array of strings C

有没有一种方法可以在不使用动态内存分配的情况下从函数返回字符串数组? 该函数是这样的:

char** modify(char original[1000][1000]){ 
    char result[1000][1000];
    // some operations are applied to the original
    // the original is copied to the result
    return result;
}

在C中,对象具有四个存储期限(也称为生存期)之一:静态,线程,自动和已分配(C 2018 6.2.4 1)。

具有自动持续时间的对象会在函数内部自动创建,并且在函数执行结束时将不复存在,因此您不能使用在函数内部创建的对象返回值。

分配了存储时间的对象将一直保留到释放为止,但是您已要求排除这些对象。

线程存储持续时间可能不适用于您的情况,或者实际上等效于静态存储持续时间,我将在下面讨论。

这意味着您可以选择:

  • 让调用者向您传递一个要在其中返回数据的对象。 该对象可以具有任何存储持续时间-您的函数不需要知道,因为它既不会分配也不会释放它。 如果这样做,则调用者必须提供一个足够大的对象以返回数据。 如果事先不知道此大小,则可以提供一个单独的函数来计算它(调用者随后将使用该函数来分配必要的空间),也可以将其作为特殊模式合并到您的函数中,在该模式下,它可以提供所需的大小而无需提供数据呢。
  • 使用具有静态存储持续时间的对象。 由于此对象是在程序启动时创建的,因此无法在函数中调整大小。 您必须在程序中建立大小限制。 这种方法的一个很大的问题是该函数只有一个对象要返回,因此一次只能使用一个对象。 这意味着,一旦调用了函数,则在调用者完成使用对象中的数据之前,不应再次调用它。 这既是程序设计中的严重限制,也是漏洞的机会,因此很少使用。

因此,典型的解决方案如下所示:

size_t HowMuchSpaceIsNeeded(char original[1000][1000])
{
    … Calculate size.
    return SizeNeeded;
}

void modify(char destination[][1000], char original[1000][1000])
{
    … Put results in destination.
}

安全性的一种变化是:

void modify(char destination[][1000], size_t size, char original[1000][1000])
{
    if (size < amount needed)
        … Report error (possibly by return value, or program abort).
    … Put results in destination.
}

然后,调用者将执行以下操作:

size_t size = HowMuchSpaceIsNeeded(original);
char (*results)[1000] = malloc(size);
if (!results)
    … Report error.
modify(results, size, original)
… Work with results.
free(results);

Davistor 注意到 ,函数可以返回嵌入在结构的阵列。 就C语义而言,这通过返回值而不是对象避免了对象生存期问题。 (该结构的全部内容就是该结构的值。)就实际的硬件实现而言,它在很大程度上等同于上面的调用者通过对象方法。 (这里的推理是基于计算机工作原理的逻辑,而不是基于C规范:为了使函数返回需要大量空间来表示的值,调用者必须向被调用函数提供所需的空间。 )通常,调用方将在堆栈上分配空间并将其提供给被调用的函数。 这可能比malloc快,但它也可能占用大量堆栈空间。 通常,我们避免使用大量堆栈空间,以避免堆栈溢出。

尽管您不能在C语言中返回数组类型,但是可以返回包含一个数组的struct

#include <string.h>

#define NSTRINGS 100
#define STR_LEN 100

typedef struct stringtable {
  char table[NSTRINGS][STR_LEN];
} stringtable;

stringtable modify ( const stringtable* const input )
{
  stringtable result;

  memcpy( &result, input, sizeof(result) );

  return result;
}

我通常建议您使用Eric Postpischil的解决方案。 一种可能效率不高的方法是,如果您需要写入特定的变量或位置。 在这种情况下,您可以传递其地址,但是在这里,您需要创建一个大型的临时数组并将其复制。

您不能返回指向局部变量的指针,因为它们所指向的内存的生存期仅限于范围。

基本上, result是指向堆栈分配的数组第一个元素的指针,因此,将其返回并稍后对其取消引用将导致未定义的行为。

要绕过此问题,很少有解决方法。

我在几个项目中看到了其中一个,但是我不建议这样做,因为它不安全。

char** modify(char original[1000][1000]){ 
    // `result` is static array, which lifetime is equal to the lifetime of the program
    // Calling modify more than one time will result in overwriting of the `result`.
    static char result[1000][1000]; 
    return result;
}

另一种方法是将result指针作为函数参数接收,因此调用方将为其分配存储空间。

void modify(char original[1000][1000], char (*result)[1000]){ 
    result[0][1] = 42; 
    //...
}
void main() {
    char result[1000][1000];
    modify(someOriginal, result);
}

无论如何,我建议您阅读一些有关C语言以及计算机内存如何工作的体面的书。

如果没有动态分配,则不能返回指向函数内部分配的内存的指针。 在您的情况下,您将在堆栈中的区域中分配result[1000][1000] ,一旦函数返回,该区域将被释放。 除了动态分配,您还可以选择将缓冲区作为参数传递给函数:

void modify(char original[1000][1000], char result[][]) { ... }

现在,必须将result矩阵分配到modify函数之外,并且其生存期将不取决于函数的生存期。 基本上,您向函数传递一个已经分配的矩阵,该矩阵将在其中写入结果。

您可以使用从第一个字符串开始到最后一个字符串结束的链表。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM