【问题标题】:Returning an array element from an array created within the function in C?从 C 函数中创建的数组返回数组元素?
【发布时间】:2016-02-07 22:25:32
【问题描述】:

这是在 C 中正确的做法吗?

char *func1() {
    char *str[3] = { "so", "is", "amazing" }; 

    return str[1]; 
}

char *func1() 返回一个指针,指向内存中存储字符串的位置。但是,当func1() 返回时,str 数组不会超出范围,我是否没有返回指向不存在的对象的指针?一个悬空指针(这个名字对吗?)

所以我有两个解决方案使str 成为全局的,这样它就永远不会超出范围,并且指针总是指向一个有效的内存地址,但这看起来很脏。

其他解决方案

char *func2() {
    char *str[3] = { "so", "is", "amazing" };
    int str1Len = strlen(str[1]); 
    char *ret = (char *)malloc(str1Len) ; // create a block of mem to hold the str[1]. 
    strncpy(ret, str[1], str1Len);
    return ret; 
}

这是正确的吗?这样做func1()func2() 的正确方法是什么? 还是有更好的办法?

我使用func1() 已经有一段时间了,它没有给我带来任何问题,这是否意味着func1() 正在做正确的事情?和func2() 是不必要的?

【问题讨论】:

  • char * str[3] = { "so" , "is" ,"amazing" } ; 被声明为函数中,并在函数返回时不再存在。 (它是函数本地的,当函数返回时释放内存——因此str[1] 指向的内存不再存在)您必须将str 作为参数传递给函数,或者为strmalloc within 函数并返回一个指向新内存块的指针。
  • @DavidC.Rankin 是的,这也是我的想法,但是 C CPP 和那里的规则简直太疯狂了。看到这个stackoverflow.com/questions/349025/…
  • @DavidC.Rankin:str 数组本身具有自动存储功能,但它的元素是指向具有静态存储的字符串文字的指针。返回其中之一即可,指针按值返回。
  • @nnrales:恐怕 DavidC.Rankin 弄错了。您的示例很简单,如果您的实际代码使用指向字符串文字的指针数组,您可以从中返回元素。
  • 如果str 没有改变,则改为static const char * const str[] = ...。这同时符合数组和指向constchar []static 使其永久化,其余使其在启动时初始化。您当前的版本将为每次调用设置数组。

标签: c arrays pointers scope malloc


【解决方案1】:

在您的第一个函数中,返回str[1] 没有问题,因为它不是指向局部变量,而是指向字符串文字的指针。请注意,字符串文字应声明为const:

const char *func1(void) {
    const char *str[3] = { "so", "is", "amazing" };
    return str[1]; 
}

您的第二个函数返回一个指向已分配空间的指针。这个指针需要在某个时候被释放。分配不正确,您应该为最终的'\0' 分配额外的 1 个字节。您可以使用strcpy 将字符串复制到分配的空间,或者简单地使用strdup() 两者兼而有之:

char *func2(void) {
    const char *str[3] = { "so", "is", "amazing" };
    int size = strlen(str[1]) + 1; 
    char *ret = malloc(size);
    return strcpy(ret, str[1]);
}

或者简单地说:

char *func2(void) {
    const char *str[3] = { "so", "is", "amazing" };
    return strdup(str[1]);
}

永远不要使用strncpy,它不会像你认为的那样做。对于程序员和阅读您的代码的人来说,这很容易出错。

【讨论】:

  • 抱歉这个愚蠢的问题,但字符串文字不是局部变量吗?
  • @nnrales 有static storage duration,如果是局部变量也没关系。如果有的话,这是一个聪明的问题。
  • @iharob 感谢静态存储持续时间页面。我对此一无所知。
【解决方案2】:
  1. 这是在 C 中正确的做法吗?

    不,不是!通常,您需要创建局部变量的副本,尽管在这种情况下,指针指向字符串文字,因此它可以工作。

  2. 你分配和复制字符串错误,你应该这样做

    size_t size =  strlen(str[1]) + 1; 
    char *result = malloc(size) ; // create a block of mem to hold the str[1]. 
    if (result != NULL)
        memcpy(ret, str[1], size);
    

【讨论】:

  • str[1] 不是指向局部变量,而是指向字符串文字的指针。退回它没有问题,除了可能是 constness。
  • 为什么你更喜欢 memcpy 而不是 strncpy ?所以如果我编写一个使用 func1() 的程序就可以了吗?为什么会这样?我虽然当一个函数返回堆栈上创建的每个对象时,技术上都会取消分配?
  • @chqrlie 我说过,我说一般来说这不是一个好主意,但因为它们是字符串文字。
  • strncpy() 不是为了这个,它会在到达length 之前尝试检测字符串的结尾(它最多复制length 字节), memcpy() 将复制您告诉它复制的确切字节数。由于您使用了strlen(),那么'\0' 最好位于源字符串中,否则将出现未定义的行为。所以你确定它在那里然后复制length字节就可以了。但是,在某些情况下,它不会复制 '\0' 终止符,因此作为一般规则不要使用 strncpy()
  • strncpy 在任何地方都是不可取的。
【解决方案3】:

虽然返回指向字符串文字的指针是可以接受的,但不是返回本地指针,但这可能是错误的方法。

如果str 没有改变,则设置为static const char * const str[] = ....,它同时符合数组和char [](每个字符串字面量)都指向const。 static 使其成为永久性的,但仍具有本地范围。它在您的程序代码开始之前设置一次。相反,非static 版本将为每次调用设置数组。

请注意,您必须返回 const char * 以保持 const 正确性。 我强烈建议这样做;如果不能,请仅省略第一个 const

注意:不要将malloc和朋友(或一般void *)的结果转换为C。

【讨论】:

  • 谢谢,我实现了你所说的,但是将它作为一个字符返回 * 我不得不放弃 const。我的教授告诉我始终将 malloc 和 void * 的结果转换为我知道的类型。你为什么说其他明智?
  • @nnrales:抱歉,您的教授大错特错!他可能想阅读 C standard。此外,这里已经有一个关于该主题的主题,请参阅信息页面以获取更多信息。也许他应该学习 C 不是 C++(在 C++ 中你必须学习)并且不应该使用 C++ 编译器来编译 C 代码。强制转换禁止类型检查,因为它们告诉你的编译器“闭嘴”而不是警告你。只有在 1) 必须 2) 真正理解 所有 含义并且 3) 完全接受它们时才使用演员表。
  • @nnrales:关于 const-correctness:我强烈建议保留它,并且 - 正如我所写的那样 - 更正其他代码。 const-correctness 是使您的代码更安全并帮助您的编译器告诉您代码是否有问题的重要措施。
  • 感谢您的提示。我的数组非常大,每次调用函数时都重新分配它,会很昂贵。
  • 对于大型数组,最好将其设为文件本地。只需将其按原样移出函数即可(保留static)。这是为了提高可读性并保持函数源代码占用空间小。
猜你喜欢
  • 2016-03-26
  • 2011-04-08
  • 2012-02-11
  • 2021-09-27
  • 2023-03-15
  • 2015-02-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多