【问题标题】:Allocating memory for strings dynamically为字符串动态分配内存
【发布时间】:2018-06-16 00:06:55
【问题描述】:
char* test() {
    char* returnValue = "test";
    return returnValue;
}  
char* test=test();
printf("%s",test);

使用安全吗?是一样的吗,和

char* test {
    char* returnValue=strdup("test");
    return returnValue;
}
char* test=test();
printf("%s",test);

如果是,那么我应该稍后释放它吗?它们似乎都可以正常工作。

【问题讨论】:

  • 每次调用malloc(strdup 使用malloc)都希望调用free,是的,第一个sn-p 是安全的,但test 是不可修改的。
  • 为什么不能修改?如果我可以在函数之外访问这个变量,那么这意味着它位于堆上吗?否则,它会在函数完成后立即销毁?
  • 因为字符串字面量通常存储在只读区域 (.rodata),即使我们退出函数,这些区域仍然存在。
  • 即使您能够修改第一个示例中的结果字符串,该更改也将是永久性的,随后对test() 的调用将返回相同更改字符串的地址。这绝对不是你想要的。

标签: c string dynamic-memory-allocation literals c-strings


【解决方案1】:

是一样的

不,不是。


char * test1() {
  char * returnValue = "test";
  return returnValue;
}  

以上代码将固定地址返回到常量字面量"test"。这将是每次调用该函数时相同的地址。

它不是内存的动态分配。

在做

printf("%d\n", test1() == test1());

将打印

1

意思是“真”,返回的两个地址是一样的。

关于“常量”

为了更好地体现test1()结果的常量性,最好定义如下:

const char * test1() {
  const char * returnValue = "test";
  return returnValue;
}  

char * test2 {
  char * returnValue = strdup("test");
  return returnValue;
}

以上代码将地址返回到已复制"test" 的新分配内存区域。每次调用函数时,这将是一个不同的*1地址。

*1:至少“不同”,只要之前对 test2() 的任何调用的结果不是 free() 已经发布了

这是内存的动态分配。因此,如果不再需要,它需要调用free() 并传入strdup()(内部调用malloc())返回的地址以释放内存。

在做

printf("%d\n", test2() == test2()); /* leaks memory: 2 times 4+1 char */

将打印

0

意思是“假”,返回的两个地址不同。

为了完整性:为了避免按照上面的 sn-p 做的泄漏

char * p, * q;
printf("%d\n", (p = test2()) == (q = test2()));
free(p);
free(q);

使用安全吗

形式上两个sn-ps的代码都是正确的。

使用哪一个以及是否使用“安全”完全取决于用例,取决于上下文。

【讨论】:

    【解决方案2】:
    char* test() {
        char* returnValue = "test";
        return returnValue;
    }  
    

    使用安全吗?

    可以,只要您不尝试修改returnValue,它是一个字符串文字。字符串字面量具有静态存储持续时间,因此它们在程序的整个生命周期内都是有效的,但尝试修改字符串字面量的内容是未定义的行为。

    是一样的吗

    char* test {
        char* returnValue=strdup("test");
        return returnValue;
    }
    

    答案是 - 否

    strdup()

    Returns a pointer to a null-terminated byte string, which is a duplicate of the string pointed to by str1. The returned pointer must be passed to free to avoid a memory leak.

    strdup() 使用malloc() 为新字符串获取内存,这里新字符串为"test"。 它一直处于分配状态,直到它被显式释放或直到程序结束。所以,一旦你完成它,应该使用free() 释放它。此外,您可以修改strdup() 返回的字符串内容,但请确保不要访问超出分配的内存块。

    【讨论】:

      【解决方案3】:

      使用安全吗?

      是的,除非您尝试更改字符串。实际上没有分配,所以每次你的函数都会返回完全相同的指针指向内存中的相同位置。

      是一样的吗

      不,strdup() 进行分配并返回新分配的内存。

      如果是,那么我应该稍后释放它吗?

      不是,但你还是需要稍后在strdup()之后释放内存。

      它们似乎都可以正常工作

      对于printf() 很好,除非您尝试更改这些字符串...您将无法更改char* returnValue = "test" 字符串,但您将能够更改strdup() 之后的字符串

      【讨论】:

      • @KeineLust 你是对的,除非你尝试更改字符串,否则它是安全的......
      【解决方案4】:

      在这两种情况下,"test" 在 堆栈 中分配为内存的只读部分 (const char)。

      在第一个块中,您返回一个指向"test" 的指针。如前所述,在对函数的调用中,这将始终是相同的值。由于其只读属性,尝试释放或修改它会引发执行错误(munmap_chunk(): invalid pointer 尝试释放时,分段错误 尝试修改时)。

      在第二个块中,您返回一个指针,指向 堆 中动态分配的内存部分。您有责任使用free() 或等效方法释放这部分内存。你可以随意修改这个变量,甚至重新分配这部分内存。

      您可以随时进行自己的测试:

      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      
      char* test1() {
          char* returnValue = "test";
          return returnValue;
      }  
      char* test2() {
          char* returnValue = strdup("test");
          return returnValue;
      }
      
      int main(void)
      {
         char* vtest1 = test1();
         printf("%s => %p\n", vtest1, &vtest1);
         char* vtest2 = test2();
         printf("%s => %p\n", vtest2, &vtest2);
      
         printf("Freeing 2nd test...\n");
         free(vtest2);
      
         printf("Trying to modify 1st test...\n");
         vtest1[0] = 'p';
         printf("Freeing 1st test...\n");
         free(vtest1);
      
         return 0;
      }
      

      【讨论】:

      • 作为内存的只读部分(const char),这令人困惑,因为它们都不是const
      • "test" 隐含为const char。代码中定义的所有字符串(使用引号)都是const char。
      • "在这两种情况下,"test" 都分配在堆栈中......" C 甚至不知道“堆栈”。文字的去向完全由实现定义。
      • @emi: nops,它们的类型为char *(但不可修改):stackoverflow.com/questions/4493139/are-string-literals-const
      • @KeineLust:不,是returnValue,也就是char *。在"test" 的情况下是文字,它们的类型是const char *。
      猜你喜欢
      • 2020-11-28
      • 1970-01-01
      • 2017-03-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-29
      • 2013-06-07
      相关资源
      最近更新 更多