当我们用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中有四种类型的内存:
- 静态内存:从开始到结束都是有效的。 它具有逻辑上只读(写入是未定义的行为)和可写的风格。
- 线程本地内存:类似于静态内存,但每个线程都不同。 这是一种新奇的东西,就像所有线程支持一样。
- 自动记忆:堆叠中的所有东西。 离开块会自动释放它。
- 动态内存:请求返回
malloc
,calloc
,realloc
等内容。 别忘了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
858927408
是0x33323130
( 0x30
是'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[] =...
,它为字符串内容的副本分配足够的空间。 由于它是本地的,当控制离开定义它的块时,它会被有效释放。 并且由于大多数编译器实现自动存储(即使对于数组)的方式,它基本上不会泄漏。
* (每个文字可能最终都在它自己的数组中。或者,相同的字符串文字可能引用相同的数组。或者,在某些情况下,数组甚至可能完全被删除。但这是所有特定于实现和/或优化相关的东西,与大多数明确定义的程序无关。)