返回一个字符指针数组

4

我正在尝试将一个char*数组返回给一个函数。为了简化代码,我创建了一个测试用例来克隆一个char数组,但是这个数组不是包含字符,而是指向这些字符的指针。

/*
 * code.c
 */
#include <stdio.h>

char* makePointerCopy(char cIn[]);

int main() {
    char cTest[] = {'c', 't', 's', 't'};
    char* cPTest[] = makePointerCopy(cTest);
    printf("%p %c", cPTest, *cPTest);
    fflush(stdout);
    return 0;
}

char* makePointerCopy(char cIn[]) {
    char* cOut[sizeof(cIn)/sizeof(cIn[0])];
    int iCntr;

    for (iCntr = 0; iCntr < sizeof(cIn)/sizeof(cIn[0]); iCntr++)
        cOut[iCntr] = cIn + iCntr;

    return cOut;
}

除了一些警告之外,这就是编译器对这段代码片段的反馈:

无效的初始化 (位于 char* cPTest[] = makePointerCopy(cTest);)

为什么会这样呢?


1
这段代码还有另一个重要问题:char* cOut[sizeof(cIn)/sizeof(cIn[0])]; 是行不通的。在参数列表中使用简单的 char cIn[] 不会包含数组的大小信息。你需要像 template<size_t N> ... (char (&cIn)[N]) 这样的东西,然后 N 将匹配元素的数量。另一种选择是使用 char* start, char* end 来传递数组。 - gimpf
把这段代码放在 makePointerCopy 调用之前的 main() 块中,只要我将 iSize 传递给函数,这样做也可以解决问题吗? int iSize = sizeof(cTest)/sizeof(cTest[0]); - Pieter
8个回答

6
因为makePointerCopy返回的是char*而不是char*[]
你可以将那一行改为:
char* cPTest = makePointerCopy(cTest);

更具体地说,你收到那个错误消息的原因是数组初始化器需要是编译时常量。引用自http://bytes.com/topic/c/answers/215573-invalid-initializer 。即使声明不在文件作用域内,在C90和C99中都是非法的。C90要求自动和寄存器数组使用编译时常量初始化器。C90和C99都要求字符数组用a)字符串文字或b)括号包围的初始化器列表来初始化。仍然,类型不匹配才是实际的问题。

3
你需要返回一个 char ** 或者 char *[]
具体来说,如果你想让 makePointerCopy 返回“一个 char* 数组”,那么你需要确实返回这样的数组。目前,你正在返回指向 char 的指针,或者说 "char*"。
问题所在的代码行试图将 makePointerCopy 的结果赋值给一个 char*[]。虽然在 C 语言中这是技术上可行的,编译器仍然会生成结果,但编译器基本上是在告诉你它生成的结果可能不会按照你的期望执行。

3

在这行代码中,你试图将一个char*赋值给一个char*数组。简单地说,它们是不同的类型。


那我需要改变返回类型吗?我该改成什么?我尝试过 char*[] 但是会产生很多错误和警告。 - Pieter
Pieter - 看看我的答案,了解需要更改的内容示例。 - danben
char** 应该可以,如果我没记错的话。也把 cPTest 的类型改成 char**。 - Aaron

2

让我们从警告开始。你声明了:

char* cPTest[]

英文原文:“cPTest 是指向字符指针的数组

以及

char* makePointerCopy(char cIn[]);

英文原文:“makePointerCopy() takes an array of chars and returns a pointer to char

所以您正在尝试将“指向字符的指针”赋值给“指向字符指针的数组”。您能看到问题吗?我建议在进行赋值之前仔细检查类型。

话虽如此,您真正想要声明makePointerCopy()返回一个“指向指向字符的指针”:

char **makePointerCopy(char cIn[]);

最终,你将返回指向返回数组第一个元素的指针。

另一个重要的点:你将"cOut"声明为函数的局部变量。

char* makePointerCopy(char cIn[]) {
    char* cOut[sizeof(cIn)/sizeof(cIn[0])];

    ...  /* cOut can ONLY be used within the function! */

    return cOut;  // <-- The address returned point to a 
                  //     block of memory that is no longer valid
                  //     after the end of the function
}

请记住,一旦函数终止,本地变量将自动失效。为了“保留”它,您可以声明它为static

char* makePointerCopy(char cIn[]) {
    static char* cOut[sizeof(cIn)/sizeof(cIn[0])];

    ...  /* cOut will survive the end of the function */

    return cOut;  // <-- The address can be returned 
}

请注意,当返回此类型的值时,必须要有良好的纪律。
作为替代方案,您可以使用 malloc() 分配所需的空间,只要在不再需要它时记得使用 free() 释放。

我想我开始明白了。至于局部变量:cOut指向一个没有在makePointerCopy内部创建的变量,因此我应该是安全的,对吧?无论如何,有没有办法防止C擦除特定的局部变量? - Pieter
你不安全,因为cOut *在函数内部声明并且在返回后将无效。你可以将其声明为“静态”,以允许返回其地址。我将编辑答案以显示这一点。 - Remo.D

2
因为你的函数返回的是char*,而你正在将其赋值给char*[]。C语言可能有一个相对较弱的类型系统,但有些事情不应该这样做:-)

2

其他回答都是正确的,但你的代码似乎还存在许多其他问题:

char* cOut[sizeof(cIn)/sizeof(cIn[0])];

我相信您认为sizeof(cIn)返回cIn数组中元素所占用的内存量。这是不正确的。在这种情况下,sizeof(cIn)将返回指针在您的系统上的大小,通常为4或8个字节。sizeof(cIn[0])将返回字符的大小,即1个字节。通常无法发现C中数组的大小,因此恐怕您必须将该大小传递给函数。

还要注意,makePointerCopy返回一个指向静态分配的内存块的指针。这个内存基本上是makePointerCopy的本地变量,并且将在makePointerCopy完成其工作时释放。换句话说,makePointerCopy将返回指向无效内存的指针。


0
该函数返回的是 char* 而不是 char[]*。

0

char * 是指向单个字符的指针,而你需要 char **,它是指向数组或 char * 的指针。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接