简体   繁体   English

使用共享库时的函数原型

[英]Function prototypes when using a shared library

I'm not sure of the correct terminology to use to describe this problem, but I will try my best. 我不确定用于描述此问题的正确术语,但是我会尽力而为。

I'm writing a toy program that prints the factorial of 12 and 13 in test.c: 我正在编写一个玩具程序,在test.c中打印出12和13的阶乘:

#include <stdio.h>

int main(void)
{
    printf("%ld\n", factorial(12));
    printf("%ld\n", factorial(13));
    return 0;
}

The factorial function is defined in a shared library with source fact.c 阶乘函数在带有源fact.c的共享库中定义

long factorial(int n)
{
    long r = 1;
    while (n > 1) r *= n--;
    return r;
}

I am compiling the shared library and my program with the following commands: 我正在使用以下命令编译共享库和程序:

$ gcc -shared -fPIC -o libfact.so fact.c
$ gcc -L. -lfact test.c

I am on x86-64, so I expect that factorial(13) (13! = 6227020800) does not overflow a 64-bit long. 我使用的是x86-64,因此我希望factorial(13) (13!= 6227020800)不会溢出64位长。 However, I get a strange result. 但是,我得到一个奇怪的结果。

$ LD_LIBRARY_PATH=. ./a.out
479001600
1932053504

Here, 1932053504 happens to be the decimal value of the lower 32 bits of the correct result. 在这里,1932053504恰好是正确结果的低32位的十进制值。 If, however, I insert the function prototype long factorial(int) at the top of test.c and recompile, I get the correct result. 但是,如果我在test.c的顶部插入函数原型long factorial(int)并重新编译,则会得到正确的结果。

$ LD_LIBRARY_PATH=. ./a.out
479001600
6227020800

This leaves me with a couple questions: 这给我留下了两个问题:

  1. What is the assumed return type if I do not specify a prototype in test.c? 如果我未在test.c中指定原型,则假定的返回类型是什么? Is this compiler dependent? 这个编译器依赖吗?
  2. Without a prototype, I seem to be able to pass in any arbitrary number of arguments to factorial . 没有原型,我似乎能够将任意数量的参数传递给factorial Is this related to this question about C void arguments and this question about function prototypes ? 这与有关C void参数函数原型的问题有关吗?
  3. Why doesn't the linker throw an undefined reference error? 链接器为什么不引发未定义的引用错误?

As of the 1990 version of the C standard, if you call a function with no visible declaration the compiler assumes that it returns type int . 从C标准的1990版本开始,如果您调用的函数没有可见的声明,则编译器会假定其返回的类型为int So for the calls to factorial() in your main program, the compiler will most likely interpret the long value it returns as if it were an int value. 因此,对于主程序中的factorial()调用,编译器很可能会将返回的long值解释 int值。 If long and int happen to have the same representation, this is likely to work. 如果longint碰巧具有相同的表示形式,则可能会起作用。 If long is wider than int , it might happen to work, or it can fail. 如果long大于int ,则它可能会起作用,否则可能会失败。 But the behavior is undefined either way. 但是无论哪种方式,行为都是不确定的。

The 1999 version of the C standard (C99) removed the "implicit int" rule. C版本(C99)的1999年版本删除了“隐式int”规则。 Calling a function with no visible declaration is a constraint violation , requiring a compiler diagnostic. 调用没有可见声明的函数是违反约束的 ,需要编译器诊断。 The compiler may then either reject the program, or continue to compile it -- but if it does, the behavior is undefined. 然后,编译器可能拒绝该程序,或者继续对其进行编译-但是,如果这样做,则行为是不确定的。

To correct this, you need to have a visible declaration of factorial() before you call it. 若要更正此问题,您需要调用有一个可见的factorial()声明。 The best way to do this is to create a header file, factorial.h : 最好的方法是创建头文件factorial.h

factorial.h: factorial.h:

#ifndef FACTORIAL_H
#define FACTORIAL_H

long factorial(int n);

#endif

factorial.c: 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: main.c:

#include <stdio.h>
#include "factorial.h"

int main(void)
{
    printf("%ld\n", factorial(12));
    printf("%ld\n", factorial(13));
    return 0;
}

Note that this still won't work if long is only 32 bits, since 13 factorial exceeds 2 31 -1. 请注意,如果long只有32位,则这仍然行不通,因为13阶乘超过2 31 -1。 Check the value of LONG_MAX and/or sizeof (long) on your system: 检查系统上LONG_MAX的值和/或sizeof (long)整数sizeof (long)

printf("LONG_MAX = %ld, sizeof (long) = %d\n", LONG_MAX, (int)sizeof (long));

Consider using long long rather than long -- or, better yet, int64_t or uint64_t , defined in <stdint.h> . 考虑使用long long而不是long -或,更好的是, int64_tuint64_t所定义<stdint.h>

(As far as I know, your use of a shared library doesn't affect any of this.) (据我所知,您对共享库的使用不会对此产生任何影响。)

  • If function return type is unknown, it is considered by the compiler to be int . 如果函数返回类型未知,则编译器将其视为int The cast that occurs in this case explains the wired output of your printf statement. 在这种情况下发生的转换说明了printf语句的有线输出。 Anyhow, prefer using long long int , as long int are defined in the standard to handle as maximal value minimum 2^31 - 1 无论如何,最好使用long long int ,因为标准中将long int定义为最大值2^31 - 1

§5.2.4.2.1 §5.2.4.2.1

— maximum value for an object of type long int — long int类型的对象的最大值

LONG_MAX +2147483647 // 2^31− 1 LONG_MAX +2147483647 // 2 ^ 31− 1

  • The linker does not throw any undefined reference error because it does not check in the dynamic library if the function really exists. 链接器不会引发任何未定义的引用错误,因为它不会在动态库中检查函数是否确实存在。 It binds it, and will try to load it at execution time. 它将绑定它,并尝试在执行时加载它。

long doesn't have to be 8 bytes on 64-bit architecture, standard requires it to be not less than 4 bytes. long在64位体系结构上不必为8个字节,标准要求它不少于4个字节。 You have an overflow. 你溢出了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM