*** stack smashing detected ***: terminated Aborted (core dumped)
我搜索了一下,发现这是一个gcc的安全问题,我听说它可能会导致seg故障,所以我尝试用-fno-stack-protector
关闭它,如果我超过字符限制,它就会正常运行
但是,如果我想写程序,如果输入长度未知,有更安全的方法吗?将char中的值增加到一个大得离谱的值更有效吗?
代码:
#include <stdio.h>
int main()
{
char in[1];
printf("in: ");
scanf("%s ", &in);
printf("nout: %sn", in);
}
p.s-im是C的新手,>2天大,所以一个简单的解释将不胜感激
char in[1];
只能容纳空字符串(单个空终止字节(,这是不可能与scanf
一起安全使用的。
还要注意,在字符串文字中显式声明null终止字节是多余的,因为它们是隐式null终止的。
但是如果我想在输入长度未知的情况下编写程序,有更安全的方法吗?将char中的值增加到一个大得离谱的值更有效吗?
这里的反问题是:
- 你认为什么是低效的
- 你把什么定义为大得离谱
在我看来,你有两个选择:
- 使用动态分配的内存读取任意大小的字符串
- 设置一个实际的输入长度上限
#1的一个例子可以在库函数(如POSIXgetline
或getdelim
(中看到。它的重新实现可以像malloc
(realloc
(、getchar
和一个循环一样简单。
#2的使用在很大程度上取决于程序的上下文,以及它应该做什么。也许你只需阅读一行,一个小的缓冲区就足够了。也许您期望得到更大的数据块,并且需要更多的内存。只有你自己才能决定。
在任何情况下,您都可以通过在未定义行为发生之前防止溢出来避免未定义行为。如果已经发生了,那就已经太晚了。
使用%s
:时使用字段宽度说明符
char buf[512];
if (1 != scanf("%511s", buf))
/* read error */;
或者使用类似fgets
的sane函数,它允许您传递缓冲区大小作为参数。
检测到堆栈粉碎
我搜索了一下,发现这是一个gcc安全
这确实是gcc通过插入所谓的";堆叠金丝雀";以发现堆栈损坏/溢出。检测到更多错误是一件好事。
我听说它可能会导致seg故障
不,应用程序中的错误会导致seg错误。如果编译器提供了在操作系统之前检测它们的方法,那是一件好事。程序中存在休眠但严重的错误是一件坏事。然而,操作系统也可能检测到错误并说";seg故障";。
所以我尝试用-fno堆栈保护器关闭它,如果我超过字符限制,它就会正常运行
基本上你知道自己是一个没有经验的司机,害怕撞到其他车。为了解决这个问题,你可以闭着眼睛开车,这样你就看不到那些你可能撞到的车了。这并不意味着它们消失了。
char in[1];
只能容纳1个字节的数据,如果您读取超出该数组的边界,则会调用未定义的行为,这将表现为堆栈粉碎或seg错误。因为你在试图写那些不属于你的记忆。这就是bug,这就是问题所在。正确的解决方案是分配足够的内存。
(您还有一个错误scanf("%s ", &in);
->scanf("%s ", in);
。不需要&,因为in
是一个数组,当您将其传递给函数时,它会自动"衰减"为指向其第一个元素的指针。(
一种合理的方法是分配128个字节左右,然后限制输入,使其不能超过128个字节。读取具有受限输入长度的字符串的合适函数是fgets
。因此,您可以切换到fgets
,也可以接受您的初学者试用程序不需要达到生产质量,现在只使用scanf
。(您可以安全地使用scanf
,如另一个答案所示,但IMO只是比使用fgets
更麻烦。(
此外,我强烈建议C初学者不要担心他们是否分配了10个字节或100个字节。用电脑学习编程,然后就没关系了。优化内存消耗是一个高级主题,您将在稍后学习。