当我声明以下内容时,幕后会发生什么:-
char a[]="June 14";
和
char *a="June 14";
我的意思是如何为上述两个声明(如 stack 、 data 段)分配内存。
以下是典型的 C 实现。
char a[] = "June 14";
定义了一个最初包含“June 14”(包括终止空值)的数组。这些字符被放置在可执行文件的数据部分中,当程序开始执行时,该数据部分被加载到内存中。(注意:加载可能是虚拟的,因为它实际上是程序虚拟地址空间的一部分,即使它没有立即物理读入内存。)
char *a = "June 14";
定义了两件事。第一个是包含“June 14”的字符串。这些字符被放入可执行文件的数据部分。它可能是只读(也称为常量)数据段。第二个是指针。指针也可能放置在数据段中(不是只读的)。指针用字符串的地址初始化。(根据程序在目标系统上的链接方式,可执行文件可能包含有关如何在加载时填写地址的指令,而不是包含实际地址本身。)
char a[] = "June 14";
定义一个最初包含“June 14”的数组。但是,每次执行到达声明时都必须创建和初始化此数组。(在文件范围声明中,数组只需要在程序启动时进行初始化。)为了完成这个初始化,编译器将“June 14”放入可执行文件的数据段(同样可能是只读数据段) )。每当执行到达声明(或即将到达)时,编译器会在堆栈上分配一些空间并将只读副本中的字符复制到堆栈上的新空间中。
char *a = "June 14";
类似于文件范围声明,因为字符串被放入数据部分(可能是只读的)并创建了一个指针。但是,指针名义上是在堆栈上而不是在数据段中。(我说“名义上”是因为优化很可能导致像这样的指针主要在处理器寄存器中,而不是实际上在堆栈上,或者甚至完全优化掉。)指针通常通过计算指令初始化来自其他信息的字符串的地址。(例如,加载器可以将寄存器设置为它加载数据段开始的地址,因此指针初始值的计算将采用该地址并将数据段内字符串的偏移量添加到该地址。 )
考虑代码:
#include <stdio.h>
char a1[] = "June 14";
const char *a2 = "June 14";
void function(void)
{
char b1[] = "June 14";
const char *b2 = "June 14";
printf("%-7s : %-7s : %-7s : %-7s\n", a1, a2, b1, b2);
a1[6]++;
b1[6]++;
a2++;
b2++;
printf("%-7s : %-7s : %-7s : %-7s\n", a1, a2, b1, b2);
}
int main(void)
{
function();
function();
return(0);
}
数组a1
被分配空间并在程序加载字符串时进行初始化。为指针a2
分配空间,并且该指针被初始化为指向字符串的副本。我使用const
是因为你不能修改字符串文字;该值可能存储在程序的文本段中(不可写)。
数组b1
在堆栈上分配空间,当函数被调用时,它用字符串初始化。这意味着,正如WhozCraig在评论中指出的那样,每次调用函数时,必须有一个字符串副本用于初始化数组。为指针b2
分配空间,并且该指针被初始化为指向字符串的副本。实际上,编译器可能会生成a2
并b2
指向相同的字符串。
该函数打印四个字符串的值;然后它修改两个数组中数字的最后一位,并增加两个指针以指向字符串常量中的下一个字符,并再次打印四个字符串。对该函数的第二次调用将重置b1
为原始字符串 ( "June 14"
),但在第二次调用开始时a1
仍更改为"June 15"
。同样,第一次打印时a2
仍然指向 ,第二次打印时仍然指向 ,同时指向第一次和第二次。因此输出为:u
n
b2
J
u
June 14 : June 14 : June 14 : June 14
June 15 : une 14 : June 15 : une 14
June 15 : une 14 : June 14 : June 14
June 16 : ne 14 : June 15 : une 14
如果函数内部有静态数组或静态指针,它们的行为将类似于外部数组a1
和指针a2
。
这里有两件事要分配空间。
char a[]="June 14";
char *a="June 14";
对于对象“a”(指针)和常量数据“June 14”。
如果在函数的上下文中定义了“a”,或者在数据段中定义了“a”,则在堆栈上分配对象的空间,如果在静态上下文中定义了“a”。
常量数据的空间在 .rodata(只读数据)部分中分配。
为了,
char a[]="6 月 14 日";
Linux 上的 gcc 在堆栈区域中分配内存。发生以下转变,
- 为堆栈中的 a 分配内存,大小为 ("June 14") + 1
- 然后在我们调用函数的时候把“June 14”复制到内存区
为了,
字符 *b="6 月 14 日";
Linux 上的 gcc 在 .rodata 中分配内存并将“June 14”字符串存储在 .rodata 部分中。然后分配变量 b 并保存 .rodata 部分中“June 14”字符串的内存区域的地址。
查看相同的示例 C 程序,
#include<stdio.h>
main()
{
char a[]="Test Message"; //memory is allocated in the stack and stores "Test Message"
char *b="Test Const String"; //Memory allocated in .rodata section and b points to the memory address
}
我已经使用带有 -c 选项的 gcc 编译了上面的代码,并使用 objdump 命令分析了 obj 文件。以下是输出,constPointer.o:文件格式 elf32-i386
Contents of section .text:
0000 8d4c2404 83e4f0ff 71fc5589 e55183ec .L$.....q.U..Q..
**0010 24c745eb 54657374 c745ef20 4d6573c7 $.E.Test.E. Mes.
0020 45f37361 6765c645 f700c745 f8000000 E.sage.E...E....**
0030 0083c424 595d8d61 fcc3 ...$Y].a..
Contents of section .rodata:
**0000 54657374 20436f6e 73742053 7472696e Test Const Strin
0010 6700 g.**
Contents of section .comment:
0000 00474343 3a202847 4e552920 342e332e .GCC: (GNU) 4.3.
0010 30203230 30383034 32382028 52656420 0 20080428 (Red
0020 48617420 342e332e 302d3829 00 Hat 4.3.0-8).
我已经突出显示(使用**)在不同部分完成的内存分配。在那个“Test const String”被放置在不可修改的.rodata部分中。
所以,a[3]='t'; 将编译没有任何错误或警告,但 b[2]='t'; 将导致运行时错误。
希望这可以帮助。