typedefは、プログラミング言語C、C++、Objective-Cの予約語です。別のデータ型の追加名(エイリアス)を作成するために使用されますが、新しい型は作成しません。[1]ただし、配列型の修飾されたtypedefのわかりにくいケースでは、typedef修飾子が配列要素型に転送されます。[2]そのため、構造体と共用体型で構成される複雑なデータ構造を宣言する構文を簡素化するために使用されることがよくありますが、さまざまなサイズの整数データ型に特定の説明的な型名を提供するためにもよく使用されます。[1]
構文
typedef宣言は、他のC識別子を宣言するのと同じ構文に従います。キーワードtypedef自体は指定子であり、通常は宣言の先頭に出現しますが、型指定子の後や2つの型指定子の間に出現することもできます。[3] [4]
C 標準ライブラリおよびPOSIX仕様では、typedef 定義の識別子には、や の_tように という接尾辞が付けられることがよくあります。これは他のコーディング システムでも実践されていますが、POSIX ではこの慣行を POSIX データ型に対して明示的に予約しています。
size_ttime_t
例
lengthこれにより、型 の同義語として
型 が作成されますint。
typedef int長さ;
ドキュメントの使用
typedef宣言は、プログラミングコンテキスト内で変数の意味を示すドキュメントとして使用できます。たとえば、測定単位やカウントの表現を含めることができます。ジェネリック宣言は、
int現在の速度; intハイスコア;
void congratulate ( int your_score ) { if ( your_score > high_score ) { // ... } }
コンテキスト固有の型を宣言することで表現できます。
typedef int km_per_hour ; typedef int points ;
// `km_per_hour` はここでは `int` と同義なので、コンパイラは
新しい変数を整数として扱います。
km_per_hour current_speed ; points high_score ;
void congratulate ( points your_score ) { if ( your_score > high_score ) { // ... } }
コードの両方のセクションは同じように実行されます。ただし、2 番目のコード ブロックで typedef 宣言を使用すると、2 つの変数は同じデータ型を表しているものの、異なるデータまたは互換性のないデータを格納していることが明確になります。 ののint定義は、プログラマーに対して、(または として宣言されていない他の変数) を引数として渡すべきではないことを示しています。両方がデータ型の変数として宣言されていた場合、これはそれほど明白ではありません。ただし、この指示はプログラマーのみを対象としています。C/C++ コンパイラーは両方の変数を 型と見なし、以下のコード スニペットの の
「間違った」引数型に対して型の不一致の警告やエラーをフラグ付けしません。congratulate()your_scorecurrent_speedpointsintintcongratulate(points your_score)
void foo () { km_per_hour km100 = 100 ; congratulate ( km100 ); }
申告の簡素化
typedefは、構造体、共用体、ポインタ型などの冗長な名前を持つ型を持つオブジェクトの宣言を簡素化するために使用できます。[5]たとえば、
構造体MyStruct { int data1 ; char data2 ; };
はデータ型を定義しますstruct MyStruct。C では、typedef がない場合、その型の変数を宣言するには完全な型名を使用する必要があります。
構造体MyStruct ;
typedef宣言は、を含まないより単純な型名を提供することができますstruct。たとえば、型宣言では
typedef struct MyStruct newtype ;
変数宣言は次のように短縮できます。
新しい型a ;
構造体の宣言と typedef を 1 つの宣言に組み合わせることもできます。
typedef struct MyStruct { int data1 ; char data2 ; } newtype ;
タグが宣言されていない場合も含みます:
typedef struct { int data1 ; char data2 ; } newtype ;
C++では、C とは異なりclass、、、、および型のタグ (上記の " " など) はstruct、型宣言の時点で明示的に typedef で宣言されているかのように、完全な型名の別名として自動的に使用できます。
unionenumMyStruct
構造体MyStruct x ; MyStruct y ;
実際、、、および 型の場合class、structC union++ ではタグをクラス名と呼びます。
この C++ 機能と typedef の対応は非常に強力で、ネストされたスコープ内の単純な型名を別の種類のエンティティの識別子として宣言することで、その型名をシャドウすることが可能です。このような場合でも、C スタイルの完全な型名 (「詳細な型指定子」) を使用して、クラスまたは列挙型を参照できます。
C++ では、これらの識別子の他の宣言がない限り、使用できるMyStruct場所であればどこでも使用できますnewtype。ただし、逆は当てはまりません。C++ では、コンストラクター メソッドの名前など、いくつかの目的でクラス名が必要になるためです。
C++でもキーワードが必要となる悪名高い例として、引数に同じ名前の構造体を使用する
POSIX stat システム コールstructがあります。
int stat ( const char *ファイル名, struct stat * buf ) { // ... }
ここでは、CとC++ のstruct両方で、パラメータ定義にキーワードが必要です。 [疑わしい–議論する]
ポインタ
typedef は新しいポインタ型を定義するために使用できます。
typedef int * intptr ;
intptrポインタ;
// 同じ:
// int *ptr;
intptrは、ポインタ型 の新しいエイリアスですint *。定義 は、型 のintptr ptr;変数を定義します。したがって、 は、型 の変数を指すことができるポインタです。
ptrint *ptrint
typedef を使用して新しいポインタ型を定義すると、混乱が生じる場合があります。例:
typedef int * intptr ;
// 'cliff' と 'allen' は両方とも int* 型です。intptr cliff , allen ;
// 'cliff2' は int* 型ですが、 'allen2' は int** 型です。
intptr cliff2 , * allen2 ;
// 次と同じです:
// intptr cliff2;
// intptr *allen2;
上記では、 は、 の両方に 型intptr cliff, allen;の 2 つの変数を定義することを意味しますint*。これは、typedef によって定義された型が型であり、展開ではないためです。言い換えると、intptrは型であり、と のint*両方を修飾します。 の場合、型はと を修飾します。したがって、は 2 つの個別の定義、およびに相当します。は、 が 型のメモリを指すポインタであることを意味します。簡単に言うと、の型は です。
cliffallenintptr cliff2, *allen2;intptrcliff2*allen2intptr cliff2, *allen2;intptr cliff2;intptr *allen2intptr *allen2allen2int*allen2int**
定数ポインタ
繰り返しになりますが、typedef は拡張ではなく型を定義するため、const 修飾子を使用する宣言では予期しない結果や直感に反する結果が生じる可能性があります。次の例では、定数整数へのポインタではなく、整数型への定数ポインタを宣言しています。
typedef int * intptr ;
const intptr ptr = NULL ;
// 以下と同じです:
// int *const ptr = NULL;
定数ポインタなので、宣言時に初期化する必要があります。
構造体と構造体ポインタ
Typedef を使用すると、構造体 ポインタ型 の定義や宣言を簡素化することもできます。次の点を考慮してください。
構造体Node { int data ;構造体 Node * nextptr ; };
typedef を使用すると、上記のコードは次のように書き直すことができます。
typedef struct Node Node ;
構造体Node { int data ; Node * nextptr ; };
C では、構造体とポインタまたは非ポインタを混在させても、1 つのステートメントで同じ型の複数の変数を宣言できます。ただし、各変数をポインタとして指定するには、その変数の前にアスタリスクを付ける必要があります。次のコードでは、プログラマーは がerrptr実際に であると想定しますNode *が、タイプミスにより が であることを意味しますerrptr。Nodeこれにより、微妙な構文エラーが発生する可能性があります。
構造体Node * startptr 、* endptr 、* curptr 、* prevptr 、errptr 、* refptr ;
typedef を定義することでNode *、すべての変数が構造体ポインター型であること、つまり各変数が構造体型を指すポインター型であることが保証されます。
typedef構造体Node * NodePtr ;
NodePtr startptr 、endptr 、curptr 、prevptr 、errptr 、refptr ;
関数ポインタ
int do_math ( float arg1 、int arg2 ) { arg2を返します。}
int call_a_func ( int ( * call_this )( float 、int )) { int output = call_this ( 5.5 、7 );
出力を返す; }
int final_result = call_a_func ( & do_math );
上記のコードは typedef 仕様で書き直すことができます。
typedef int ( * MathFunc )( float , int );
int do_math ( float arg1 、int arg2 ) { arg2を返します。}
int call_a_func ( MathFunc call_this ) { int output = call_this ( 5.5 , 7 );
出力を返す; }
int final_result = call_a_func ( & do_math );
ここで、MathFuncは型の新しいエイリアスです。A は、MathFunc整数を返し、引数として float とそれに続く整数を受け取る関数へのポインターです。
関数が関数ポインタを返す場合、typedef がないとさらに混乱する可能性があります。以下はFreeBSDのsignal(3)の関数プロトタイプです。
void ( *シグナル( int sig 、void ( * func )( int )))( int );
上記の関数宣言は、関数が引数として受け入れるものや、返す型が明確に示されていないため、わかりにくいです。初心者のプログラマーは、関数がint引数として 1 つ受け入れて何も返さないと想定するかもしれませんが、実際には関数ポインターも必要で、別の関数ポインターを返します。より明確に記述できます。
typedef void ( * sighandler_t )( int );
sighandler_tシグナル( int sig 、sighandler_t func );
配列
typedefは配列型の定義を簡略化するためにも使用できます。たとえば、
typedef char arrType [ 6 ];
arrType arr = { 1 、2 、3 、4 、5 、6 }; arrType * pArr ;
// 以下と同じです:
// char arr[6] = {1, 2, 3, 4, 5, 6};
// char (*pArr)[6];
ここで、は 型arrTypeの新しいエイリアスでありchar[6]、6 つの要素を持つ配列型です。 の場合arrType *pArr;、は型pArrのメモリを指すポインタですchar[6]。
型キャスト
typedef は型定義構文を使用して作成されますが、型キャスト構文を使用して作成されたかのように使用できます。(型キャストはデータ型を変更します。) たとえば、次の最初の行の後の各行では、
// `funcptr` は `double` を受け取って `int` を返す関数へのポインタです。
typedef int ( * funcptr )( double );
// C と C++ の両方で有効です。
funcptr x = ( funcptr ) NULL ;
// C++ でのみ有効です。
funcptr y = funcptr ( NULL ); funcptr z = static_cast < funcptr > ( NULL );
funcptrは左側で変数を宣言するために使用され、右側で値をキャストするために使用されます。したがって、typedef は、定義構文を型キャスト構文に変換する方法を理解したくないプログラマーが使用できます。
typedef がなければ、定義構文とキャスト構文を互換的に使用することは通常できません。例:
void * p = NULL ;
// これは正当です。int ( * x )( double ) = ( int ( * )( double )) p ;
// 左側は不正です。int
( * )( double ) y = ( int ( * ) ( double )) p ;
// 右側は不正です。int
( * z )( double ) = ( int ( * p ) ( double ));
C++での使用法
C++ では型名が複雑になることがあり、typedef は型に単純な名前を割り当てるメカニズムを提供します。
std :: vector < std :: pair < std :: string , int >>値;
for ( std :: vector < std :: pair < std :: string , int >>:: const_iterator i = values.begin ( ) ; i != values.end ( ) ; ++ i ) { std :: pair < std :: string , int > const & t = * i ;
// ...
}
そして
typedef std :: pair < std :: string , int > value_t ; typedef std :: vector < value_t > values_t ;
values_t値;
values_t :: const_iterator i = values.begin ( ) ; i ! = values.end ( ) ; ++ i ) { value_t const & t = * i ;
// ...
}
C++11では、usingの代わりにを使ってtypedefを表現できるようになりましたtypedef。例えば、上記の2つのtypedefは次のように同等に記述できます。
value_t = std :: pair < std :: string , int >を使用します。values_t = std :: vector < value_t >を使用します。
テンプレートを使用する
C++03 ではテンプレート化されたtypedefは提供されません。たとえば、すべての型をstringpair<T>表すには、次のものは使用できません。
std::pair<std::string, T>T
template < typename T > typedef std :: pair < std :: string , T > stringpair < T > ; // 動作しません
stringpair<T>::typeただし、の代わりにを受け入れる場合はstringpair<T>、使用されていないテンプレート化されたクラスまたは構造体内の typedef を介して目的の結果を得ることができます。
template < typename T > class stringpair { private : // `stringpair<T>` のインスタンス化を防止します。stringpair (); public : // `stringpair<T>::type` が `std::pair<std::string, T>` を表すようにします。typedef std :: pair < std :: string , T > type ; };
// `std::pair<std:: string , int>` 型の変数を宣言します。stringpair < int >:: type my_pair_of_string_and_int ;
C++11では、テンプレート化されたtypedefが次の構文で追加されました。この構文では、キーワードusingではなく キーワードが必要ですtypedef。(テンプレートエイリアスを参照してください。)[6]
テンプレート< typename T >でstringpair = std :: pair < std :: string , T >を使用します。
// `std::pair<std::string, int>` 型の変数を宣言します。
stringpair < int > my_pair_of_string_and_int ;
その他の言語
SystemVerilogでは、typedefはCやC++とまったく同じように動作します。[7]
Haskell、Miranda、OCamlなどの多くの静的に型付けされた関数型言語では、 C の typedef と同じ 型シノニムを定義できます。Haskell の例:
型PairOfInts = ( Int , Int )
この例では、型シノニムをPairOfInts整数型として定義しています。
Seed7では、定数型の定義を使用して型の同義語を導入します。
const 型: myVector は配列整数です。
Swiftでは、typealiasキーワードを使用して typedef を作成します。
タイプエイリアス PairOfInts = ( Int 、 Int )
C#usingにはC++のtypedefや構文に似た機能が含まれています。 [8] [6]
using newType = global :: System.Runtime.Interop.Marshal ; using otherType = Enums.MyEnumType ; using StringListMap = System.Collections.Generic.Dictionary < string , System.Collections.Generic.List < string >> ;
Dではキーワードalias[9]を使用して型または部分型の同義語を作成できます。
構造体Foo ( T ){}エイリアスFooInt = Foo ! int ;エイリアスFun = int delegate ( int );
使用上の懸念
Kernighan と Ritchie は、typedef を使用する 2 つの理由を挙げています。[1]まず、typedef はプログラムの移植性を高めたり、保守しやすくしたりする手段を提供します。プログラムのソース ファイル全体で型を変更する代わりに、1 つの typedef ステートメントを変更するだけで済みます。size_tおよびptrdiff_tは<stdlib.h>、そのような typedef 名です。2 番目に、typedef を使用すると、複雑な定義や宣言を理解しやすくなります。
プログラマーの中には、typedef の広範な使用に反対する人もいます。ほとんどの議論は、typedef は単に変数の実際のデータ型を隠すだけであるという考えに基づいています。たとえば、Linux カーネルのハッカーでドキュメンテーション作成者のGreg Kroah-Hartman は、関数のプロトタイプ宣言以外では typedef を使用しないことを推奨しています。彼は、この方法はコードを不必要に難読化するだけでなく、プログラマーが大きな構造を単純な型と勘違いして誤って使用する原因にもなると主張しています。[10]
参照
参考文献
- ^ abc カーニハン、ブライアン W. ;リッチー、デニス M. (1988)。プログラミング言語 C (第 2 版)。ニュージャージー州エングルウッドクリフス: プレンティスホール。p. 147。ISBN 0-13-110362-8. 2016 年6 月 18 日に取得。C
には、新しいデータ型名を作成するための typedef という機能があります。… typedef 宣言は、いかなる意味でも新しい型を作成するものではなく、既存の型に新しい名前を追加するだけであることを強調する必要があります。
- ^ 「const 型修飾子」。cppreference.com 。2020年 10 月 20 日閲覧。
- ^ "typedef 指定子 (C++)". cppreference.com . 2016 年6 月 18 日閲覧。
- ^ "typedef宣言 (C)". cppreference.com . 2016年6月18日閲覧。
- ^ Deitel, Paul J.; Deitel, HM (2007). C プログラミング方法 (第 5 版). Upper Saddle River, NJ: Pearson Prentice Hall. ISBN 9780132404167. 2012 年9 月 12 日取得。
構造体型の名前は、より短い型名を作成するためにtypedefで定義されることがよくあります。
- ^ ab "型エイリアス、エイリアス テンプレート (C++11 以降) - cppreference.com". en.cppreference.com . 2018 年 9 月 25 日閲覧。
- ^ Tala, Deepak Kumar. 「SystemVerilog データ型パート V」www.asic-world.com . ASIC World . 2018 年9 月 25 日閲覧。
- ^ 「Visual Studio 2003 廃止された技術ドキュメント」。
- ^ 「宣言 - Dプログラミング言語」dlang.org 。 2017年5月28日閲覧。
- ^ Kroah-Hartman, Greg (2002-07-01). 「適切な Linux カーネル コーディング スタイル」. Linux Journal . 2007-09-23
取得。typedef を使用すると、変数の実際の型のみが非表示になります
。
