问道 C 的基础题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
b00tyhunt3r
V2EX    C

问道 C 的基础题

  •  
  •   b00tyhunt3r 2019-04-14 15:56:09 +08:00 5827 次点击
    这是一个创建于 2450 天前的主题,其中的信息可能已经有所发展或是发生改变。

    对于以下代码段,正确的说法是:

     char *p; while (1) { p = malloc(1); *p = 0; } 

    A. 最终程序会因为没有没有空间了而退出

    B. 最终程序会因为向 0 地址写入而退出

    C. 程序会一直运行下去

    D. 程序不能被编译

    主要是内部原理不太明白

    40 条回复    2019-04-30 01:35:24 +08:00
    webdisk
        1
    webdisk  
       2019-04-14 15:57:54 +08:00
    OOM
    jxf2008
        2
    jxf2008  
       2019-04-14 16:00:17 +08:00
    A
    sfqtsh
        3
    sfqtsh  
       2019-04-14 16:00:25 +08:00 via Android
    D.
    AngelCriss
        4
    AngelCriss  
       2019-04-14 16:04:13 +08:00 via Android
    当然是选 B 咯,malloc 失败返回 NULL,解引用 NULL 就挂了,如果没有*p = 0,那就看 oom killer 杀不杀,不杀就一直跑。
    b00tyhunt3r
        5
    b00tyhunt3r  
    OP
       2019-04-14 16:08:56 +08:00
    @AngelCriss
    只有你对了,不过本 noob 第一句就看不懂了,为什么 malloc 会失败,是 OOM 之后才失败吗?姐引用是什么意思?多谢大佬解惑
    kljsandjb
        6
    kljsandjb  
       2019-04-14 16:18:04 +08:00 via iPhone   2
    @b00tyhunt3r #5 内存 run out 的时候,malloc 返回 null 的情况吧…所以一般会在操作内存的时候最好判断是否空指针,像嵌入式开发这种内存较为稀缺的,这种情况会稍微多见一点,话说这是啥题目啊,这么考感觉非常有中国考试特色啊
    AngelCriss
        7
    AngelCriss  
       2019-04-14 16:24:59 +08:00
    模拟一下不就知道了
    ```
    ─abby@Chameleon /home/abby system
    ─$ cat f.c 139
    #define NULL 0
    char memory[10];
    int idx = 0;

    char* my_malloc()
    {
    if(idx == sizeof(10))
    {
    return NULL;
    }
    char* ptr = memory + idx;
    idx += 1;
    return ptr;
    }

    int main()
    {
    char* p;
    while(1)
    {
    p = my_malloc();
    #ifdef MO
    *p = 0;
    #endif
    }
    }
    ─abby@Chameleon /home/abby system
    ─$ cc f.c
    ─abby@Chameleon /home/abby system
    ─$ ./a.out
    ^C ─abby@Chameleon /home/abby system
    ─$ cc f.c -DMO 130
    ─abby@Chameleon /home/abby system
    ─$ ./a.out
    fish: “./a.out ” terminated by signal SIGSEGV (Address boundary error)
    ─abby@Chameleon /home/abby system
    ─$
    ```
    @b00tyhunt3r
    webdisk
        8
    webdisk  
       2019-04-14 16:26:28 +08:00
    @kljsandjb 如果一直 malloc(1) 就会出现返回 NULL 的情况, 如果一直 malloc(1024 * 1024) 就会出现 OOM.
    返回 NULL 的时候应该是遇到了 glibc 对内存分配的限制, 或者是 linux 内核对内存分配的限制.
    b00tyhunt3r
        9
    b00tyhunt3r  
    OP
       2019-04-14 16:28:48 +08:00
    @kljsandjb
    浙大幕客题
    其实我不太明白的是,
    定义一个 char 指针 char *p 之后,分配一个字节内存 p = malloc(1);给 p,此刻在计算机内部 p 的状态是什么样的?
    例如 p 自身的位置是 0x100010,
    那么此时:
    ( 1 ) p 指向哪里?
    ( 2 ) OOM 了之后,malloc 函数返回了 NULL 给 p,此刻得到 NULL 的 p 是个什么状态?又指向哪里?
    ( 3 )如果我仅仅只是定义了一个 char *p,但什么也不做(或者定义之后程序还没赋值给 p 这段时间内),
    此刻的内存里,指针 p 是哪里也不指的状态吗?有这种状态的?

    好像教材上没有深入讲
    ashlord
        10
    ashlord  
       2019-04-14 16:35:00 +08:00
    @b00tyhunt3r
    p 指向属于堆( heap )的某块内存
    注意 p 只是一个数值变量,其值恰好是内存地址,所以返回 null 后 p 的值就是 null,即语义上没有指向有效的堆内存。但是由于 c 语言中 null 就是 0,所以会出现对 0 地址写入
    仅定义后 p 就是一个未初始化的变量,具体的值是 compiler i mplementation,c 标准应该没有定义。因此常说变量申明后必须初始化才能使用

    怎么说呢,不要把指针想的太神秘,它就是一个有特殊意义和操作的数值变量……
    alphaprogrammer
        11
    alphaprogrammer  
       2019-04-14 16:35:40 +08:00
    malloc 后需要转为目标类型
    kljsandjb
        12
    kljsandjb  
       2019-04-14 16:36:35 +08:00 via iPhone
    @b00tyhunt3r #9 p 分配在栈空间或者寄存器吧,它的值应该是在堆为你开辟的一块空间的起始位置(也就是指向了堆的某个位置)。你这个相当于死循环一直在开空间,你可以了解一下进程地址空间的概念,看看它的结构,程序怎么在进程上下文运行的,栈、堆的概念等都有,推荐一本书:csapp
    PureWhiteWu
    &nbs;   13
    PureWhiteWu  
       2019-04-14 16:41:42 +08:00
    选 D,没有 include 头文件,编译不过。
    kljsandjb
        14
    kljsandjb  
       2019-04-14 16:41:45 +08:00 via iPhone
    @b00tyhunt3r #9 第三个问题,未初始化的临时变量通常值是不确定的,因为栈始终伴随着程序的调用返回一直在变化,举一个比方,就是刚好一个函数返回,栈指针恢复,但是原先栈中的内容残留了,那么你新建一个临时变量有可能值就是残留的,说白了不用特殊修饰不被优化的临时变量,都是栈地址的表征
    fsafdasfsdafsd
        15
    fsafdasfsdafsd  
       2019-04-14 16:51:15 +08:00
    我的理解是
    malloc 一直分配空间,直到空间满 malloc 分配失败产生 nullptr
    wevsty
        16
    wevsty  
       2019-04-14 16:53:32 +08:00
    D
    malloc(1)返回的类型是 void*,void*直接赋值给 char*编译器会提示错误。
    kljsandjb
        17
    kljsandjb  
       2019-04-14 16:58:03 +08:00 via iPhone
    @wevsty #16 我记得不强制转换只对指针的运算有限制吧,因为不知道运算的步长,赋值应该不会导致编译期的错误
    kljsandjb
        18
    kljsandjb  
       2019-04-14 16:58:30 +08:00 via iPhone
    @wevsty #16 顶多应该是 warnings 吧
    wevsty
        19
    wevsty  
       2019-04-14 17:20:50 +08:00
    @kljsandjb
    试了一下,GCC 上 C 方式编译的话是可以过的,CPP 方式的话过不了。
    看来是我想当然了。
    q8515620
        20
    q8515620  
       2019-04-14 17:30:58 +08:00 via Android   2
    q8515620
        21
    q8515620  
       2019-04-14 17:32:09 +08:00 via Android   1
    好不容易码完字,还不让发。。也是够了
    lance6716
        22
    lance6716  
       2019-04-14 19:26:05 +08:00   1
    求求你看看 CSAPP
    geelaw
        23
    geelaw  
       2019-04-14 20:10:58 +08:00   3
    不能编译通过,因为 while block 必须是函数 block 的后代。

    当然如果假设代码是这样的

    #include<stdlib.h>
    int main(void)
    {
    char *p;
    while (1)
    {
    p = (char *)malloc(1);
    *p = 0;
    }
    }

    那么结论是这四个选项都不对。当 malloc 失败的时候,p = NULL,此后 *p = 0 是未定义行为,程序可以崩溃也可以继续运行,还可能会发射核弹。
    eret9616
        24
    eret9616  
       2019-04-14 21:06:33 +08:00
    所以到最后 也没人给出结论到底应该选什么 真娱乐啊 药丸。。。
    eret9616
        25
    eret9616  
       2019-04-14 21:16:57 +08:00
    @eret9616 所以我来给个结论把 正确答案是 B
    Nerv
        26
    Nerv  
       2019-04-14 21:52:02 +08:00
    win10 gcc 下运行,成功死机
    abccba
        27
    abccba  
       2019-04-14 22:11:30 +08:00 via iPhone
    这个题(已知的信息)没有正确答案吧。不知道 malloc()失败和 oom 谁先来。
    HHehr0ow
        28
    HHehr0ow  
       2019-04-14 22:30:00 +08:00
    jedihy
        29
    jedihy  
       2019-04-15 00:22:28 +08:00 via iPhone
    出这种题的人水平极低
    stephen9357
        30
    stephen9357  
       2019-04-15 00:35:03 +08:00
    B 啊,这也太基础了。不停 malloc,最终会 OOM,但 OOM 并不影响程序的正常运行,指示 malloc 分配失败会返回空指针,这时*p=0 自然就崩掉了。
    usingnamespace
        31
    usingnamespace  
       2019-04-15 00:56:15 +08:00 via iPhone
    @b00tyhunt3r 解引用是 C 最基础的概念,即对指针变量里存的地址进行读取或写入内容 。事实上每个地址你能不能写 ,操作系统都是有数的,比如 NULL 就是一个被定义为 0 的宏,这个就是专门用来表示不可以被解引用的地址 0x00000。说个扩展的,就 malloc 来说,大部分 malloc 会在其返回的指针的前面一小块还存了这段开辟出来的内存的长度
    usingnamespace
        32
    usingnamespace  
       2019-04-15 01:00:15 +08:00 via iPhone
    @b00tyhunt3r 怎么是 c 语言没学好吗?看得都不太想给你解释了。。。如果是大一学生的话希望你一定要把 c 学的扎扎实实的 虽然真正的 c 要在 linux 系统编程才能体现的淋漓精致。。
    azh7138m
        33
    azh7138m  
       2019-04-15 01:22:46 +08:00
    @geelaw 我翻了一下 ISO/IEC DIS 14882:2017(E)里面提到 dereferenceable 的部分似乎也没说是 0 的地方要怎么处理?
    azh7138m
        34
    azh7138m  
       2019-04-15 01:30:26 +08:00   1
    @geelaw 以为是 C++了,C17 里面翻到了
    6.5.3.2 Address and indirection operators
    I an invalid value has been assigned to the pointer, the behavior of the unary * operator is undefined.

    Among the invalid values for dereferencing a pointer by the unary* operator are a null pointer, an address inappropriately aligned for the type of object pointed to, and the address of an object after the end of its lifetime.
    raysonx
        35
    raysonx  
       2019-04-15 09:08:38 +08:00 via Android
    很不幸的是,C 语言中 dereferencing null pointer 是未定义行为,不一定导致程序错误退出。而且如同前面其他人所说,你不能假定 malloc 返回 null 之前不会被 oom kill
    bp0
        36
    bp0  
       2019-04-15 10:09:48 +08:00
    看起来这道题的考察点是分配内存后没有检查返回值则直接使用,但是却有很多漏洞。

    比如,严格说这段程序无法通过编译。因为没有包含头文件,也没有定义函数,而且循环语句无法写在文件作用域的。

    如果不考虑 D。在 Linux 系统中,如果没有修改 overcommit_memory 参数,则有可能在返回 null 之前被 oomkiller 杀掉了,所以 AB 都有可能。

    C 看似最不可能,但如果有无限大的内存和地址空间,为什么不行呢?虽然现实中不存在,但是题目也没说哦。
    kljsandjb
        37
    kljsandjb  
       2019-04-15 20:13:45 +08:00
    b00tyhunt3r
        38
    b00tyhunt3r  
    OP
       2019-04-29 14:07:59 +08:00 via iPad
    @webdisk “如果一直 malloc(1) 就会出现返回 NULL 的情况, 如果一直 malloc(1024 * 1024) 就会出现 OOM.”

    一直 malloc(1)最后不也会因为 oom 而返回 null 吗?和 malloc (1024*1024)有啥区别?后者 oom 了之后不也一样会返回 null 吗
    webdisk
        39
    webdisk  
       2019-04-29 15:13:26 +08:00
    @b00tyhunt3r
    一直 malloc(1) 返回 NULL 是碰到了 glibc|内核 对分配内存块的 数量 的限制, 这里是块的数量
    一直 malloc(1024 * 1024) 出现 OOM, 是因为超过了物理内存和 swap 的总量, 这里总内存量

    首先碰到哪个限制就出现哪个情况.
    如果内存很小, malloc(1) 还没有达到 glibc|内核 限制, 物理内存就光了, 这个时候应该是 OOM
    如果内存巨大, malloc(1024 * 1024) 还没有达到限制, 但是达到了 glibc 的限制, 这时候就会返回 NULL

    没有看代码, 只是做了一种符合观测结果的合理猜测
    OOM 是内核的机制, 杀的进程不一定是运行这个程序的进程, 可能别的进程躺枪
    尤其是设置了不允许对某个进程 OOM kill 的情况.

    个人觉得了解大概就行, 没必要深究这种细节, 碰到自然就明白了
    b00tyhunt3r
        40
    b00tyhunt3r  
    OP
       2019-04-30 01:35:24 +08:00 via iPad
    @webdisk 感谢!
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5750 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 42ms UTC 06:13 PVG 14:13 LAX 22:13 JFK 01:13
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86