【问题标题】:Cannot create a program which will invert string无法创建将反转字符串的程序
【发布时间】:2010-09-02 13:33:02
【问题描述】:

我正在使用 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.

我该怎么办?

【问题讨论】:

  • 我个人也不会使用length 变量作为您的循环计数器变量。这很令人困惑。除非您在内存中遇到问题,否则只需声明一个 int 或 char 计数器变量。 (此处可以使用 char,因为长度最多为 100)。
  • @Stephen:只需使用int。使用char 通常不会节省内存,因为编译器会将其填充到寄存器中,而不是将其保存到内存中。并且编译器可能会将length 优化掉。
  • 您可以省略 for 循环中的第一个子句。 length=length 毫无意义。 for( ; length&gt;=0; length--) {...}
  • @Philip:我认为抱怨是变量length 最好重命名为i 或index 或pos 或类似的东西,因为虽然它被初始化为长度,它实际上用作数组索引,而不是任何东西的长度。
  • @Stephen:如果有人有足够的内存用于标准库,那么(本地,偶数)char 与 int 的重要性有多普遍?此外,如果您在循环开始时说“i = length - 1”并且不再使用长度,编译器很可能会将其优化掉。 [编辑:其他人似乎打败了我。 :)]

标签: c linux string reverse


【解决方案1】:

忘记函数gets() 的存在——它是致命的。请改用fgets()(但请注意,它不会删除行尾的换行符)。

您想一次输入一个字符:使用putchar() 将其写入标准输出。不要忘记在循环之后在输出中添加换行符。

另外,for (length = length; length &gt;= 0; length--) 不是惯用的 C。使用以下之一:

  • for ( ; length &gt;= 0; length--)
  • for (length = strlen(string) - 1; length &gt;= 0; length--)
  • for (int length = strlen(string) - 1; length &gt;= 0; length--)

最后一个替代方案使用添加到 C99 的功能(很久以前在 C++ 中可用)。

此外,我们可以讨论length 是否是变量的合适名称。最好将其重命名为 i 或 pos 或类似名称,因为虽然它被初始化为输入的长度,但它实际上用作数组索引,而不是任何东西的长度。

主观:不要在函数名和参数列表之间加空格。 C 语言的创始人不会那样做——你也不应该这样做。


为什么 gets() 是致命的?

第一个互联网蠕虫——1988 年的Morris 蠕虫——利用了fingerd 程序,该程序使用gets() 而不是fgets()。从那时起,许多程序因使用gets() 而不是fgets() 或其他替代方案而崩溃。

根本问题是gets() 不知道有多少空间可用于存储它读取的数据。这会导致“缓冲区溢出”,这是一个可以在您最喜欢的搜索引擎中搜索到的术语,它会返回大量条目。

如果有人在示例程序中输入了 150 个字符,那么gets() 将在长度为 100 的数组中存储 150 个字符。这永远不会带来快乐 - 它通常会导致核心转储,但需要仔细选择输入- 通常由 Perl 或 Python 脚本生成 - 您可能可以让程序执行任意其他代码。如果程序将由具有“提升权限”的用户运行,这真的很重要。

顺便说一句,gets() 可能会在下一个版本中从标准 C 库中删除(C1x - 请参阅来自WG14 的 n1494)。它不会在很长一段时间内(20 年?)从实际的 C 库中消失,但应该用这个实现(或类似的东西)替换它:

#undef NDEBUG
#include <assert.h>
char *gets(char *buffer)
{
    assert("Probability of using gets() safely" == 0);
}

另一个小细节,在主要问题的 cmets 部分讨论。

显示的代码显然是针对 C99 的; length 在函数中的声明在 C89 中是无效的。鉴于此,main() 函数不显式返回值是“可以的”,因为 C99 标准遵循 C++ 标准,允许您省略来自 main() 的返回,效果与return(0); 或 return 0; 最后。

因此,这个问题中的程序不能因为最后没有return 而被严格地指责为错误。然而,我认为这是更特殊的标准化决定之一,如果标准将这一规定排除在外,或者做一些更激进的事情,比如允许无处不在但错误的void main(),观察到当控制权从那里恢复时,我会更喜欢它,结果是成功状态返回给环境。改变标准的这方面是不值得的 - 遗憾的是 - 但作为个人风格决定,我不会利用授予的许可从main()中省略最后的return。如果代码必须与 C89 编译器一起使用,它应该在末尾有显式的 return 0;(但是 length 的声明也必须修复)。

【讨论】:

    【解决方案2】:

    你也可以使用递归来做到这一点。我认为它看起来比使用循环时更好。

    只需用你的字符串调用方法,在方法中打印字符之前,用相同的字符串再次调用方法,减去第一个字符。

    这将以相反的顺序打印出你的字符串。

    【讨论】:

    • 如果您的内存不紧张并且字符串很小,这很有效。对于任何非平凡大小的字符串,您将获得堆栈和内存使用量的爆炸式增长。
    【解决方案3】:

    第一:

    永远不要永远不要使用gets();它会在您的代码中引入一个故障点。没有办法告诉gets() 目标缓冲区有多大,所以如果你传递一个大小可以容纳 10 个字符的缓冲区并且输入流中有 100 个字符,gets() 会很乐意将这额外的 90 个字符存储在内存之外缓冲区结束,可能会破坏一些重要的东西。缓冲区溢出是一种简单的恶意软件利用; Morris 蠕虫专门利用了 sendmail 中的gets() 调用。

    请改用fgets();它允许您指定从输入流中读取的最大字符数。但是,与gets() 不同,fgets() 会将终止换行符保存到缓冲区(如果有空间),因此您必须考虑到这一点:

    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是整数类型)。要将单个字符写入标准输出,请使用 putchar() 或 fputc()。

    【讨论】:

      【解决方案4】:

      您应该使用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 传递给需要整个字符串(char 数组,NULL 终止字符串)的函数时,编译器会感到困惑。

      【讨论】:

        【解决方案5】:

        使用putc 或putchar,因为puts 被指定为使用char*,而您正在为它提供char。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2016-04-11
          • 1970-01-01
          • 1970-01-01
          • 2021-12-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多