问道 C 的基础题

2019-04-14 15:56:09 +08:00
 b00tyhunt3r

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


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

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

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

C. 程序会一直运行下去

D. 程序不能被编译

主要是内部原理不太明白

5224 次点击
所在节点    C
40 条回复
q8515620
2019-04-14 17:32:09 +08:00
好不容易码完字,还不让发。。也是够了
lance6716
2019-04-14 19:26:05 +08:00
求求你看看 CSAPP
geelaw
2019-04-14 20:10:58 +08:00
不能编译通过,因为 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
2019-04-14 21:06:33 +08:00
所以到最后 也没人给出结论到底应该选什么 真娱乐啊 药丸。。。
eret9616
2019-04-14 21:16:57 +08:00
@eret9616 所以我来给个结论把 正确答案是 B
Nerv
2019-04-14 21:52:02 +08:00
win10 gcc 下运行,成功死机
abccba
2019-04-14 22:11:30 +08:00
这个题(已知的信息)没有正确答案吧。不知道 malloc()失败和 oom 谁先来。
HHehr0ow
2019-04-14 22:30:00 +08:00
jedihy
2019-04-15 00:22:28 +08:00
出这种题的人水平极低
stephen9357
2019-04-15 00:35:03 +08:00
B 啊,这也太基础了。不停 malloc,最终会 OOM,但 OOM 并不影响程序的正常运行,指示 malloc 分配失败会返回空指针,这时*p=0 自然就崩掉了。
usingnamespace
2019-04-15 00:56:15 +08:00
@b00tyhunt3r 解引用是 C 最基础的概念,即对指针变量里存的地址进行读取或写入内容 。事实上每个地址你能不能写 ,操作系统都是有数的,比如 NULL 就是一个被定义为 0 的宏,这个就是专门用来表示不可以被解引用的地址 0x00000。说个扩展的,就 malloc 来说,大部分 malloc 会在其返回的指针的前面一小块还存了这段开辟出来的内存的长度
usingnamespace
2019-04-15 01:00:15 +08:00
@b00tyhunt3r 怎么是 c 语言没学好吗?看得都不太想给你解释了。。。如果是大一学生的话希望你一定要把 c 学的扎扎实实的 虽然真正的 c 要在 linux 系统编程才能体现的淋漓精致。。
azh7138m
2019-04-15 01:22:46 +08:00
@geelaw 我翻了一下 ISO/IEC DIS 14882:2017(E)里面提到 dereferenceable 的部分似乎也没说是 0 的地方要怎么处理?
azh7138m
2019-04-15 01:30:26 +08:00
@geelaw 以为是 C++了,C17 里面翻到了
6.5.3.2 Address and indirection operators
If 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
2019-04-15 09:08:38 +08:00
很不幸的是,C 语言中 dereferencing null pointer 是未定义行为,不一定导致程序错误退出。而且如同前面其他人所说,你不能假定 malloc 返回 null 之前不会被 oom kill
bp0
2019-04-15 10:09:48 +08:00
看起来这道题的考察点是分配内存后没有检查返回值则直接使用,但是却有很多漏洞。

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

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

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

一直 malloc(1)最后不也会因为 oom 而返回 null 吗?和 malloc (1024*1024)有啥区别?后者 oom 了之后不也一样会返回 null 吗
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
2019-04-30 01:35:24 +08:00
@webdisk 感谢!

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

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

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

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

© 2021 V2EX