gcc中的c内存管理

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个存储位置.任何具体的好处?

The*_*kis 7

堆栈和堆启动的地址由操作系统提供给进程.其他所有内容都由编译器决定,使用编译时已知的偏移量.其中一些可能遵循目标体系结构中遵循的现有约定,而其中一些则不遵循.

C标准没有强制要求关于堆栈帧内局部变量的顺序(正如评论中所指出的,它甚至根本不要求使用堆栈).标准只涉及到结构时定义顺序,即使这样,它也没有定义特定的偏移,只有这些偏移必须按递增顺序排列.通常,编译器会尝试以这样的方式对齐变量:对它们的访问尽可能少地获取CPU指令 - 而标准允许这样做,而不强制它.

  • @rootkea - 如果地址是数据大小的偶数倍,则在处理器(x86)上获取数据的速度最快.这样,信息可以直接在硬件中传播,而不需要任何可能的延迟来对比特进行排序.例如,如果你的`double`在一个以4结尾的地址上,那么处理器可能必须读取两个64位字并从每次读取中选择四个字节.这可能需要一些额外的时间. (2认同)

Bas*_*tch 5

部分原因是由系统和处理器的应用程序二进制接口(ABI)规范强制实施的.

请参阅x86调用约定SVR4 x86-64 ABI补充(我给出了最新副本的URL;最新的原始文件在Web上很难找到).

在给定的调用帧内,编译器可以将变量放在任意堆栈槽中.它可以尝试(在优化时)随意重组堆栈,例如通过减少对齐约束.你不应该担心这一点.

编译器尝试将局部变量放在具有适当对齐的堆栈位置上.请参阅GCC 的alignof扩展.编译器确切地放置这些变量并不重要,请参阅我的答案.(如果它对你的代码很重要,你真的应该将变量打包在一个公共的本地struct,因为每个编译器,版本和优化标志可以做不同的事情;所以不要依赖于你的特定编译器的精确行为).