JavaScript is required to for searching.
ナビゲーションリンクをスキップ
印刷ビューの終了
Oracle Solaris Studio 12.2: C ユーザーガイド
search filter icon
search icon

ドキュメントの情報

はじめに

1.  C コンパイラの紹介

2.  C コンパイラ実装に固有の情報

3.  C コードの並列化

4.  lint ソースコード検査プログラム

5.  型に基づく別名解析

6.  ISO C への移行

6.1 基本モード

6.1.1 -Xc

6.1.2 -Xa

6.1.3 -Xt

6.1.4 -Xs

6.2 古い形式の関数と新しい形式の関数の併用

6.2.1 新しいコードを書く

6.2.2 既存のコードを更新する

6.2.3 併用に関する考慮点

6.3 可変引数を持つ関数

6.4 拡張: 符号なし保存と値の保持

6.4.1 背景

6.4.2 コンパイルの動作

6.4.3 例 1 : キャストの使用

6.4.4 ビットフィールド

6.4.5 例 2 : 同じ結果

6.4.6 整数定数

6.4.7 例 3 : 整数定数

6.5 トークン化と前処理

6.5.1 ISO C の翻訳段階

6.5.2 古い C の翻訳段階

6.5.3 論理的なソース行

6.5.4 マクロ置換

6.5.5 文字列の使用

6.5.6 トークンの連結

6.6 const と volatile

6.6.1 右辺値 (lvalue) 専用の型

6.6.2 派生型の型修飾子

6.6.3 const は readonly を意味する

6.6.4 const の使用例

6.6.5 volatile は文字どおりの解釈を意味する

6.6.6 volatile の使用例

6.7 複数バイト文字とワイド文字

6.7.1 アジア言語は複数バイト文字を必要とする

6.7.2 符号化の種類

6.7.3 ワイド文字

6.7.4 変換関数

6.7.5 C 言語の機能

6.8 標準ヘッダーと予約名

6.8.1 標準ヘッダー

6.8.2 実装で使用される予約名

6.8.3 拡張用の予約名

6.8.4 安全に使用できる名前

6.9 国際化

6.9.1 ロケール

6.9.2 setlocale() 関数

6.9.3 変更された関数

6.9.4 新しい関数

6.10 式のグループ化と評価

6.10.1 定義

6.10.2 K&R C の再配置の権利

6.10.3 ISO C の規則

6.10.4 括弧

6.10.5 as if 規則

6.11 不完全な型

6.11.1 型

6.11.2 不完全な型を完全にする

6.11.3 宣言

6.11.4 式

6.11.5 正当性

6.11.6 例

6.12 互換型と複合型

6.12.1 複数の宣言

6.12.2 分割コンパイル間の互換性

6.12.3 単一のコンパイルでの互換性

6.12.4 互換ポインタ型

6.12.5 互換配列型

6.12.6 互換関数型

6.12.7 特別な場合

6.12.8 複合型

7.  64 ビット環境に対応するアプリケーションへの変換

8.  cscope: 対話的な C プログラムの検査

A.  機能別コンパイラオプション

B.  C コンパイラオプションリファレンス

C.  ISO/IEC C 99 の処理系定義の動作

D.  C99 でサポートされている機能

E.  ISO/IEC C90 の処理系定義の動作

F.  ISO C データ表現

G.  パフォーマンスチューニング

H.  K&R Solaris Studio C と Solaris Studio ISO C の違い

索引

6.2 古い形式の関数と新しい形式の関数の併用

1990 ISO C 規格での最大の変更点は、C++ 言語の機能である関数プロトタイプを使用できることです。各関数にパラメータの数と型を指定することにより、すべての通常のコンパイルにおいて、関数呼び出しごとに (lint のように) 引数とパラメータが検査されるだけではなく、引数が (代入だけで) 自動的に関数が期待する型に変換されます。プロトタイプを使用するように変更できる (また、変更すべき) 既存の C コードが非常に多く存在するため、1990 ISO C 規格には、古い形式と新しい形式の関数宣言を併用する規則が含まれています。

1999 ISO C 規格によって、古い形式の関数宣言は廃止されました。

6.2.1 新しいコードを書く

まったく新しいプログラムを書くとき、ヘッダーでは、新しい形式の関数宣言 (関数プロトタイプ) を使用し、それ以外の C ソースファイルでは、新しい形式の関数宣言と関数定義を使用します。しかし、ISO C 以前のコンパイラを持つマシンにコードを移植する可能性がある場合は、ヘッダーとソースファイルの両方で、マクロ __STDC__ (ISO C コンパイルシステム専用に定義されている) を使用することをお勧めします。例については、「6.2.3 併用に関する考慮点」を参照してください。

同じオブジェクトまたは関数に対して 2 つの互換性のない宣言が同じスコープの中にある場合、ISO C 準拠のコンパイラは診断メッセージを発行しなければいけません。すべての関数がプロトタイプで宣言および定義され、適切なヘッダーが正しいソースファイルにインクルードされている場合、すべての呼び出しは関数の定義に従うはずです。この取り決めによって、起こりがちな C プログラミングの誤りを防ぐことができます。

6.2.2 既存のコードを更新する

既存のアプリケーションで関数プロトタイプを利用したい場合、どれくらいのコードを変更するかによって、更新方法が異なります。

  1. 変更せずに再コンパイルする

    コードを変更しなくても、-v オプションでコンパイラを実行すると、パラメータの型と数の不一致について警告が発行されます。

  2. ヘッダーだけに関数プロトタイプを追加する

    大域的な関数へのすべての呼び出しが診断の対象になります。

  3. ヘッダーには関数プロトタイプを追加し、各ソースファイルの先頭には局所 (静的な) 関数に対する関数プロトタイプを追加する

    関数へのすべての呼び出しが診断の対象になります。ただしこの方法では、ソースファイル内で局所関数ごとに 2 回インタフェースを入力する必要があります。

  4. すべての関数宣言と関数定義を、関数プロトタイプを使用するように変更する

結果として受ける恩恵とそのための負荷を考えると、ほとんどの場合、前述の 2 か 3 が適切な選択だと言えるでしょう。ただしこれらを選択する場合、古い形式と新しい形式を併用するための規則を詳細に知っておく必要があります。

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_t や ino_t など)、typedef 名がデフォルトの引数拡張によって影響を受ける型を指しているかどうかを確認することが重要です。前述の 2 つの typedef 名を例にすると、off_t は long です。したがって、関数プロトタイプで使用することは適切な使用方法です。しかし、ino_t は unsigned short であったため、プロトタイプで使用すると、古い形式の定義とプロトタイプが異なる互換性のないインタフェースを指定するため、診断メッセージが発行されます。

最後の問題は、unsigned short の代わりに何を使用するかです。K&R C と 1990 ANSI/ISO C コンパイラ間の最大の非互換性の 1 つは、unsigned char と unsigned short を int 値に広げるための拡張規則です (「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;
      {
      /* .  .  .  */
      }
      /* .  .  .  */