当我们用c中的字符串文字初始化一个char数组时,是否会发生垃圾收集?

当我们在C中编写以下代码行时,

char local_arr[] = "I am here"; 

文字“我在这里”被存储在内存的只读部分(比如RM )。 我如何想象它是如何在RM中连续存储的(是吗?)。 然后,数组local_arr (即本地数组)通过索引从RM中的位置复制此数组索引。

但是在local_array复制之后,文字会发生什么? 它是否丢失从而导致内存泄漏? 或者是否有类似Java的垃圾收集器来清理未引用的对象?

例如,如果我写一段代码如下:

 for(int i=0;i<100000;i++) char local[] = "I am wasting memory"; 

我不会用完记忆吗? 每次迭代都会在每次迭代时创建相同文字的新实例吗? 或者他们都会引用相同的文字,因为每次文字的价值是相同的?

RM是属于堆内存还是堆中的专用段?

本地数组也存储在堆栈中,对吧? 如果我使用动态数组或全局数组怎么办? 那么会发生什么?

C没有垃圾收集,因此如果您忘记使用正确的解除分配器释放已分配的内存,则会出现内存泄漏。
虽然有时使用像Boehm收集器这样的保守垃圾收集器 ,但这会导致许多额外的麻烦。

现在,C中有四种类型的内存:

  • 静态内存:从开始到结束都是有效的。 它具有逻辑上只读(写入是未定义的行为)和可写的风格。
  • 线程本地内存:类似于静态内存,但每个线程都不同。 这是一种新奇的东西,就像所有线程支持一样。
  • 自动记忆:堆叠中的所有东西。 离开块会自动释放它。
  • 动态内存:请求返回malloccallocrealloc等内容。 别忘了free 。 使用另一个适当的解除分配器。

您的示例使用local_arr自动内存,并使实现可以自由地将其初始化为提供的文字,无论哪种方式最有效。

 char local_arr[] = "I am here"; 

除其他外,这可能意味着:

  • 使用memcpy / strcpy并将文字放入静态内存。
  • 通过推送部件在堆栈上构造数组,从而将其放入执行的指令中。
  • 其他任何被认为合适的东西。

同样有趣的是,C常量文字没有标识,因此可以共享空间。
无论如何,使用as-if规则,可以多次优化静态(甚至动态/自动)变量。

数组根据其范围类型存储在cml(即连续的内存位置)中。 例如,全局(静态)数组将保存在符号开始的块(bbs)中,这是数据段的一部分,而本地则在计算机内存中创建为堆栈。 它是一个字符串,因为数组中的每个元素都指向它的下一个元素,形成字符序列,形成字符串。 根据问题的新变化进行编辑

 char str[] = "Hello World"; 

你做:

 char str[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '\0'}; 

由于最后一个字符是'\0' / NULL / 0您不会将信息填充到存储数据类型的最后一个内存块中。 在这种情况下,您将终止字符串,您将不会收到泄漏。 这就是C如何处理char数组,尤其是字符串。 它们是以null结尾的字符串。 很多像strlen这样的函数只有在有一个null终止符时才有效。

此外,如果您使用动态创建的数组,它们将存储在堆中。 我知道堆不多,基本上它提供了一个分配环境并为此目的管理内存。

不是答案(Deduplicator已经给了一个好的,我想),但也许这将说明你的问题……

考虑以下C代码:

 #include  int main() { char foo[] = "012"; /* I just do something with the array to not let the compiler * optimize it out entirely */ for(char *p=foo; *p; ++p) { putchar(*p); } putchar('\n'); return 0; } 

使用汇编程序输出(在我的机器上使用GCC):

 [...] .LC0: .string "012" [...] main: [...] movl .LC0(%rip), %edi 

你在只读内存中有一个字符串(该字符串将从程序启动直到退出)。 当我将初始化foo的行更改为

  char foo[] = "0123"; 

海湾合作委员会认为值得这样做:

  movl $858927408, (%rsp) # write 858927408 long (4 bytes) to where the stack pointer points to movb $0, 4(%rsp) # write a 0 byte to the position 4 bytes after where the stack pointer points to 

8589274080x333231300x30'0'的ASCII码, '1' 0x31 ,依此类推); 在后一种情况下,字符串不存储在只读存储器中,它存储在指令本身中。 在这两种情况下,您最终访问的arrays始终位于堆栈中。 在这种情况下,你永远无法访问只读内存中的字符串文字,即使它存在。

HTH

字符串文字存储在静态区域中。 将字符串文字复制到局部变量时,将有两个副本:静态区域和堆栈。 静态区域中的副本不会被删除。 C中没有GC。但是如果在函数中使用指针,则可以访问该字符串。

 #include  char *returnStr() { char *p="hello world!"; return p; } char *returnStr2() { char p[]="hello world!"; return p; } int main() { char *str=NULL; char *str2=NULL; str=returnStr(); str2 = returnStr2(); printf("%s\n", str); printf("%s\n", str2); getchar(); return 0; } 

因此在第一个函数中,它将打印字符串,因为它使用指针。 在第二个函数中,堆栈中的字符串将被删除,因此它将打印垃圾。

每次循环命中它时,程序都不会创建新的字符串。 只有一个字符串已经存在,而文字只是指那个数组。

编译器看到常规字符串文字时,它会创建一个包含字符串内容的char的静态数组(C11§6.4.5/ 6,C99§6.4.5/ 5),并添加数组(或代码)创建它到它的输出。 *

函数中唯一的分配是使用char local_arr[] =... ,它为字符串内容的副本分配足够的空间。 由于它是本地的,当控制离开定义它的块时,它会被有效释放。 并且由于大多数编译器实现自动存储(即使对于数组)的方式,它基本上不会泄漏。

* (每个文字可能最终都在它自己的数组中。或者,相同的字符串文字可能引用相同的数组。或者,在某些情况下,数组甚至可能完全被删除。但这是所有特定于实现和/或优化相关的东西,与大多数明确定义的程序无关。)