返回字符串C的函数

时间:2018-12-06 18:18:01

标签: c arrays string function return-type

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

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;
}

5 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:1)

尽管您无法在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的解决方案。一种可能效率不高的方法是,如果您需要写入特定的变量或位置。在这种情况下,您可以传递其地址,但是在这里,您需要创建一个大型的临时数组并将其复制。

答案 2 :(得分:0)

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

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语言以及计算机内存如何工作的不错的书。

答案 3 :(得分:0)

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

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

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

答案 4 :(得分:-3)

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