ポインタを傷つけない(5)--voidとvoidポインタの詳細

6636 ワード

原文転載先:http://blog.csdn.net/sunchaoenter/article/details/6587426
自分の考えを増やして、メモとします.
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)関数パラメータの定義.
第3節では、以上の2点について具体的に説明します.
ポインタp 1とp 2のタイプが同じであれば、p 1とp 2の間で直接値を割り当てることができることはよく知られている.p 1とp 2が異なるデータ型を指す場合、強制型変換演算子を使用して、付与演算子の右側のポインタタイプを左のポインタのタイプに変換する必要があります.
例:
float *p1;



int *p2;



p1 = p2;

ここでp 1=p 2文はコンパイルエラーが発生し、「'=':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タイプ宣言を加えることで,コードの「自己注釈」の役割も果たす.コードの「自己注釈」は、コードが自分で注釈できることです.
ルール2:関数にパラメータがない場合は、そのパラメータをvoidと宣言します.
C++言語で次のような関数を宣言します.
int function(void)

{

        return 1;

}

次の呼び出しは合法的ではありません.
function(2);
C++では、関数パラメータがvoidであることは、この関数がパラメータを受け入れないことを意味するからです.
Turbo C 2.0でコンパイルしました.
#include "stdio.h"

fun()

{

        return 1;

}

main()

{

        printf("%d",fun(2));

        getchar();

}

コンパイルが正しく出力されている1は、C言語では、パラメータのない関数に任意のタイプのパラメータを転送できますが、C++コンパイラで同じコードをコンパイルするとエラーが発生することを示しています.C++では、パラメータのない関数にパラメータを転送することはできません.エラーメッセージ「'fun':function does not take 1 parameters」.
したがって、CでもC++でも、関数がパラメータを受け入れない場合は、パラメータが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基準に迎合しなければならない.
ルール4:関数のパラメータが任意のタイプのポインタである場合、そのパラメータは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 intarray 1[100]、intarray 2[100]を受け入れる.memcpy ( intarray1, intarray2, 100*sizeof(int) );//intarray 2をintarray 1にコピー
興味深いことに、memcpyとmemset関数はvoid*タイプを返し、標準ライブラリ関数の作成者はどんなに学問に富んでいるのか.
ルール5:voidは真実の変数を表すことはできません
次のコードはvoidに真実の変数を表すようにしようとしているので、エラーのコードです:void a;//エラーfunction(void a);//エラー
voidは抽象的で、この世界の変数はすべて“タイプがあります”で、例えば一人が男ではありませんて女です(まだ人妖がありますか?).
voidの出現は抽象的な必要性のためだけであり,オブジェクト向けの「抽象ベースクラス」の概念を正しく理解すればvoidデータ型も容易に理解できる.抽象ベースクラスにインスタンスを定義できないように、void(voidを「抽象データ型」と呼ぶ)変数を定義することもできません.
4.まとめ
小さなvoidは豊富な設計哲学を秘めており、プログラム設計者として、問題に対して深い思考を行うことは必然的に私たちに利益をもたらす.