名前修飾

名前修飾(ネームマングリング)とは



名前修飾(name mangling)とは、プログラミング言語処理系において、サブルーチン(関数)名などのシンボル名を、その表層的な名前だけでなく、引数や返り値の型といった意味的な情報を含めて変換する技術です。この手法は、特に多重定義(オーバーロード)をサポートする言語で不可欠であり、コンパイラ、リンカ、デバッガなどのシステム全体で一貫したシンボル管理を可能にします。

名前修飾の必要性



現代のプログラミング言語は、関数の多重定義やテンプレートなど、高度な機能を提供しています。これらの機能を実装するためには、同じ名前の関数でも、引数の型や数が異なれば、別の関数として区別する必要があります。名前修飾は、この区別を実現するための重要な役割を担っています。

たとえば、C++では、同じ名前の関数でも引数の型が異なる場合、コンパイラは内部的に異なる名前を生成します。これにより、リンカはどの関数を呼び出すべきかを正確に判断できます。また、デバッガは、プログラムの実行中にシンボル名を正しく解釈し、デバッグを支援できます。

名前修飾の具体的な例



名前修飾の具体的な例を見てみましょう。C++では、次の2つの関数は、名前は同じですが、引数の型が異なります。

cpp
void f(int x);
void f(double x);


コンパイラは、これらの関数をそれぞれ異なるシンボル名に変換します。例えば、`f(int)`は`_Z1fi`、`f(double)`は`_Z1fd`のように変換されることがあります。

Microsoft Windowsの場合


Microsoft Windowsでは、CやPascalなどの言語では、通常は関数の多重定義をサポートしないため、名前修飾は必須ではありません。しかし、呼び出し規約(サブルーチンとデータのやり取りの方法)が複数存在する場合、名前修飾によって呼び出し規約を詳細に記述します。Microsoftによって確立された名前修飾スキームがあり、他のコンパイラも非公式にこれに従っています。例えば、`_cdecl`はCの標準の呼び出し規約を示す修飾子です。

例えば、`stdcall`と`fastcall`では、関数名は`_名前@X`や`@名前@X`のようにエンコードされ、`X`にはスタックに積まれる引数のバイト数が格納されます。

C++の場合


C++は、名前修飾が最も広く用いられている言語の一つですが、標準化が進んでいません。C++コンパイラは、クラス、テンプレート、演算子オーバーロードなどの情報を格納する必要があるため、名前修飾が複雑になりがちです。このため、異なるコンパイラでコンパイルされたオブジェクトコードは、通常リンクできません。

GNU GCCの例


GNU GCCでは、名前修飾されたシンボルは`_Z`で始まり、ネストされた名前は`N`と長さと識別子のペアでエンコードされます。例えば、`wikipedia::article::format`は`_ZN·9wikipedia·7article·6format·E`と修飾されます。さらに、関数の場合は型情報も追加されます。例えば、`format()`がvoid関数であれば、`_ZN·9wikipedia·7article·6format·E·v`となります。

コンパイラによる名前修飾の相違



C++では、名前修飾の標準スキームが存在しないため、コンパイラやバージョン、プラットフォームによって修飾規則が異なります。そのため、同じ関数でもコンパイラによって異なる名前修飾が行われる場合があります。これは、異なるコンパイラでコンパイルされたコードをリンクする際に問題となることがあります。

C++からリンクする際のCシンボルの扱い



C++からCのシンボルをリンクする際には、`extern "C"`を使用します。これにより、C++コンパイラは、Cのシンボルに対して名前修飾を行わずに、そのままの名前でリンクします。Cは名前修飾を使用しないため、C++コンパイラもこれらの識別子を参照する際に名前修飾を避ける必要があります。

C++での名前修飾の標準化の困難性



C++では、名前修飾の標準化は困難です。名前修飾は、ABI(アプリケーションバイナリインタフェース)や例外処理、仮想関数テーブルのレイアウトなど、他の複雑な要素とも密接に関連しています。さらに、特定の修飾法を決定すると、実装が制限される可能性や、名前修飾を必要としない実装を妨げる可能性もあります。そのため、C++の標準では、名前修飾を標準化することは特に目指されていません。

C++名前修飾問題の現実的な影響



C++では、名前修飾スキームが異なる場合、コンパイルされたライブラリをリンクする際に問題が生じます。複数のコンパイラが混在する環境では、ライブラリを複数のコンパイラでコンパイルする必要があり、これは大きな負担となります。

Javaの場合



Javaでは、言語、コンパイラ、.classファイルフォーマットが同時に設計されたため、名前修飾のような問題は発生しません。しかし、内部クラスや無名クラスに対して一意の名前を与えるために、名前の変換が行われます。例えば、内部クラス`Foo.bar`は`Foo$bar.class`のように変換されます。

Java Native Interface (JNI) を使用する際には、Javaとネイティブコードの間で名前の変換が必要になる場合があります。

Pythonの場合



Pythonでは、識別子の先頭に2つのアンダースコアを付けることで、プライベートな名前を示すことができます。Pythonコンパイラは、これらの名前をクラス名で修飾し、大域的なシンボルとして扱います。

その他の言語



Turbo Pascal/Delphiでは、`exports`句を使用して名前修飾を抑制できます。Objective-Cでは、クラスメソッドとインスタンスメソッドに対して特定の名前修飾が行われますが、セレクタという仕組みでメソッドを効果的に扱います。

まとめ



名前修飾は、現代のプログラミング言語において、多重定義やテンプレートなどの高度な機能を実装するために不可欠な技術です。コンパイラやリンカ、デバッガといったシステム全体で一貫したシンボル管理を可能にし、異なるコンパイラや言語間の連携を円滑にします。しかし、C++のように名前修飾の標準化が進んでいない言語では、ABIの問題や、異なるコンパイラ間での互換性の問題が発生する可能性があり、注意が必要です。

もう一度検索

【記事の利用について】

タイトルと記事文章は、記事のあるページにリンクを張っていただければ、無料で利用できます。
※画像は、利用できませんのでご注意ください。

【リンクついて】

リンクフリーです。