2017-05-21 43 views
0

某处在github上,我看到了下面这段代码获取)和malloc(在C

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

我试过相同,发现它的工作原理。 无论我输入多长时间的字符串,它都会被存储并且不会给出分段错误。怎么运行的?我只给了它1个字节。

加上当我输入free(p);它给出奇怪的输出。

+2

未定义的行为。 – Fredrik

+0

'gets'不应该被使用。对于任何长度大于1的字符串,你都有一个超出界限的存储器访问。 – nucleon

+2

如果'gets()'在'p'(除了''\ 0'')之外写入多于零个字符,它会导致一个缓冲区溢出(注意'gets()'没有办法检查要写入多少个字符)。之后,任何事情都可能发生。它可能看起来像你期望的那样工作,它可能在星期三崩溃你的程序,... – pmg

回答

1

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

gets从标准输入读取一系列字符,直到看到一个换行符,并将该序列存储到地址为p的缓冲区。 gets不知道目标缓冲区有多大,并且如果输入序列比缓冲区的大小要长,那么gets会很高兴地将这些多余的字符存储到紧跟在缓冲区之后的内存中,可能会导致各种混乱。

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

在这个特定的情况下,没有重要的被覆盖,所以你的代码看起来正常工作。但是,在另一个上下文中,此代码可能会导致其他数据被破坏或导致运行时错误。

写入缓冲区末尾的行为是undefined;编译器不必警告任何事情,编译后的代码可以做任何事情,从崩溃彻底执行病毒按预期工作。

所以,

  1. 永远永远永远永远永远永远使用gets。永远。在任何情况下。甚至没有玩具代码。就像我说的,它不再是标准库的一部分。

  2. C将资源管理的所有负担都放在你身上,程序员 - 缓冲区不会自动增长以适应额外的输入,也不会有任何自动垃圾收集来清除不再被引用的动态内存。

  3. C不会保护你免于做一些愚蠢的事 - 语言假定你知道你在做什么。

0

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

未定义的行为可能比较棘手。这是一个类似于你的问题,可能会让你更容易思考。 “

”某处我听到这个规则,'当光线是红色的时候,你不能通过这个区域' 我试过了,发现它的工作原理。 无论我通过红灯开车多少次,没有什么不好的事情发生。 它是如何工作的? 我住在一个非常小的城镇,由于预算削减,我们前一阵子不得不解雇我们的警察。“

0

假设你有一个朋友是一个心不在焉的房地产经纪人,有一天你对他说,“我想建一座房子”,他问道:“你想要多大的房子?”你说:“哦,1000平方英尺应该没问题。”他说,“事实上,有1000平方英尺的空地很多就在我家旁边。你可以拥有它“然后他休假休假

假设你有另一位朋友是一个粗心的建设者,你说:”我刚刚买了很多。你能为我建一座房子吗?“”没问题,“他说,然后他开始挖掘和建造,事实上,既然你没有告诉他建造一栋房子有多大,他会为你建造一座房子10000平方英尺的豪宅,在此过程中他不经意间拆除了隔壁房地产经纪人的房屋,同时为新楼西翼挖掘。把这批货卖回房地产经纪人,当他从假期回来时,你用他的手机打电话给他,告诉他这件事,你希望他说:“好吧,好的,现在我可以卖给别人“相反,他大叫道:”你这个混蛋,我要告你了!“你认为这是一件奇怪的事情。

如果不是很明显,房地产经纪人在这个小故事中扮演着mallocfree的一部分,而建筑师扮演着gets的角色。

你的代码还有一个问题。这个比喻更不现实,但让我们试试看。假设在建造者完成建造你的豪宅之后,但在你试图摆脱它之前,你决定你想要一张照片。所以你问问你的朋友,这位文字般的摄影师为你拍照。 “你知道我的规则,”他说。 “请列出你希望我拍照的物品,然后放在我的信箱里。”你找不到一张纸,你认为只用一个物品写一个清单是很愚蠢的,所以不要在他的邮箱中列出一个名为“我的房子”的清单,而是要拿起你的整个房子并尝试把它放在他的邮箱中。当然,它不适合,所以你把它放在他的前廊上。当他回到家时,他看到了你的房子,并且一度困惑,但是你的房子里有你的邮箱,所以现在他重新站稳脚跟,他知道该怎么办,他打开邮箱找到他的下一个任务。在您的邮箱中,您的新副本是National Geographic,封面故事是“世界上最美丽(但致命的)十个地方”。因此,他会为你拍照,而你再也不会收到他的消息。

因此,当您听到printf需要第一个参数列出您希望打印的内容时,即使只有一件您希望打印的东西,请始终将其列出。也就是说,请改为printf("%s", p)。 (要知道为什么,假设你输入的字符串不是“A”,而是“Hello”,但是“字符串是%s。”然后仔细想想printf要做什么,记住printf就像你的摄影师朋友一样。)