我不确定用于描述此问题的正确术语,但我会尽力而为。
我正在编写一个玩具程序,在test.c中打印出12和13的阶乘:
#include <stdio.h>
int main(void)
{
printf("%ld\n", factorial(12));
printf("%ld\n", factorial(13));
return 0;
}
factorial函数在source fact.c的共享库中定义。
long factorial(int n)
{
long r = 1;
while (n > 1) r *= n--;
return r;
}
我正在使用以下命令编译共享库和我的程序:
$ gcc -shared -fPIC -o libfact.so fact.c
$ gcc -L. -lfact test.c
我在x86-64上,所以我希望factorial(13)
(13!= 6227020800)不会溢出64位长。但是,我得到了一个奇怪的结果。
$ LD_LIBRARY_PATH=. ./a.out
479001600
1932053504
这里,1932053504恰好是正确结果的低32位的十进制值。但是,如果我在test.c的顶部插入函数原型long factorial(int)
并重新编译,我会得到正确的结果。
$ LD_LIBRARY_PATH=. ./a.out
479001600
6227020800
这给我留下了几个问题:
factorial
。这与此question about C void arguments和此question about function prototypes?答案 0 :(得分:3)
从1990版C标准开始,如果调用没有可见声明的函数,编译器会假定它返回类型int
。因此,对于主程序中对factorial()
的调用,编译器很可能会将其返回的long
值解释为,就像它是int
值一样。如果long
和int
碰巧具有相同的表示形式,则可能会起作用。如果long
宽于int
,则可能正常工作,或者可能失败。但无论如何,这种行为都是不确定的。
1999版C标准(C99)删除了“隐式整数”规则。调用没有可见声明的函数是约束违规,需要编译器诊断。然后,编译器可以拒绝该程序,也可以继续编译它 - 但如果确实如此,则行为是未定义的。
要更正此问题,您需要在之前显示factorial()
的可见声明。最好的方法是创建一个头文件factorial.h
:
<强> factorial.h:强>
#ifndef FACTORIAL_H
#define FACTORIAL_H
long factorial(int n);
#endif
<强> factorial.c:强>
#include <stdio.h>
#include "factorial.h"
long factorial(int n)
{
printf("factorial(%d)", n);
long r = 1;
while (n > 1) r *= n--;
printf(" --> %ld\n", r);
return r;
}
<强> main.c中:强>
#include <stdio.h>
#include "factorial.h"
int main(void)
{
printf("%ld\n", factorial(12));
printf("%ld\n", factorial(13));
return 0;
}
请注意,如果long
仅为32位,则仍然将不起作用,因为13阶乘超过2 31 -1。检查系统上LONG_MAX
和/或sizeof (long)
的值:
printf("LONG_MAX = %ld, sizeof (long) = %d\n", LONG_MAX, (int)sizeof (long));
考虑使用long long
而不是long
- 或者更好的是int64_t
或uint64_t
,<stdint.h>
中定义。
(据我所知,您使用共享库不会影响其中任何一项。)
答案 1 :(得分:1)
int
。在这种情况下发生的强制转换解释了printf
语句的有线输出。无论如何,更喜欢使用long long int
,因为long int
在标准中定义为最小值2^31 - 1
§5.2.4.2.1
- long int
类型的对象的最大值LONG_MAX +2147483647 // 2 ^ 31-1
答案 2 :(得分:-1)
long
在64位架构上不必是8个字节,标准要求它不小于4个字节。你有溢出。