C言語でのvoid *の詳細な説明と適用

 

Voidは、英語では「空、スペース、ギャップ」として名詞として解釈され ますが、C言語では、void「タイプなしとして翻訳され、対応するvoid *は「型なしポインター」です。

Voidは「コメント」とプログラム制限の機能しかないようですが、もちろんここでの「コメント」はコメントではなく、いわゆるコンパイラへのコメントです。


ボイドの役割

1.関数の戻りの制限。この状況は私たちにとってより一般的です。

2.関数パラメータの制限、この状況も比較的一般的です。

一般に、これら2つの状況は一般的です。

  • 関数が値を返す必要がない場合は、voidで修飾する必要があります。これを最初のケースと呼びます。例:void func(int a、char * b)。
  • 関数がパラメーターを受け入れることが許可されていない場合は、void修飾を使用する必要があります。これは、2番目のケースと呼ばれます。例:int func(void)。

voidポインタを使用するためのルール

1. voidポインターは、任意のタイプのデータを指すことができます。つまり、任意のタイプのポインターを使用して、voidポインターに値を割り当てることができます。例えば:

int * a;
void * p;
p = a;

voidポインタpを他のタイプのポインタに割り当てたい場合は、キャストする必要があります。この場合は、a =(int *)pです。メモリ割り当てでは、voidポインタの使用を確認できます。メモリ割り当て関数malloc関数によって返されるポインタはvoid *です。ユーザーがこのポインタを使用する場合、型変換を実行する必要があります。つまり、指定されたメモリを明示的に指定する必要があります。 (int *)malloc(1024)に格納されるデータのタイプは、mallocによって返されるvoid *ポインタが指すメモリがintタイプのデータを1つずつ格納することが必須であることを意味します。

2. ANSI C標準では、p ++やp + = 1などのvoidポインターに対する一部の算術演算は許可されていません。これは、voidが型指定されていないため、charTypeなどの各算術演算で操作するバイト数がわからないためです。操作sizeof(char)バイト、int操作sizeof(int)バイト。GNUでは、デフォルトでvoid *がchar *と同じであると見なされるため、許可されます。確かなので、もちろん、いくつかの算術演算を実行できます。ここでは、sizeof(* p)== sizeof(char)です。

ボイドはほとんど「コメント」してプログラムの役割を制限しているだけです。ボイド変数を定義した人は誰もいないため、次のように定義してみましょう。

ボイドa;

このステートメント行をコンパイルすると、エラーが発生し、「タイプ「void」の不正使用」を促します。void aがエラーなしでコンパイルされたとしても、実用的な意味はありません。

ご存知のとおり、ポインターp1とp2が同じタイプの場合、p1とp2の間に値を直接割り当てることができます.p1とp2が異なるデータ型を指している場合は、キャスト演算子を使用してポインターを変換する必要があります代入演算子の右側の型左ポインタの型です。

float * p1; 
int * p2; 
p1 = p2; 
// p1 = p2ステートメントはエラーをコンパイルします、
//プロンプト "'=': 'int *'から 'float *'"に変換できません。次のように変更する必要があります:
p1 =(float *)p2;

void *は異なり、強制することなく、任意のタイプのポインターを直接割り当てることができます。

void * p1; 
int * p2; 
p1 = p2;

ただし、これは、void *をキャストせずに他のタイプのポインターにも割り当てることができるという意味ではありません。「notype」には「typed」を含めることができ、「typed」には「notype」を含めることはできません。

voidポインタタイプに注意してください:

ANSI(American National Standards Institute)規格によると、voidポインターに対して算術演算を実行することはできません。つまり、次の演算は不正です。

void * pvoid; 
pvoid ++; // ANSI:エラー
pvoid + = 1; // ANSI:エラー
// ANSI規格がこれを認識する理由は、次のように主張しているためです。算術演算のポインタは、それが指すデータ型。
//例:
int * pint; 
pint ++; // ANSI:正解

pint ++の結果は、sizeof(int)を増やすことです。

しかし、GNUはそうは考えていません。それは、void *の算術演算がchar *と一致することを指定しています。したがって、GNUコンパイラでは次のステートメントが正しいです。

pvoid ++; // GNU:正しい
pvoid + = 1; // GNU:正しい

pvoid ++の実行結果は、1増加します。

実際のプログラム設計では、ANSI規格に適合し、プログラムの移植性を向上させるために、次のように同じ機能を実現するコードを記述できます。

void * pvoid; 
((char *)pvoid)++; // ANSI:間違っている; GNU:正しい
(char *)pvoid + = 1; // ANSI:間違っている; GNU:正しい

GNUとANSIの間にはいくつかの違いがあります。一般に、GNUはANSIよりも「オープン」であり、より多くの構文サポートを提供します。しかし、実際に設計する場合でも、可能な限りANSI規格に準拠する必要があります。関数のパラメーターが任意のタイプのポインターである可能性がある場合、パラメーターはvoid *として宣言する必要があります。

注: voidポインターは任意のタイプのデータにすることができるため、プログラムにいくつかの利点があります。関数がポインタータイプの場合、関数が任意のタイプのポインターを受け入れることができるように、voidポインターとして定義できます。といった:

典型的なメモリ操作関数memcpyおよびmemsetの関数プロトタイプは次のとおりです。

void * memcpy(void * dest、const void * src、size_t len); 
void * memset(void * buffer、int c、size_t num);

このようにして、任意のタイプのポインタをmemcpyとmemsetに渡すことができます。これは、メモリのタイプに関係なく、操作するオブジェクトがメモリの一部にすぎないため、メモリ操作関数の意味も真に反映しています(Cを参照)。言語実装ジェネリックプログラミング)。memcpyとmemsetのパラメータタイプがvoid *ではなくchar *である場合、それは本当に奇妙です!このようなmemcpyとmemsetは、明らかに「純粋で低レベルの楽しみ」機能ではありません。ボイドの出現は抽象的な必要性のためだけです。オブジェクト指向の「抽象的な基本クラス」の概念を正しく理解していれば、ボイドのデータ型も簡単に理解できます。抽象基本クラスのインスタンスを定義できないのと同じように、void(voidを「抽象データ型」と同様に呼ぶ)変数を定義することはできません。

おすすめ

転載: blog.csdn.net/mid_Faker/article/details/109802699