为什么gets(stdin)返回一个整数?和其他错误

问题描述 投票:3回答:3

我是C编程的新手(尽管我有Java经验)。在阅读了一些教程之后,我决定开始在Coderbyte上解决编码挑战。

我尝试的第一个挑战是this one

挑战

让函数FirstFactorial(num)接受传递的num参数并返回它的阶乘。例如:如果num = 4,则程序应返回(4 * 3 * 2 * 1)= 24.对于测试用例,范围将介于1和18之间,输入将始终为整数。

样本测试案例

输入:4 产量:24

输入:8 输出:40320

我的解决方案

#include <stdio.h>

void FirstFactorial(int num[]) {

  int i = num -1;

  for(i ; i > 0; i--) {
    num = num * i;
    printf("%d",i);
  }

  printf("\t %d", num);
}

int main(void) {

  // disable stdout buffering
  setvbuf(stdout, NULL, _IONBF, 0);

  // keep this function call here
  FirstFactorial(gets(stdin));
  return 0;

}

输入参数的值:8

错误信息:

main.c: In function 'FirstFactorial':
main.c:5:11: warning: initialization makes integer from pointer without a cast [-Wint-conversion]
   int i = num -1;
           ^~~
main.c:8:15: error: invalid operands to binary * (have 'int *' and 'int')
     num = num * i;
               ^
main.c: In function 'main':
main.c:23:18: warning: passing argument 1 of 'FirstFactorial' makes pointer from integer without a cast [-Wint-conversion]
   FirstFactorial(8);
                  ^
main.c:3:6: note: expected 'int *' but argument is of type 'int'
 void FirstFactorial(int num[]) {
      ^~~~~~~~~~~~~~

exit status 1

所以似乎有一些问题,我有几个问题:

  1. 我从未听说过gets(stdin)。我查了gets(),glibc文档说这个函数返回一个char*。我怎样才能将它传递给需要int的函数?
  2. 看起来像 int i = num -1; i初始化为4而不是7.为什么?
  3. for循环似乎正在递减ii = 7,6,5,4,3,2,1)。但是这句话: num = num * i; 正在生成错误。这有什么问题?它看起来像是正常的乘法。
c compiler-errors standard-library gets
3个回答
5
投票

为了未来的访客:

这是对Coderbytes用于“方便”的语言的可怕滥用。 gets(stdin)应该永远不会起作用:类型不起作用。

实际发生的是Coderbytes在将代码发送到编译器之前,盲目地使用您作为输入提供的文字字符串来查找和替换gets(stdin)的第一个实例。这甚至不是预处理器宏,它是源上的盲目替代。

因此,虽然你不应该在现实中这样做,但在Coderbytes上它是一个必要的恶魔:这似乎是唯一支持将输入放入程序的方法。

Source


此外,如果你想要一些娱乐,请尝试清除所有其他内容并将其放入Coderbytes:

int main(){
    printf("%s", "This is a literal string containing gets(stdin) along with other words");
}

你会看到甚至在字符串文字中也会发生替换!


3
投票

忽略gets是危险的,因此根据Why is the gets function so dangerous that it should not be used?完全从C语言中删除,以下是您的问题的答案:

  1. 我从未使用过gets(stdin)。我在C-Library参考上检查了它。看起来它会返回一个字符(是的用户输入)。为什么我可以将它作为整数传递给函数?

没有人使用过gets(stdin),因为它期望参数是指向存储结果的字符缓冲区的指针,而不是stdin。与fgets不同,gets只能从stdin读取,并且默认设置为stdin - 你不能改变它。

你无法将它传递给期待int[]的函数。您的编译器必须在此处提供诊断消息,因为从char*返回的getsint[]不兼容。如果您的编译器没有给出这样的消息,那么它就会被破坏,不应该被使用。

gcc编译器确实在这里给出了一条消息,而不是你引用的消息。它闻起来好像你在gnu90(“垃圾模式”)中运行gcc,不建议初学者使用。请参阅本答案的底部,了解如何运行它。

  1. 它看起来像int i = num -1;我初始化为4而不是7.我不明白为什么?

