为什么调用sbrk(0)两次会得到不同的值?

18

我正在尝试理解sbrk()函数。

根据我的了解:
sbrk(0)返回当前断点地址,不会增加它。
sbrk(size)将断点地址增加size字节,并返回先前的断点地址。

所以我创建了一个测试程序:

#include <unistd.h>
#include <stdio.h>

int main(void)
{
    printf("sbrk(0) = %p\n", sbrk(0)); // should return value x
    printf("sbrk(0) = %p\n", sbrk(0)); // should return value x
    printf("sbrk(5) = %p\n", sbrk(5)); // should return value x
    printf("sbrk(0) = %p\n", sbrk(0)); // should return value x + 5
}

所以我希望看到的结果应该是这样的:

sbrk(0) = 0x1677000 // x value
sbrk(0) = 0x1677000 // x value
sbrk(5) = 0x1677000 // x value
sbrk(0) = 0x1677005 // x value + 5

但是我得到的却是这个:

sbrk(0) = 0x1677000 // x value
sbrk(0) = 0x1698000 // y value
sbrk(5) = 0x1698000 // y value
sbrk(0) = 0x1698005 // y value + 5
为什么 sbrk(0) 的前两次调用返回值不同?在这两次调用之间发生了什么导致断点地址发生变化?
编辑:将地址存储在变量中可以解决该问题。
int main(void)
{
    void *toto1 = sbrk(0);
    void *toto2 = sbrk(0);
    void *toto3 = sbrk(5);
    void *toto4 = sbrk(0);

    printf("sbrk(0) = %p\n", toto1);
    printf("sbrk(0) = %p\n", toto2);
    printf("sbrk(5) = %p\n", toto3);
    printf("sbrk(0) = %p\n", toto4);
}


14
"printf" 可能会使用一些动态内存操作。尝试将值分配给变量,然后一次性打印它们。 - Eugene Sh.
1
请更新您的问题并附上其他代码,这样就不会重复假设了。 - Eugene Sh.
3
你需要使用“多个”变量,并在调用“printf”之前完成对“sbrk”的“所有”调用。你更新的代码基本上与第一个代码相同,并且将具有相同的问题。 - Some programmer dude
1
我的猜测是标准 I/O 部分的初始化是懒惰的。也就是说,当需要它们时才进行初始化,并且其中一部分初始化是为 stdout 分配缓冲区。 - Some programmer dude
1
这是glibc的参数处理代码:https://github.com/bminor/glibc/blob/master/stdio-common/vfprintf-internal.c它在几个地方调用了malloc() - rumpelsepp
显示剩余5条评论
1个回答

15

您的程序执行以下调用序列:

sbrk()
printf()
sbrk()
printf()
...

printf的第一次调用会在内部调用malloc函数来为stdout分配一个缓冲区(默认情况下,stdout是行缓冲的,但是当您第一次将数据打印到它时,缓冲区才会按需创建)。

这就是为什么第二次调用sbrk会返回不同值的原因。

(这个回答与直接相关性不强,但是valgrind的错误消息揭示了隐藏在printf内部的malloc调用的存在。)

你的第二个示例将所有的sbrk调用都集中在一起,这样就不会有其他函数在背后调用malloc而使你惊讶了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接