roo*_*kea 6 c arrays variables gcc memory-management
我在Ubuntu 12.10 x86_64上使用gcc版本4.7.2.
首先是我终端上数据类型的大小:
sizeof(char) = 1
sizeof(short) = 2 sizeof(int) = 4
sizeof(long) = 8 sizeof(long long) = 8
sizeof(float) = 4 sizeof(double) = 8
sizeof(long double) = 16
Run Code Online (Sandbox Code Playgroud)
现在请看一下这段代码:
int main(void)
{
char c = 'a';
printf("&c = %p\n", &c);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
如果我没有错,我们无法预测任何有关地址的信息c
.但每次这个程序给出一些随机的十六进制地址f
.因此,下一个可用位置将是一些以十六进制结束的值0
.在其他数据类型的情况下我也观察到了这种模式.对于int值,地址是以十进制结尾的十六进制值c
.对于double,它是以结束的一些随机十六进制值8
,依此类推.
所以我在这里有2个问题.
1)谁在管理这种内存分配?是gcc还是C标准?
2)无论是谁,为什么会这样?为什么变量的存储方式是下一个可用的内存位置以十六进制值结束0
?任何特定的好处?
现在请看一下这段代码:
int main(void)
{
double a = 10.2;
int b = 20;
char c = 30;
short d = 40;
printf("&a = %p\n", &a);
printf("&b = %p\n", &b);
printf("&c = %p\n", &c);
printf("&d = %p\n", &d);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
现在,我观察到的对我来说是全新的.我认为变量将按照它们声明的相同顺序存储.但不是!事实并非如此.以下是随机运行之一的示例输出:
&a = 0x7fff8686a698
&b = 0x7fff8686a694
&c = 0x7fff8686a691
&d = 0x7fff8686a692
Run Code Online (Sandbox Code Playgroud)
似乎变量按其大小的递增顺序排序,然后它们以相同的排序顺序存储,但保持观察1.即最后一个变量(最大的一个)以这样的方式存储,即下一个可用的存储位置是一个十六进制值结束0
.
这是我的问题:
3)谁支持这个?是gcc还是C标准?
4)为什么要先浪费时间对变量进行排序然后再分配内存而不是直接按"先到先得"的方式分配内存?这种排序的任何特定好处,然后分配内存?
现在请看一下这段代码:
int main(void)
{
char array1[] = {1, 2};
int array2[] = {1, 2, 3};
printf("&array1[0] = %p\n", &array1[0]);
printf("&array1[1] = %p\n\n", &array1[1]);
printf("&array2[0] = %p\n", &array2[0]);
printf("&array2[1] = %p\n", &array2[1]);
printf("&array2[2] = %p\n", &array2[2]);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
现在这对我来说也是令人震惊的.我观察到的是,数组总是存储在一个以'0'结尾的随机十六进制值中elements of an array >= 2
,如果elements < 2
然后它在观察1后得到内存位置.
所以这是我的问题:
5)谁在这个存储数组后面的某个随机十六进制值以0
事物结束?是gcc还是C标准?
6)现在为什么浪费记忆?我的意思是array2
可以在之后立即存储array1
(因此array2
将以内存位置结束2
).但不是那样array2
存储在下一个十六进制值结束,0
从而在其间留下14个存储位置.任何具体的好处?
堆栈和堆启动的地址由操作系统提供给进程.其他所有内容都由编译器决定,使用编译时已知的偏移量.其中一些可能遵循目标体系结构中遵循的现有约定,而其中一些则不遵循.
C标准没有强制要求关于堆栈帧内局部变量的顺序(正如评论中所指出的,它甚至根本不要求使用堆栈).标准只涉及到结构时定义顺序,即使这样,它也没有定义特定的偏移,只有这些偏移必须按递增顺序排列.通常,编译器会尝试以这样的方式对齐变量:对它们的访问尽可能少地获取CPU指令 - 而标准允许这样做,而不强制它.
部分原因是由系统和处理器的应用程序二进制接口(ABI)规范强制实施的.
请参阅x86调用约定和SVR4 x86-64 ABI补充(我给出了最新副本的URL;最新的原始文件在Web上很难找到).
在给定的调用帧内,编译器可以将变量放在任意堆栈槽中.它可以尝试(在优化时)随意重组堆栈,例如通过减少对齐约束.你不应该担心这一点.
编译器尝试将局部变量放在具有适当对齐的堆栈位置上.请参阅GCC 的alignof扩展.编译器确切地放置这些变量并不重要,请参阅我的答案.(如果它对你的代码很重要,你真的应该将变量打包在一个公共的本地struct
,因为每个编译器,版本和优化标志可以做不同的事情;所以不要依赖于你的特定编译器的精确行为).