使用共享库时的函数原型

时间:2012-11-04 22:34:49

标签: c gcc linker shared-libraries x86-64

我不确定用于描述此问题的正确术语,但我会尽力而为。

我正在编写一个玩具程序,在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

这给我留下了几个问题:

  1. 如果我没有在test.c中指定原型,假设的返回类型是什么?这个编译器是否依赖?
  2. 如果没有原型,我似乎能够将任意数量的参数传递给factorial。这与此question about C void arguments和此question about function prototypes
  3. 有关
  4. 为什么链接器不会抛出未定义的引用错误?

3 个答案:

答案 0 :(得分:3)

从1990版C标准开始,如果调用没有可见声明的函数,编译器会假定它返回类型int。因此,对于主程序中对factorial()的调用,编译器很可能会将其返回的long值解释为,就像它是int值一样。如果longint碰巧具有相同的表示形式,则可能会起作用。如果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_tuint64_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个字节。你有溢出。