記事のディレクトリ
(翻訳されたテキスト、コンテンツは削除および変更されました)
1.typedefの定義とスコープ
Typedefを使用すると、既存の型またはユーザー定義型のエイリアスを次の形式で作成できます。
typedef <existing_names_of_datatype> <alias__userGiven_name>;
例:
typedef int myint;
myintはintのエイリアスです。それ以降、intの代わりにmyintを使用して、新しいint変数を定義できます。
myint i = 0; // this statement is equivalent to int i = 0;
同じタイプに対して複数のエイリアスを作成することもできます。例えば:
typedef int myint, integer;
このステートメントは、int型の2つのエイリアス、つまりmyintとintegerを作成します。
宣言が許可されている場所ならどこでもtypedef宣言を書くことができます。ただし、宣言の範囲はtypedefステートメントの場所によって異なることに注意してください。定義がすべての関数の外部に配置されている場合、スコープはグローバルであり、すべての関数で元の名前の代わりにエイリアスを使用できます。一方、定義が関数で宣言されている場合、スコープはローカルであり、この関数のみがエイリアスを使用できます。
例1:typedefを使用してローカルエイリアスを宣言する
#include<stdio.h>
void foo(void);
int main()
{
typedef unsigned char uchar;
uchar ch = 'a';
printf("ch inside main() : %c\n", ch);
foo();
return 0;
}
void foo(void)
{
// uchar ch = 'a'; // Error
unsigned char ch = 'z';
printf("ch inside foo() : %c\n", ch);
}
出力:
ch inside main() : a
ch inside foo(): z
typedefはmain()関数で定義されているため、main()ではエイリアスucharのみを使用できます。15行目のコメントを外してプログラムをコンパイルしようとすると、foo()関数でエイリアスucharが使用できないため、コンパイラからエラーが発生します。
例2:typedefを使用してグローバルエイリアスを宣言する
#include<stdio.h>
typedef unsigned char uchar;
void foo(void);
int main()
{
uchar ch = 'a';
printf("ch inside main() : %c\n", ch);
foo();
return 0;
}
void foo(void)
{
uchar ch = 'z';
printf("ch inside foo() : %c\n", ch);
}
出力:
ch inside main() : a
ch inside foo(): z
ここで、typedef宣言はすべての関数よりも高いため、どの関数もエイリアスucharを使用してunsignedchar型の変数を宣言できます。
2.ポインターのエイリアスを定義します
typedef int * iptr;
このステートメントの後、iptrはintまたは(int *)へのポインターのエイリアスです。
例:
iptr a, *b; // same as int *a, **b;
iptr arr[10]; // same as int *arr[10];
最初の宣言では、aはintへのポインターであり、bはintへのポインターです。2番目の宣言では、arrは10個の整数ポインターの配列です。
例:
#include <stdio.h>
typedef int * iptr;
int main()
{
iptr arr[10];
int a;
arr[1] = &a;
a = 56;
printf("%d\n", a);
printf("%d\n", *(arr[1]));
return 0;
}
ここで、arrは配列であり、各要素はポインターを保持し、出力結果は次のとおりです。
56
56
3.アレイのエイリアスを定義します
typedef int iarr[10];
iarrは、10個の整数要素で構成される配列のエイリアスです。
iarr a, b, c[5]; // same as int a[10], b[10], c[10][5];
この宣言では、aとbは10個の整数の配列であり、cは10 * 5の2次元配列です。
例:
#include<stdio.h>
typedef int iarr[10];
int main()
{
int i;
// same as int a[10] = {12,43,45,65,67,87,89,91,14,19}
iarr a = {
12,43,45,65,67,87,89,91,14,19};
for(i = 0; i < 10; i++)
{
printf("a[%d] = %d\n",i ,a[i]);
}
return 0;
}
出力:
a[0] = 12
a[1] = 43
a[2] = 45
a[3] = 65
a[4] = 67
a[5] = 87
a[6] = 89
a[7] = 91
a[8] = 14
a[9] = 19
4.構造のエイリアスを定義します
struct book
{
char title[20];
char publisher[20];
char author[20];
int year;
int pages;
};
typedef struct book Book;
このステートメントの後、Bookは構造体bookのエイリアスです。したがって、新しい構造変数を宣言するために構造体ブックを使用する必要はありません。ブックを直接使用できます。
Book b1 = {
"The Alchemist", "TDM Publication" , "Paulo Coelho", 1978, 331 };
構造体の定義をtypedef宣言と組み合わせることもできます。構文は次のとおりです。
typedef struct tagname
{
data_type member1;
data_type member1;
...
} newname;
typedefの新しい構文で構造体ブックの定義を書き直してみましょう。
typedef struct book
{
char title[20];
char publisher[20];
char author[20];
int year;
int pages;
} Book;
次のプログラムは、構造体でtypedefを使用する方法を示しています。
#include<stdio.h>
typedef struct book
{
char title[20];
char publisher[20];
char author[20];
int year;
int pages;
} Book;
int main()
{
Book b1 = {
"The Zahir",
"Harper Perennial" ,
"Paulo Coelho",
2005,
336
};
printf("Title: %s\n", b1.title);
printf("Author: %s\n", b1.author);
return 0;
}
出力:
Title: The Zahir
Author: Paulo Coelho
同様に、typedefをunionで使用できます。
5.typedefと#define
typedefはプリプロセッサディレクティブではないため、その解釈はプリプロセッサではなくコンパイラによって処理されることに注意する必要があります。#defineディレクティブを使用すると、任意のテキストの拡張子を定義できますが、typedefは、任意のデータ型のエイリアスを作成するために使用されることを思い出してください。
ただし、場合によっては、#defineとtypedefで同じ結果が生成されます。例は次のとおりです。
#defineディレクティブ | typedef宣言 | |
---|---|---|
#define uchar unsigned char | typedef unsigned char uchar; | |
テストステートメント | uchar ch; | uchar ch; |
変換された結果 | unsigned char ch; | unsigned char ch; |
以下は、#defineとtypedefが異なる結果を生成する状況です。
#defineディレクティブ | typedef宣言 | |
---|---|---|
#define fp float * | typedef float * fp; | |
テストステートメント | fp a、b、c; | fp a、b、c; |
変換された結果 | フロート* a、b、c; | フロート* a、* b、* c; |
2番目のケースでは、プリプロセッサがステートメントを確認したとき
fp a, b, c;
fpをfloat *に置き換えます。したがって、上記のステートメントは次のようになります。
float *a, b, c;
一方、typedefはより意味的な意味を持っているため、コンパイラーはプリプロセッサーのようにそれを置き換えることはありません。
次のプログラムは、#defineとtypedefの違いを示しています。
#include<stdio.h>
#define ptr int * // replace occurence of ptr by int *
typedef int * iptr; // iptr is an alias of pointer to int or int*
int main()
{
ptr a, b, c; // same as int *a, b, c;
iptr p1, p2, p3; // same as int *p1, *p2, *p3
b = 10;
c = 20;
a = &b;
p1 = &b;
p2 = &c;
p3 = &c;
printf("Value at a = %d\n", *a); // print value of b
printf("Value at p2 = %d\n", *p2); // print value of b
return 0;
}
出力:
Value at a = 10
Value at p2 = 20
6.typedefの利点
それはプログラムをより読みやすくします。もちろん、ブックb1は、構造体ブックb1よりも読みやすく直感的です。
それはプログラムを移植可能にします。sizeof()演算子とmalloc()関数のプロトタイプを見て説明しましょう。
size_t sizeof(type);
void *malloc(size_t size);
ご存知のように、両方のプロトタイプはsize_t型を使用しており、size_tをunsigned intとして扱うように指示しましたが、これは完全には正しくありません。C標準では、sizeof()は整数を返す必要があると規定されていますが、システムはどのタイプを返すかを決定します。これは、C標準委員会が、オプションを提供しないことが各プラットフォームにとって最良の選択である可能性があると考えているためです。そのため、size_t、time_tなどの新しい型を作成し、システムがtypedefを使用してこれらの名前を特定の型に設定できるようにしました。したがって、あるシステムのsize_tの型はunsigned intであり、別のシステムのsize_tの型はunsigned longintである可能性があります。
参考文献
[1] C [EB / OL]のOverIQ.com.typedefステートメント。https://overiq.com/c-programming-101/typedef-statement-in-c/,2020年7月27日。