该行无效C.在这种情况下,num是一个数组,由于它是一个函数参数,因此可以调整为int*类型。 num - 1因此给出指针算术,这不是你想要的。结果是int*类型。您不能将结果类型为int*的表达式分配给int。您的编译器必须再次提供诊断消息,并且它正确执行:

warning: initialization makes integer from pointer without a cast

如果它产生了可执行文件,尽管上面的消息,该程序的行为是未定义的,因为它是无效的C,然后任何事情都可能发生。

  1. 但它看起来像声明:num = num * i;不起作用。

出于与上述相同的原因,num被声明为数组,因此您无法以任何合理的方式对其进行算术运算。


总的来说,你不能通过“猜测试验和错误”来编程,编程不会这样。你必须真正知道每一行代码的作用。我强烈建议您将编译器警告调到最高级别,并确保在运行程序之前没有警告:

gcc -std=c11 -pedantic-errors -Wall -Wextra

0
投票

如前所述,你不应该使用gets(),因为它非常容易出现缓冲区溢出(你肯定需要了解它);正如其他人所提到的,更好的选择是fgets。它更好的原因是因为它只会写入缓冲区,最多可以指定为长度的字节数;如果它没有停止,那么它将继续写入缓冲区的末尾到其他内存,这是不好的。这会导致很多安全问题,而且会导致严重崩溃。

处理代码中的其他问题如下:

在C类型中,比在Java中使用的类型更流畅。变量只是记忆中的一个点;你可以将内存中的那个点解释为不同的东西,C允许你很容易地在类型之间进行转换,如果你不小心的话可能会咬你。

C中的数组总是指针或内存地址,因此对于int num[]num是指针,而不是int,它就像int* num。这就是为什么当你尝试对num和int变量进行算术运算时,你的编译器会给你警告。这意味着你从gets或fgets中获得的是一个字符串,而不是一个int,所以将get的原始输出传递给FirstFactorial会给你带来垃圾。您需要做的是获取字符串表示的整数。

gets返回的内容可以用作int,因为它是一个char *,它是一个地址,可以被“解释”为一个整数(因为它实际上是一组字节,其中包含一个指向内存的数字)地址)。因为它们都只是位串,编译器可以用这种方式解释它们,但会警告你,你可能在语义上并不意味着你要告诉它做什么。

字符串实际上只是内存中的数字“代表”字符的字形(就像ASCII是:http://ee.hawaii.edu/~tep/EE160/Book/chap4/subsection2.1.1.1.html)你在做什么就像这个FirstFactorial('4'),你想要的是FirstFactorial(4)。将字符串'4'转换为4的最佳方法是使用strtol(这里有示例:https://www.techonthenet.com/c_language/standard_library_functions/stdlib_h/strtol.php),但是atoi稍微容易一些,作为一个例子,它在语义上做你的代码所做但更安全和编译:

#include <stdio.h>

void FirstFactorial(int num) {

    int i = num -1;

    for(i ; i > 0; i--) {
        num = num * i;
    //        printf("%d",i);
    }

   printf("\t %d", num);
}

#define BUFFER_LENGTH 60
int main(void) {

   char str[BUFFER_LENGTH];

   // disable stdout buffering
   if( fgets (str, BUFFER_LENGTH, stdin)!=NULL ) {
       int num = atoi(str);
       FirstFactorial( num );
   }
   // keep this function call here
   return 0;

} 

注意#define,这是一个预处理器宏,它有助于保持缓冲区大小和传递给fgets的长度之间的共同点。如果您决定需要更改缓冲区的大小,这会有所帮助;你可能会改变缓冲区大小(比如40),但忘记改变传递给fgets的长度;你会回到fgets写入最多60个字节到缓冲区的问题,这个问题只有40个字节,这意味着你可以覆盖20个字节的其他内存,这又是坏事。

在C上获取一本现代书籍会非常有帮助,一位导师或导师帮助您解决这些差异可能非常有利于节省时间和避免一些容易出现的陷阱并导致代码中出现问题。如果没有关于为什么要做或不做的事情的一些指导,C有点难以获得最佳实践。

© www.soinside.com 2019 - 2024. All rights reserved.