【问题标题】:Perfect Numbers bug?完美数字错误?
【发布时间】:2011-03-01 05:45:59
【问题描述】:

目前我有这个代码,它可以工作。

#include <stdio.h>

int isFactor(long number1, long number2)
{
  int isitFactor = (number2 % number1 == 0)?1:0;
  return isitFactor;
}

int isPerfect(int number)
{
   int counter;
   int sum;
   for (counter = 1; counter < number; counter++) 
      if (isFactor(counter, number)) 
         sum += counter;
   return (sum == number);
}

int main()
{
   int counter = 1;
   for (counter = 1; counter <= 100; counter++)
   {
      printf("", isPerfect(counter));
      if (isPerfect(counter)) 
         printf("%d\n", counter);
   }
}

但是,如果我在 main() 中用 printf 去掉不必要的行,它就无法产生任何数字.... 可能的原因?!

【问题讨论】:

  • “可能的原因?”:不启用编译器警告以使用未初始化的变量,或者如果您确实收到了此类警告,则忽略它们。
  • 如何在 gcc 上启用这些警告?

标签: c debugging math for-loop numbers


【解决方案1】:

函数isPerfect中的变量sum未初始化。

【讨论】:

  • @tekknolagi printf 可能会将 sum 恰好结束的堆栈位置归零。
【解决方案2】:

该代码有两个问题,第一个是sum 未初始化,通常会设置为调用函数时堆栈上发生的任何垃圾。自动变量保证被初始化为零(或任何东西,就此而言),因此,如果您需要它们以特定值开始,您必须自己初始化它们。

第二个问题(现在已通过您的编辑解决)是缺少isFactor。虽然您可能希望它作为一个函数,但下面的代码可以工作,产生两个小于 100 的完美数字,628

#include "stdio.h"

#define isFactor(c,n) ((n % c) == 0)

int isPerfect (int number) {
    int counter;
    int sum = 0;  // <-- note initialisation here.
    for (counter = 1; counter < number; counter++)
        if (isFactor(counter, number)) sum += counter;
    return (sum == number);
}

int main (void) {  // try to use one of the two canonical forms.
    int counter = 1;
    for (counter = 1; counter <= 100; counter++)
        if (isPerfect(counter)) printf("%d\n", counter);
    return 0;
}

而且,看看为什么它可能与额外的 printf 一起工作,这是一个可行的解释。

调用函数时,只需通过减少堆栈指针即可在堆栈上分配局部变量。您的isPerfect 汇编代码可能只有一个序言,例如:

sub %esp,8

然后您将 %esp 的内存用于counter%esp + 4 用于总和。如果不初始化 sum,它将从该内存位置发生的任何事情开始,这可能不为零。

现在想想当你第一次打电话给printf 时会发生什么。毫无疑问,它有自己的局部变量,因此它使用您稍后将依赖的堆栈部分初始化为零。当printf 返回时,它不会将这些内存位置设置回它们之前的值,它只是增加堆栈指针以跳过它们。

然后,当您调用isPerfect 时,这些内存位置很有可能与您调用printf 之前的位置不同,因为printf 一直将它们用于自己的目的。

如果您幸运(或不幸,取决于您的观点),sum 所在的内存位置甚至可能为零。但它仍然是未定义的行为,您应该依赖它 - 显式初始化 sum,您的(立即)问题将结束。


如果这是作业,请随意忽略这一点(事实上,主动忽略它,因为你可能会因为抄袭而被抓到)。这就是我将如何实现isPerfect 函数作为第一次切割的方式。它不会调用函数来计算因子,因为虽然它们通常很快,但它们并非没有成本,而且无论如何都可以在一行 C 中完成一些简单的事情。

int isPerfect (int num) {
  int i, left;
  for (i = 1, left = num; i < num; i++)
    if ((num % i) == 0)
      left -= i;
  return (left == 0);
}

毫无疑问,它可以做得更快,但投资回报在某个点之后会很快下降。

【讨论】:

  • 你的宏很危险。应该是(((n) % (c)) == 0)
  • @Jim。宏函数固有地是危险的,我只是将其作为示例提供。但是,我编写它的方式将包括宏,甚至可能包括一个检查因子的函数 - 请参阅最后一点。
  • @paxdiablo 来吧......那个宏只有你写的方式才是危险的。为参数加上括号是避免不这样做所固有的危险的常见做法。你怎么写或不怎么写无关紧要,重要的是你怎么写,这是一个不好的例子。
  • 抱歉,我的意思是在我之前的评论中,不会包含宏而不是会。而且,如果您不喜欢它, @Jim,投反对票。这与手头的问题无关,因为我明确表示你会更好地使用函数,但你的投票是你认为合适的投票。宏 本质上是危险的,但可以通过使用来缓解。只要您使用单个变量,我的版本就可以了。甚至带括号的版本也不能保护您免受一切侵害(例如,在宏中重复使用单个 n-- 参数)。
  • @paxdiablo 我只是说这很危险并指出了一个改进。我不会把时间浪费在智力上的不诚实上。在 that 宏中,这两个参数都没有被多次使用,这是我明确提到的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-23
  • 1970-01-01
  • 1970-01-01
  • 2015-10-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多