C/C++voidおよびvoidポインタ

4131 ワード

1.概説多くの初心者はC/C++言語のvoidおよびvoidポインタタイプをあまり理解していないため、使用上いくつかのエラーが発生した.本稿ではvoidキーワードの深い意味について解説し,voidおよびvoidポインタタイプの使用方法とテクニックについて詳しく述べる.2.voidの意味voidの字面は「タイプなし」を意味し、void*は「タイプポインタなし」を意味し、void*は任意のタイプのデータを指すことができる.voidはほとんど「注釈」と制限プログラムの役割しかありません.void変数を定義する人はいないので、定義してみましょう.
void a;この行の文のコンパイル中にエラーが発生し、「illegal use of type'void」というメッセージが表示されます.しかし、void aのコンパイルが間違っていなくても、実際の意味はありません.voidが本当に発揮する役割は、(1)関数に対する戻りの制限;(2)関数パラメータの定義.
ポインタp 1とp 2のタイプが同じであれば、p 1とp 2の間で直接値を割り当てることができることはよく知られている.p 1とp 2が異なるデータ型を指す場合、強制型変換演算子を使用して、付与演算子の右側のポインタタイプを左のポインタのタイプに変換する必要があります.例:
float *p1;
int *p2;
p1 = p2;

  p1 = p2       ,  “'=' : cannot convert from 'int *' to 'float *'”,    :
p1 = (float *)p2;

void*は、強制的なタイプ変換を行うことなく、任意のタイプのポインタを直接割り当てることができます.
void *p1;
int *p2;
p1 = p2;

ただし、void*は、強制的なタイプ変換を必要とせずに他のタイプのポインタに付与されてもよいという意味ではありません.[タイプなし](No Type)は[タイプ](Type)を収容し、[タイプ](Type)は[タイプなし](No Type)を収容できないためです.理屈は簡単で、私たちは「男も女も人だ」と言うことができますが、「人は男だ」とか「人は女だ」とは言えません.次の文のコンパイルエラー:
void *p1;
int *p2;
p2 = p1;

  “'=' : cannot convert from 'void *' to 'int *'”。

3.voidの使用
voidキーワードの使用規則を次に示します.
 
ルール1関数が値を返さない場合はvoidタイプとして宣言します.
C言語では,戻り値タイプに限定されない関数は,コンパイラによって戻り整数値として扱われる.しかし、多くのプログラマーはvoidタイプだと勘違いしている.例:
add ( int a, int b )
{
return a + b;
}
int main(int argc, char* argv[])
{
printf ( "2 + 3 = %d", add ( 2, 3) );
}

プログラム実行の結果出力:2+3=5
これは、戻り値の説明を加えない関数がint関数であることを示しています.
林鋭博士の『高品質C/C++プログラミング』では、「C++言語には厳格なタイプのセキュリティチェックがあり、上記の状況(関数にタイプ宣言を加えないことを指す)が発生することは許されない」と述べている.しかし、コンパイラは必ずしもそう認定するものではなく、例えばVisual C++6.0で上述したadd関数のコンパイルに間違いがなく、警告もなく正しく動作するため、コンパイラが厳格なタイプチェックを行うことは期待できない.
したがって,混乱を避けるために,C/C++プログラムを記述する際には,どの関数に対してもそのタイプを漏れなく指定しなければならない.関数に値が返されない場合は、voidタイプとして宣言する必要があります.これはプログラムの良好な可読性の必要性であり、プログラミングの規範性の要求でもある.また,voidタイプ宣言を加えることで,コードの「自己注釈」の役割も果たす.コードの「自己注釈」は、コードが自分で注釈できることです.
ルール3 voidポインタタイプに注意
ANSI(American National Standards Institute)規格に従って、voidポインタをアルゴリズム操作することはできません.すなわち、以下の操作はすべて合法ではありません.
void * pvoid;
pvoid++; //ANSI:  
pvoid += 1; //ANSI:  
//ANSI         ,      :                          。
//  :
int *pint;
pint++; //ANSI:    	pint++        sizeof(int)

しかし、有名なGNU(GNU's Not Unixの略)は、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基準に迎合しなければならない.
  
ルール3関数のパラメータが任意のタイプのポインタである場合、そのパラメータはvoid*であることを宣言します.
メモリ操作関数memcpyやmemsetのような典型的な関数のプロトタイプは、それぞれ次のとおりです.
void * memcpy(void *dest, const void *src, size_t len);
void * memset ( void * buffer, int c, size_t num );

これにより、任意のタイプのポインタがmemcpyおよびmemsetに転送されます.これは、このメモリがどのタイプであっても、操作対象がメモリ1枚であるため、メモリ操作関数の意味を実質的に表します.もしmemcpyとmemsetのパラメータタイプがvoid*ではなくchar*であれば、それは本当におかしいです!このようなmemcpyとmemsetは明らかに「純粋で、低級な趣味から離れた」関数ではない!
次のコードは正しく実行されます.
//  :memset        
int intarray[100];
memset ( intarray, 0, 100*sizeof(int) ); // intarray 0

//  :memcpy        
int intarray1[100], intarray2[100];
memcpy ( intarray1, intarray2, 100*sizeof(int) ); // intarray2   intarray1

興味深いことに、memcpyとmemset関数はvoid*タイプを返し、標準ライブラリ関数の作成者はどんなに学問に富んでいるのか.
  
ルール4 voidは真実の変数を表すことはできません
次のコードは、voidに実際の変数を表すようにしようとしているので、エラーのコードです.
void a;//エラーfunction(void a);//エラー
voidは抽象的で、この世界の変数はすべて“タイプがあります”で、例えば一人が男ではありませんて女です(まだ人妖がありますか?).voidの出現は抽象的な必要性のためだけであり,オブジェクト向けの「抽象ベースクラス」の概念を正しく理解すればvoidデータ型も容易に理解できる.抽象ベースクラスにインスタンスを定義できないように、void(voidを「抽象データ型」と呼ぶ)変数を定義することもできません.
http://pcedu.pconline.com.cn/empolder/gj/c/0509/702366_all.html