c中的gets和malloc

Gets and malloc in c

我在 github 的某处看到了以下代码

char *p=malloc(1);
gets(p);
printf(p);

我也试过,发现它有效。 无论我输入多长的字符串,它都会被存储并且不会出现分段错误。怎么运行的?我只给了它1个字节。

另外,当我输入 free(p); 时,它会给出奇怪的输出。

该代码是为什么 gets 已从 2011 标准的标准库中删除的教科书示例。这是一个恶意软件漏洞。

gets 从标准输入中读取一个字符序列,直到它看到一个换行符,并将该序列存储到从地址 p 开始的缓冲区中。 gets 不知道目标缓冲区有多大,如果输入序列的长度超过了缓冲区的大小,那么 gets 会很乐意将这些多余的字符存储到缓冲区后面的内存中,可能会造成各种混乱。

您分配了一个全部为 1 字节宽的缓冲区。当您调用 gets 时,它会将输入的第一个字符写入该缓冲区,然后将任何其他输入(加上一个零值终止符)写入缓冲区后面的未分配堆内存。

在这种特定情况下,没有重要的内容被覆盖,因此您的代码似乎可以正常运行。但是,在另一个上下文中,此代码可能会导致其他数据损坏或导致运行时错误。

写入超过缓冲区末尾的行为是未定义;编译器不需要警告你任何事情,编译后的代码可以做任何事情,从彻底崩溃到执行病毒以按预期工作。

所以,

  1. NEVER NEVER NEVER NEVER NEVER 使用 gets。曾经。在任何情况下。甚至在玩具代码中也不行。正如我所说,它不再是标准库的一部分。

  2. C 将资源管理的所有负担都推给了你,程序员——缓冲区不会自动增长以容纳额外的输入,也没有任何自动垃圾收集来清理不再存在的动态内存参考。

  3. C 不会保护您免于做一些愚蠢的事情——该语言假定您始终知道自己在做什么。

正如其他人所指出的,这是 未定义的行为

未定义的行为可能很难考虑。这里有一个类似你的问题,可能更容易思考。

“我在哪里听说过这个规则,'When the light is red, you must not go through the interesection'。 我尝试了同样的方法,发现它有效。 无论我开车闯红灯多少次,都没有发生任何不好的事情。 它是如何工作的? 我住在一个非常小的城镇,不久前由于预算削减,我们不得不解雇我们的警察。"

假设你有一个心不在焉的房地产经纪人朋友。有一天你对他说,"I want to build a house."他问,"How big a house do you want?"你说,"Oh, 1,000 square feet should be fine."他说,"As a matter of fact, there's a 1,000 square foot vacant lot right next to my house. You can have it."然后他去度假了。

假设你有另一个粗心的建筑师朋友。你说,"I just bought a lot. Can you build a house for me on it?" 他说 "No problem,",然后他就开始挖掘和建造。其实你不告诉他要盖多大的房子,他就给你盖一万尺的豪宅。在这个过程中,他无意中拆除了隔壁房地产经纪人的房子,同时为新豪宅的西翼开挖。

后来你意识到你根本不想要这座豪宅,所以你决定把这块地卖回给房地产经纪人。当他 returns 休假时,你打他的手机 phone 告诉他这件事。你希望他说,"Okay, great, now I can sell the lot to someone else." 相反,他大喊,"You bastard, I'm going to sue you!" 你认为这样说很奇怪。

如果不是很明显,这个小故事中的房地产经纪人扮演 mallocfree 的角色,建筑商扮演 gets 的角色。

您的代码还有一个问题。这个类比更不现实,但让我们试一试。假设,在建筑商完成您的豪宅建造之后,但在您尝试摆脱它之前,您决定想要一张它的照片。所以你请你的朋友,一位非常注重文字的摄影师,为你拍张照片。 "You know my rules," 他说。 "Please make a list of the items you'd like me to photograph, and put it in my mailbox." 你找不到一张纸条,而且你认为只写一个清单很傻,所以你没有在他的邮箱里放一张写着 "my house" 的清单,而是拿起你的整个房子,试着把它放在他的邮箱里。当然,它放不下,所以你把它放在他的前廊上。当他回到家,他看到你的房子,一时糊涂了,但你房子上有你的邮箱,所以现在他回到了坚实的基础上,他知道该怎么做,他打开邮箱找到他的下一个任务。在你的邮箱里是你的新副本国家地理,封面故事在"The ten most beautiful (but deadly) places in the world." 所以他去为你拍摄那些,你再也没有收到他的消息。

所以当你听说 printf 需要一个你希望它打印的东西的列表作为它的第一个参数时,请总是给它那个列表,即使你只希望它打印一个东西.也就是说,请改为 printf("%s", p)。 (要明白为什么,假设你输入到 gets 调用的字符串不是 "A",不是 "Hello",而是 "The string is %s."。然后仔细想想 printf 会这样做,请记住 printf 和你的摄影师朋友一样注重文字。)