题
我正在使用Linux。我正在尝试在C中编写一个将向后打印字符串的程序。这是我的代码:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main (){
char string[100];
printf ("Enter string:\n");
gets (string);
int length = strlen (string)-1;
for (length = length; length>=0; length--){
puts (string[length]);
}
}
这是错误:
a.c:10: warning: passing argument 1 of ‘puts’ makes pointer from integer without a cast
/usr/include/stdio.h:668: note: expected ‘const char *’ but argument is of type ‘char’
/tmp/cc5rpeG7.o: In function `main':
a.c:(.text+0x29): warning: the `gets' function is dangerous and should not be used.
我应该怎么办?
解决方案
第一的:
永远不会从不 利用 gets()
;它 将要 在您的代码中引入故障点。没有办法告诉 gets()
目标缓冲区有多大,因此,如果您通过缓冲区大小以保持10个字符,并且输入流中有100个字符,则 gets()
将在缓冲区末端的内存中快乐地存储这些额外的90个字符,并有可能抓住重要的东西。缓冲区超支是一种简单的恶意软件利用;莫里斯蠕虫专门利用 gets()
致电Sendmail。
利用 fgets()
反而;它允许您指定从输入流读取的最大字符数。但是,与众不同 gets()
, fgets()
如果有空间,将将终止的newline字符保存到缓冲区,因此您必须考虑到:
char string[100];
char *newline;
printf("Enter a string: ");
fflush(stdout);
fgets(string, sizeof string, stdin);
newline = strchr(buffer, '\n'); // search for the newline character
if (newline) // if it's present
*newline = 0; // set it to zero
现在那是不好的...
您的错误来自以下事实 puts()
期望类型的论点 char *
, ,但是您正在传递类型的论点 char
, ,因此“来自无铸件的整数指针”消息(char
是一种积分类型)。要写一个字符以进行stdout,请使用 putchar()
或者 fputc()
.
其他提示
忘记功能 gets()
存在 - 这是致命的。利用 fgets()
相反(但请注意,它不会在行末尾删除新线)。
您想一次放一个字符:使用 putchar()
将其写入Stdout。不要忘记在循环后向输出添加新线。
还, for (length = length; length >= 0; length--)
不是惯用的C。使用:
for ( ; length >= 0; length--)
for (length = strlen(string) - 1; length >= 0; length--)
for (int length = strlen(string) - 1; length >= 0; length--)
最后一个替代方法使用添加到C99中的功能(在C ++之前很久就可以使用)。
另外,我们可以辩论是否 length
是变量的适当名称。它会更好地重命名为 i
或者 pos
或类似的东西,因为它被初始化为输入的长度,但实际上被用作数组索引,而不是任何内容的长度。
主观: :不要在函数的名称及其参数列表之间放置空间。 C的开国元勋不这样做 - 您也不应该这样做。
为什么get()致命?
第一个互联网蠕虫 - 莫里斯 1988年的蠕虫 - 利用 fingerd
使用的程序 gets()
代替 fgets()
. 。从那以后,许多程序崩溃了,因为他们使用了 gets()
并不是 fgets()
或其他选择。
基本问题是 gets()
不知道有多少空间可用于存储其读取的数据。这导致“缓冲区溢出”,该术语可以在您喜欢的搜索引擎中搜索,该术语将返回大量条目。
如果某人键入示例程序的150个字符的输入,则 gets()
将在数组中存储150个字符的长度为100。这永远不会带来幸福 - 通常会导致核心转储,但是经过精心选择的输入(通常由Perl或Python脚本生成),您可能可以使程序可以执行任意任意其他代码。该程序是否会由具有“提高特权”的用户运行,这真的很重要。
顺便, gets()
可能会在下一个版本中从标准C库中删除(C1X-参见N1494 WG14)。它不会从实际的C库中消失很长一段时间(20年?),但是应该用此实现(或类似的东西)代替:
#undef NDEBUG
#include <assert.h>
char *gets(char *buffer)
{
assert("Probability of using gets() safely" == 0);
}
另一个次要细节,部分在主要问题的评论下进行了讨论。
显示的代码显然适用于C99;声明 length
在C89中,通过该函数的一部分无效。考虑到这一点,对 main()
函数不明确返回值,因为C99标准遵循C ++标准的引线,并允许您省略从 main()
效果与 return(0);
或者 return 0;
在最后。
因此,这个问题中的程序不能严格错误地错误 return
在最后。但是,我认为这是最特殊的标准化决策之一,如果标准排除了该规定,或者做了更激进的事情,例如允许无处不在但错误的事情 void main()
观察到控制返回时,结果是将成功状态返回到环境。可悲的是,不值得战斗以使标准的这一方面发生变化 - 但是作为个人风格的决定,我没有利用授予的许可来省略最终 return
从 main()
. 。如果代码必须与C89编译器合作,则应具有明确的 return 0;
最后(但随后声明 length
也必须修复)。
您也可以使用递归来完成。我认为在使用循环时看起来比它更好。
只需用字符串调用该方法,然后在方法中打印char之前,请再次使用相同的字符串调用该方法,减去第一个char。
这将以相反的顺序打印出您的字符串。
您应该使用 putchar
代替 puts
所以这个循环:
for (length = length; length>=0; length--){
puts (string[length]);
}
将会:
for (length = length; length>=0; length--){
putchar (string[length]);
}
putchar
将以一个字符作为参数并将其打印到 stdout
, ,这就是您想要的。 puts
, 另一方面,将将整个字符串打印到 stdout
. 。因此,当您将单个字符传递给期望整个字符串的函数时(char数组, 无效的 终止字符串),编译器感到困惑。
利用 putc
或者 putchar
, , 作为 puts
指定要服用 char*
而你正在喂养它 char
.