开运中国官方网站 C言语内存分派, 栈区、堆区、全局区、常量区和代码区齐是什么

代码装在内存里,却没东说念主真见过它长啥样,当作跑着跑着就崩了,到底哪块儿出了问题?我今天我方脱手扒了一遍。
我过去以为内存分区即是讲义上画的那几条横线,栈在上面堆鄙人头,什么常量区数据区的,背得挺熟。效果写了个小当作,拿`/proc/[pid]/maps`一瞅,全乱了——`r-xp`段好几个,`rw-p`连着三块,还有块`r--p`标着`[anon]`,根底没听过。蓝本讲义说的“五区”,仅仅淳厚为了好讲,硬凑出来的说法。

C言语圭臬里根本没提“栈”“堆”这两个字。翻了翻PDF版C11草案,在6.2.4节只写了“自动存储期”和“静态存储期”,至于存在哪儿,圭臬说:不归我管。这话听着有点铁心无论的意思意思,但其实很真的——编译器和操作系统才决定东西放哪儿。GCC把`int x = 5;`扔`.data`,把`char s[] = "hi";`塞`.rodata`,而`const char *p = "bye";`呢?字符串已经在`.rodata`,指针`p`却在`.data`里,值是指向阿谁只读地址。这下显然为啥改字符串字面量会段演叨了——不是当作错了,是硬件成功拦住不让你写。
我试了`size a.out`,发现`.bss`大小竟然是0,天然代码里写了`int a, b, c;`三个没启动化的全局变量。再查`readelf -S`,果然`.bss`标着`ALLOC`但没标`LOAD`,意思意思即是:磁盘上不存,加载时系统顺遂清零就行。省空间,也快。`.rodata`却老淳结实占着硬盘,因为“hello”这种字符串得从文献读进去。是以`const int x = 123;`进`.rodata`,`const int y = z + 1;`(z是变量)就不可——编译时算不出来,只可放`.data`。
栈也没思象中那么“广大上”。我写了个函数,澳门十大信誉网2026世界杯(中国)官网里头开个`int arr[100000];`,一跑就段演叨。`ulimit -s`一看,8192KB。不是编译器拦你,是Linux内核在页内外设的红线,越界就发`SIGSEGV`。奇怪的是,用`alloca(100000)`也崩,但崩得更“脆”,连调试信息齐少半截。栈照实是CPU和编译器联手搭的架子,`call`推帧,`ret`弹帧,干净利落,但没缓冲区。
堆更践诺。`malloc(100)`看起来浅易,背后是glibc的ptmalloc在吃力:查tcache有莫得现成块,莫得就找fastbin,再不可就碰`brk`或`mmap`。我用`malloc_stats`打出来,看到“fastbins”“unsorted bin”一堆名词,才懂为啥小内存分派其实挺快——很厚情况根本没动系统调用。但`malloc(200*1024)`(200KB)就成功`mmap`了,开运官网`/proc/[pid]/maps`里多了个落寞的`rw-p`段,跟堆干线断开了。不是`malloc`偷懒,是OS以为这样大一块,单独管更省事。
我还试了`thread_local int t = 99;`,`pstack`看不出啥,但`/proc/[pid]/maps`里多了一小块带`[stack:xxx]`标识的内存,每个线程一份。TLS不是存在某个“颠倒区”,即是给每个线程暗暗划了一小块地,名字叫`dtv`,glibc我方记住。还有`mmap`映文献,一个`open+read`变`open+mmap`,读大文献时少一次拷贝,但内存用量成功变大——这些齐不是C言语教的,是Linux给你开的后门。
终末我把通盘变量地址全打出来:全局`g`在`0x404024`(`readelf`阐述是`.data`),字符串`"abc"`在`0x402004`(`.rodata`),局部`int x`在`0x7fff...`(栈),`malloc`总结的在`0x7f...`(堆)。地址数字本人没意旨,但看权限就懂:栈地址`rw-p`,代码地址`r-xp`,`.rodata`地址`r--p`。W^X安全模子就靠这个撑着——写代码段?不可。推论数据段?也不可。当代系统不是靠当作员自发,是靠硬件页表死卡着。
考证这事真不难。写个十几行的C文献,`gcc -g -O0`编译,后台跑起来,查`pid`,再猫进`/proc/[pid]/maps`,左边地址右边权限,中间旅途,一目了然。`pstack`看栈帧,`cat /proc/[pid]/status`看总内存。不需要懂汇编,也无须背术语,眼睛盯着`r-xp`和`rw-p`,比啥齐准。
许多东说念主说堆比栈慢,我测了下100次`malloc(8)`和`int x[8]`,时刻差不到0.01毫秒。真慢的是`free`之后又`malloc`,碎屑多了就得归并。或者你`malloc`一兆再`free`,效果别东说念主`malloc`两百字节卡半天——不是堆慢,是处分战略在衡量。选栈已经堆,看生命周期就行:函数里用,走栈;要传出去、要活久点,走堆。别的齐是障眼法。
我删掉了通盘“常量区”“静态区”的札记。`.rodata`即是`.rodata`,它跟`.data`同属数据段,但权限不同。`static`变量放在`.data`或`.bss`,跟“静态”俩字不进攻,只跟有无启动值探求。术语越迷糊,debug越持瞎。
当今我看当作开运中国官方网站,不先思语法,先思内存。`printf("%s", p);`崩了?先查`p`指向哪——栈上局部数组已开释?堆上`free`过甚?已经`.rodata`里改了字符串?地址一打,权限一看,或然心里就荒芜了。