Я написал следующую программу,
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include "rand.h"
int main (int argc, char* argv[]) {
void *x = sbrk(0);
printf("The initial top of the heap is %p.\n", x);
void *y = sbrk(0);
printf("The current top of the heap is %p.\n", y);
printf("The difference is %d (%x)\n", (int) (y-x), (int) (y-x));
return 0;
}
Я понимаю, почему разрыв кучи отличается: он должен выделять пространство в куче для вызовов печати.
Чего я не понимаю, так это почему на моем компьютере с Linux x86_64 разница составляет именно 25600 байт.
Например, самой строке, вероятно, требуется лишь небольшое количество байтов (каждый байт — это символ, добавьте немного данных заголовка к строке в куче), а даже близко не требуется даже 1000 байт, верно?
У меня было небольшое предположение, что это может быть как-то связано с пейджингом, чего я пока не совсем понимаю. Но при беглом поиске кажется, что подкачка в любом случае выделяет только около 4000 байт за раз, так что, вероятно, это тоже не так, верно?
Может быть, различные include
имеют к этому какое-то отношение? Я, честно говоря, не знаю, как это вообще влияет на память кучи.
В любом случае, прямой вопрос: почему эта программа заставляет кучную память перемещаться на 25600 байт?
Обратите внимание, что арифметика с указателями void *
не определена стандартом C. Это сбивающее с толку расширение GCC: насколько велик ничтожный объект, у которого нет типа и который не существует?
@AndrewHenle Возможно, я ошибаюсь, но я думал, что смысл void *
заключался в том, чтобы просто изучить сам адрес, не смягчаемый интерпретацией системы типов. Я имею в виду, что можно таким образом сжечь сетчатку, но это для науки, так что...
И я думаю, что в результате вы могли бы подумать, что просто определяете плюс и минус с помощью целочисленной арифметики без знака.
Как сказал какой-то чувак-программист. Выполните еще один printf, а затем еще один sbrk(0). Я подозреваю, что это не сильно изменится, если вообще изменится.
@Аддем Нет. Итоговый вывод: арифметика с void* запрещена как в C, так и в C++.
Почему печать приводит к такому значительному увеличению разрыва кучи?
Поскольку «печать» выделяет память, поэтому перемещается разрыв кучи. Запустив программу под отладчиком, вы можете заметить:
(gdb) bt
#0 __GI___sbrk (increment=increment@entry=135168) at sbrk.c:37
#1 0x00007ffff7e45b36 in __glibc_morecore (increment=increment@entry=135168) at /usr/src/debug/glibc/glibc/malloc/morecore.c:29
#2 0x00007ffff7e46bfd in sysmalloc (nb=nb@entry=656, av=av@entry=0x7ffff7f87ac0 <main_arena>) at malloc.c:2709
#3 0x00007ffff7e47c5a in _int_malloc (av=av@entry=0x7ffff7f87ac0 <main_arena>, bytes=bytes@entry=640) at malloc.c:4481
#4 0x00007ffff7e47f37 in tcache_init () at malloc.c:3252
#5 0x00007ffff7e48776 in tcache_init () at malloc.c:3248
#6 __GI___libc_malloc (bytes=bytes@entry=1024) at malloc.c:3313
#7 0x00007ffff7e21f14 in __GI__IO_file_doallocate (fp=0x7ffff7f885c0 <_IO_2_1_stdout_>) at filedoalloc.c:101
#8 0x00007ffff7e31214 in __GI__IO_doallocbuf (fp=0x7ffff7f885c0 <_IO_2_1_stdout_>) at /usr/src/debug/glibc/glibc/libio/libioP.h:1030
#9 __GI__IO_doallocbuf (fp=fp@entry=0x7ffff7f885c0 <_IO_2_1_stdout_>) at genops.c:342
#10 0x00007ffff7e2f2e8 in _IO_new_file_overflow (f=0x7ffff7f885c0 <_IO_2_1_stdout_>, ch=-1) at fileops.c:745
#11 0x00007ffff7e2fde8 in _IO_new_file_xsputn (f=0x7ffff7f885c0 <_IO_2_1_stdout_>, data=<optimized out>, n=47)
at /usr/src/debug/glibc/glibc/libio/libioP.h:1030
#12 _IO_new_file_xsputn (f=0x7ffff7f885c0 <_IO_2_1_stdout_>, data=<optimized out>, n=47) at fileops.c:1197
#13 0x00007ffff7dfd549 in __printf_buffer_flush_to_file (buf=buf@entry=0x7fffffffd9b0) at ../libio/libioP.h:1030
#14 0x00007ffff7dfd60c in __printf_buffer_to_file_done (buf=buf@entry=0x7fffffffd9b0) at printf_buffer_to_file.c:120
#15 0x00007ffff7e08e0c in __vfprintf_internal (s=0x7ffff7f885c0 <_IO_2_1_stdout_>,
format=0x555555556008 "The initial top of the heap is %p.\n", ap=ap@entry=0x7fffffffdab0, mode_flags=mode_flags@entry=0)
at vfprintf-internal.c:1545
#16 0x00007ffff7dfccf3 in __printf (format=<optimized out>) at printf.c:33
#17 0x0000555555555181 in main ()
Выделение происходит из буфера выделения glibc для стандартного вывода в https://github.com/lattera/glibc/blob/master/libio/fileops.c#L752.
Возможно, буферы
stdout
и другие структуры и данные не выделяются до первого вызоваprintf
(или любой другой функции, использующейstdout
). На самом деле для строки формата или самой сгенерированной строки не выделяется память, она напрямую использует частные буферы.