0

この問題を説明するために使用する正しい用語がわかりませんが、最善を尽くします。

test.c で 12 と 13 の階乗を出力するおもちゃのプログラムを書いています。

#include <stdio.h>

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

階乗関数は、ソースの 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 ビットの 10 進数値です。long factorial(int)ただし、 test.c の先頭に関数プロトタイプを挿入して再コンパイルすると、正しい結果が得られます。

$ LD_LIBRARY_PATH=. ./a.out
479001600
6227020800

これにより、いくつかの質問が残ります。

  1. test.c でプロトタイプを指定しない場合、想定される戻り値の型は何ですか? このコンパイラは依存していますか?
  2. プロトタイプがなければ、任意の数の引数を に渡すことができるようですfactorialこれは、C void 引数に関するこの質問と関数プロトタイプに関するこの質問に関連していますか?
  3. リンカーが未定義の参照エラーをスローしないのはなぜですか?
4

3 に答える 3

3

1990 年バージョンの C 標準では、可視宣言なしで関数を呼び出すと、コンパイラはそれが type を返すと想定しますint。そのため、メイン プログラムでの呼び出しのfactorial()場合、コンパイラは、long返さた値を値であるかのように解釈する可能性が高くなりintます。longintがたまたま同じ表現を持っている場合、これは機能する可能性があります。longが よりも広い場合、intたまたま機能するか、失敗する可能性があります。しかし、動作はどちらの方法でも未定義です。

C 標準 (C99) の 1999 年版では、"implicit int" ルールが削除されました。宣言が表示されていない関数の呼び出しは制約違反であり、コンパイラの診断が必要です。コンパイラは、プログラムを拒否するか、コンパイルを続行する可能性がありますが、拒否した場合の動作は未定義です。

これを修正するには、呼び出す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;
}

13 階乗は 2 31 -1を超えるため、 が 32 ビットのみの場合、これはまだ機能しないことに注意してください。システムのand/orの値を確認します。longLONG_MAXsizeof (long)

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

で定義されている-- または、さらに良いのは、orlong longではなく、使用を検討してください。longint64_tuint64_t<stdint.h>

(私の知る限り、共有ライブラリの使用はこれに影響しません。)

于 2012-11-05T07:35:45.287 に答える
1
  • 関数の戻り値の型が不明な場合は、コンパイラによって と見なされますint。この場合に発生するキャストは、printfステートメントのワイヤード出力を説明しています。とにかく、標準で定義されているように、最大​​値最小値として処理することをお勧めしlong long intますlong int2^31 - 1

§5.2.4.2.1

— long int 型のオブジェクトの最大値

LONG_MAX +2147483647 // 2^31−1

  • リンカーは、関数が実際に存在するかどうかを動的ライブラリーでチェックインしないため、未定義の参照エラーをスローしません。それをバインドし、実行時にロードしようとします。
于 2012-11-04T22:38:33.613 に答える
-1

long64 ビット アーキテクチャでは 8 バイトである必要はありません。標準では 4 バイト以上である必要があります。オーバーフローがあります。

于 2012-11-04T22:42:05.950 に答える