#define和声明之间有什么区别?

4
#include <stdio.h>
#include <math.h>

// #define LIMIT 600851475143
int isP(long i);
void run();
// 6857
int main()
{
    //int i = 6857;
    //printf("%d\n", isP(i));
     run();
}

void run()
{
long LIMIT = 600851475143;
// 3, 5
// under 1000

long i, largest =1, temp=0;
for(i=3; i<=775147; i+=2)
{
    temp = ((LIMIT/i)*i);
if(LIMIT == temp)
    if(isP(i)==1)
            largest = i;
}
printf("%d\n",largest);
}

int isP(long i)
{
long j;
for(j=3; j<= i/2; j+=2)
    if(i == (i/j)*j)
        return 0;

return 1;

}

我刚遇到了一个有趣的问题。如上所示,这段代码的设计目的是计算LIMIT内最大的质数。但是,上面的程序给出了错误的答案29。

然而,不可思议的是,当我定义了LIMIT值(而不是将其声明为long类型)时,它可以给我正确的值:6857。

有人能帮我找出原因吗?非常感谢!


1
这里运行良好?Clang 4.0。可能是因为我的“long”是64位的。 - James M
@JamesMcLaughlin 是的 - 在许多平台上,long 是一个32位有符号整数,可能会失败,但这取决于平台/编译器。 - Reed Copsey
你需要加上 #define LIMIT 600851475143L,参考这个回答:https://dev59.com/mnM_5IYBdhLWcg3wQQpd - Dampsquid
当整数常量在表达式中使用时,C语言会自动将其提升为所需的大小(除非您明确指定类型)。但如果它不适合,您仍然不能将其分配给long - teppic
谢谢你们以上的所有人! - user2085606
6个回答

3

在许多平台上,long 是一个 4 字节整数,在 2,147,483,647 处会溢出。例如,请参见 Visual C++ 的数据类型范围 页面。

当您使用 #define 时,编译器可以选择一个更合适的类型来保存您非常大的数字。这可能会使它正确地行为,并给出您期望的答案。

然而,总的来说,我建议明确指定数据类型,并尽可能选择一个能够正确表示数字而不需要特定于编译器和平台的行为的数据类型。


非常感谢大家。我过去一直使用Java,其中long类型是完全可以的...我忽略了这些细节,最近遇到了很多难题...我应该更加小心。再次感谢。 - user2085606

1
我会怀疑这里存在数字类型问题。
#define是一个预处理器指令,因此在运行编译器之前,它将在代码中用那个数字替换LIMIT。这为编译器解释该数字留下了空间,但可能不是作为long类型。
在你的情况下,long可能不够大,所以当你使用#define时,编译器选择其他东西。为了保持一致的行为,应指定您知道具有适当范围的类型,而不是依赖于编译器正确猜测。
您还应该在编译器上打开完整警告,它可能能够帮助您检测此类问题。

1

当你输入如下表达式时:

(600851475143 + 1)

一切都很好,因为编译器会自动将这些常量提升到适当的类型(例如在您的情况下是long long),以执行计算。您可以按此方式执行任意数量的表达式。但是当您写:

long n = 600851475143;

编译器试图将一个 long long 类型(或者其他常量隐式转换为的类型)赋值给一个 long 类型,这在你的情况下会导致问题。你的编译器应该会对此发出警告,例如 gcc 会提示:
warning: overflow in implicit constant conversion [-Woverflow]

当然,如果long足够大以容纳该值,则没有问题,因为常量将是与long大小相同或更小的类型。

0

可能是因为 600851475143 大于 LONG_MAX(根据 this,其值为2147483647)


0

尝试用'long long'替换限制类型。目前的状态下,它会环绕(尝试将限制打印为长整型)。预处理器知道这一点并使用正确的类型。


0

你的代码基本上可以归结为这两种可能性:

long LIMIT = 600851475143;
x = LIMIT / i;

对比。

#define LIMIT 600851475143
x = LIMIT / i;

第一种方法相当于将常量转换为long

x = (long)600851475143 / i;

而第二个则会被预编译成:

x = 600851475143 / i;

这里的区别在于:600851475143 对于编译器的 long 类型来说太大了,如果将其强制转换为 long,则会溢出并变得不可控。但是,如果直接在除法中使用它,编译器知道它不适合 long,会自动将其解释为 long long 字面量,i 会被提升,并且除法会作为 long long 进行。

然而,请注意,即使算法似乎大部分时间都有效,您仍然可能在其他地方遇到溢出,因此代码是不正确的。您应该将可能保存这些大值的任何变量声明为 long long


感谢您如此详细的解释! - user2085606

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接