看看坛友中还有多少是和我一样长年战斗在 C 上的,答对三个就算

2018-09-25 17:36:23 +08:00
 dingzs3

#include<stdio.h>

#include<string.h>

#include<stdlib.h>

void main()

{

    char *p1=(char *)malloc(1024);  //1k

    char *p2=(char *)malloc(4096);  //4k

    memset(p2,0,4096);

    char *p3=(char *)malloc(8192);  //8k

    char *p4=(char *)malloc(128*1024*1024); //128k

    char *p5=(char *)malloc(115*1024);//115k

    printf("p1=%p p2=%p p3=%p p4=%p p5=%p\n",p1,p2,p3,p4,p5);


    memset(p2,'1',20);

    free(p2);

    memset(p2,'y',10);

    printf("p2=%s\n",p2);

    free(p3); //core

    free(p5);

    free(p1);

}

输出: p1=0x1c60010 p2=0x1c60420 p3=0x1c61430 p4=0x7ffbabc2b010 p5=0x1c63440

p2=yyyyyyyyyy?

Segmentation fault (core dumped)

1.p2-p2 为啥不是 1024

2.p4 的地址为啥和其它的不一样

3.为啥在 free(p2)之后还能读写 p2

4.为啥 p2 的打印不是 yyyyyyyyyy1111111111

5.为啥在 free(p3)的时候会 core

8070 次点击
所在节点    程序员
76 条回复
p2pvideo
2018-09-25 21:27:13 +08:00
@dingzs3
顺便问问:楼主在哪个城市啊?
WordTian
2018-09-25 22:10:08 +08:00
1.分配的空间除了 1024,还有一段存储分配空间元数据的空间
2.p4 分配的空间太大,不是从快表(?)分配,具体怎么分的忘了,总之和小空间不连在一块的
3.好像是将 p2 所在块添加到空表,但 p2 的指针还在
4.p2 用 memset 赋值时给尾部 /0,printf 输出时到 /0 终止
5.可能是第一次给 p2 赋值的时候,在尾部添加了 /0,覆盖了 p3 块的元数据,导致报错


不知道我的答案对不对,只是之前了解 linux 堆的时候看了点资料,凭印象来扯淡
yankebupt
2018-09-25 22:54:40 +08:00
关于 3 4 5 题
看了一个来源 blackhat.com 的<draft>文档可能是关于某种 heap 实现的,里面这么讲的

首先 1k 以上 128k 以下不属于 fastbin,但也没有用 mmap(应该)
说法貌似是说 free()了之后内存因为不够对齐,并没有立刻交还给 OS,而是原 userdata 头部部分填上了用于遍历 free 列表的前进后退两个指针,同时后面的部分有可能被清空了。
这就导致写 yyy 的时候覆盖了指针...同时导致后面的任意 free 失效...

两点疑问:第一这种 heap 尤其是 free()实现方式会不会很小众...
第二:理论上不 free p3,free p4 的话会不会有可能不会 core?
lolcat
2018-09-26 00:42:16 +08:00
0.void main()没错,只不过为了程序规范性和可移植性,c99 建议使用 int main();
1.p2-p2 等于 0 ;
2.malloc 时,系统会自动在堆中找一块连续的内存,只要长度够就行,没规定必须紧挨着前面申请的内存空间,而且在申请的空间的前几个字节是有存储数据的,比如存储了这块空间的大小;
3.free(p2)只是告诉系统 p2 申请的空间可以重新新被申请了,里面的数据没有被清空,但如果继续对 p2 指向的空间进行操作就是操作野指针;
4.我觉得不是因为 free(p3)时报的段错误,在你打印野指针 p2 时就会报段错误;
5.长年战斗在 c 上的楼主连这些问题都没弄懂,我现在连工作都找不到,哎,实在是揪心啊。
lolcat
2018-09-26 00:55:51 +08:00
试了一下,果然是在 printf 这句出现段错误的,不是在 free(p3)时
crazyneo
2018-09-26 06:17:47 +08:00
都是些操作系统和实现库相关的东西。
1. 这和 malloc 实现有关,tcmalloc jemalloc ptmalloc dlmalloc 实现各有不同,这个问题没意义。
2. linux 下超过 128m 分配在映射区,这个也和操作系统有关,aix6 上并不存在类似限制,这个问题没意义。
3. 还是和 malloc/free 实现有关,free 之后并不是马上被回收,以及看编译器参数,比如 clang 带上-address 参数,直接就 coredump 了,这个问题仍然没意义。
4 和 5 都是针对被释放内存的瞎搞。

这几个给学生做考试题都非常的不规范,你好歹写个
···
int a[10];
memset(a, 1, sizeof(a));
···
类似这种
dingzs3
2018-09-26 07:20:30 +08:00
@zmj1316 抱歉忘记写了
dingzs3
2018-09-26 07:21:16 +08:00
@innoink 哥,看来你是明白人,我觉着长期写 c 的人不可能不懂操作系统和用到的库,以及编译器吧
dingzs3
2018-09-26 07:22:56 +08:00
@besto 老哥可以算一个
dingzs3
2018-09-26 07:23:43 +08:00
dingzs3
2018-09-26 07:24:46 +08:00
@iceheart 的确是,应该是 128M,或者是代码里面少乘一个 1024
dingzs3
2018-09-26 07:25:19 +08:00
@catror +1
dingzs3
2018-09-26 07:25:43 +08:00
dingzs3
2018-09-26 07:28:10 +08:00
@zwh2698 8 年了,我觉得是指针指向的内存,你可以存任意类型,这个内存想用什么类型操作都可以告诉编译器,或者自己直接用指针+加偏移来弄
dingzs3
2018-09-26 09:06:07 +08:00
@p2pvideo 南京啊,正在想办法回成都

@where2go 这种写法 gcc 也是支持的吧,我这个不需要返回值做判断,所以一个小验证程序没必要这么严谨吧
dingzs3
2018-09-26 09:08:11 +08:00
dingzs3
2018-09-26 09:09:15 +08:00
@lolcat 可能我们的环境不一样吧,用的库也不一样,导致结果不一致了
shilyx
2018-09-26 09:20:52 +08:00
1.p2-p1 为啥不是 1024
malloc 分配了只管在大小范围内用,malloc 也是人实现的,不需要保证任何顺序

2.p4 的地址为啥和其它的不一样
malloc 在原来的地方分不出来了,或者不愿在老地方分配了导致的;无论分配的大小,都可能出现

3.为啥在 free(p2)之后还能读写 p2
malloc 之后保证可以读写,但是 free 或没 malloc 的,不保证读写,也不保证不可读写,可能能读写也可能不能读写,也可能只能读,也可能只能写

4.为啥 p2 的打印不是 yyyyyyyyyy1111111111
可能是也可能不是,malloc 本身有一部分私有内容管理分配信息,不知道他会出现在何处; free 了之后甚至整个堆都有可能消失,说不准

5.为啥在 free(p3)的时候会 core
p3 core 了说明已被破坏,但是这是说不准的,不是必然被破坏
dingzs3
2018-09-26 09:23:09 +08:00
@yankebupt 是的,不会 core,这个 mmap 的内存应该是直接释放的,不会遍历链表
zmj1316
2018-09-26 09:26:38 +08:00
@dingzs3 #28 MMAP_THRESHOLD 默认应该= 128k 你是对的......

@lolcat 打印野指针只要没有 invalid page fault,不会崩的,如果 free p4 再打印 p4 一般会崩,
找不到工作还是搞 C++吧,不用管这些揪心的事情,我其实是 C++选手😀

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/492448

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX