Oracle Solaris Studio 12.2: C ユーザーガイド

6.2.3 併用に関する考慮点

関数プロトタイプ宣言と古い形式の関数定義がともに機能するためには、両方が機能的に同じインタフェースを指定しなければいけません。つまり、ANSI/ISO C の用語を使用する「互換形式」を持っていなければいけません。

可変引数を持つ関数の場合は、ANSI/ISO C の省略記号と古い形式の varargs() 関数定義を併用することはできません。固定数のパラメータを持つ関数の場合、以前の実装で渡したとおりのパラメータの型を指定するだけです。

K&R C では、各引数は、呼び出された関数に渡される直前に、デフォルトの引数拡張に従って変換されました。 このような拡張では、int より狭いすべての整数型を int サイズに拡張し、また、任意の float 引数を double に拡張するように指定されていたため、コンパイラとライブラリの両方が単純化されていました。関数プロトタイプを使用すると、よりわかりやすく表現できます。つまり、指定したパラメータの型が、そのまま、関数に渡されるパラメータの型となります。

したがって、既存の (古い形式の) 関数定義用に関数プロトタイプを書く場合、関数プロトタイプに次の型のパラメータは使用できません。

char

signed char

unsigned char

float

short

signed short

unsigned short

 

プロトタイプを書く際には、さらに 2 つの問題があります。typedef 名と、狭い unsigned 型の拡張規則です。

古い形式の関数内のパラメータが typedef 名で宣言されている場合 (off_tino_t など)、typedef 名がデフォルトの引数拡張によって影響を受ける型を指しているかどうかを確認することが重要です。前述の 2 つの typedef 名を例にすると、off_tlong です。したがって、関数プロトタイプで使用することは適切な使用方法です。しかし、ino_tunsigned short であったため、プロトタイプで使用すると、古い形式の定義とプロトタイプが異なる互換性のないインタフェースを指定するため、診断メッセージが発行されます。

最後の問題は、unsigned short の代わりに何を使用するかです。K&R C と 1990 ANSI/ISO C コンパイラ間の最大の非互換性の 1 つは、unsigned charunsigned shortint 値に広げるための拡張規則です (「6.4 拡張: 符号なし保存と値の保持」を参照)。このような古い形式のパラメータにあたる型は、コンパイル時に使用するコンパイルモードによって異なります。

最良の方法は、int または unsigned int のどちらかを指定するように古い形式の定義を変更して、一致する型を関数プロトタイプで使用することです。必要であれば、関数を入力したあとでも、より狭い型の値を局所変数に代入できます。

前処理によって影響を受ける可能性のあるプロトタイプでは、ID の使用に気を付けてください。次の例を考えてみましょう。


#define status 23
void my_exit(int status);   /* Normally, scope begins */
                            /* and ends with prototype */

関数プロトタイプは、狭い型を持つ古い形式の関数定義と併用できません。


void foo(unsigned char, unsigned short);
void foo(i, j) unsigned char i; unsigned short j; {...}

__STDC__ を適切に使用すれば、古いコンパイラと新しいコンパイラの両方で使用できるヘッダーファイルを作成できます。


header.h:
    struct s { /* .  .  .  */ };
    #ifdef __STDC__
       void errmsg(int, ...);
       struct s *f(const char *);
       int g(void);
    #else
      void errmsg();
      struct s *f();
      int g();
    #endif

次の関数はプロトタイプを使用していますが、古いシステムでもコンパイルできます。


struct s *
#ifdef __STDC__
    f(const char *p)
#else
    f(p) char *p;
#endif
{
    /* .  .  .  */
}

次の例は、更新したソースファイルです (選択肢 3 を使用したもの)。局所関数は古い形式の定義を使用していますが、新しいコンパイラ用にプロトタイプも含まれています。


source.c:
   #include “header.h”
      typedef /* .  .  .  */ MyType;
   #ifdef __STDC__
      static void del(MyType *);
      /* .  .  .  */
      static void
      del(p)
      MyType *p;
      {
      /* .  .  .  */
      }
      /* .  .  .  */