voidとvoid*の詳しい説明

高洛峰
リリース: 2016-12-13 13:14:35
オリジナル
4399 人が閲覧しました

void キーワードの使用規則:

1. 関数に戻り値がない場合は、void 型として宣言する必要があります。 2. 関数にパラメーターがない場合は、そのパラメーターを void として宣言する必要があります。関数パラメータ 任意の型のポインタにすることができ、そのパラメータは void * として宣言する必要があります

4. void は抽象化を表しており、この世界のすべての変数は「型付き」です

############################################# # ######################
1. 概要
多くの初心者は C/C++ 言語の void および void ポインター型を理解していないため、使用時にいくつかのエラーが発生します。発生した。この記事では、void キーワードの深い意味を説明し、void および void ポインター型の使用法とテクニックについて詳しく説明します。

2. void の意味
void の文字通りの意味は「型なし」、void * は「型なしのポインター」であり、void * は任意の型のデータを指すことができます。

void はプログラムを「コメント」して制限する機能しかほとんどありません。誰も void 変数を定義したことがないので、定義してみましょう:


void a;

この行のステートメントは、次の場合にエラーを引き起こします。コンパイルすると、「タイプ 'void' の不正な使用」が表示されます。ただし、 void がエラーなしでコンパイルされたとしても、実際的な意味はありません。

void の本当の役割は、
(1) 関数の戻り値の制限
(2) 関数のパラメータの制限。

上記2点については、第3章で詳しく説明します。

ご存知のとおり、ポインター p1 と p2 の型が同じであれば、p1 と p2 の間で値を直接割り当てることができます。p1 と p2 が異なるデータ型を指す場合は、次を使用する必要があります。代入演算子の右側を変更する強制型

変換演算子 ポインタ型は左ポインタの型に変換されます。

例:
float *p1;
int *p2;

p1 = p2 ステートメントはコンパイル エラーを引き起こし、「'=' : 'int *' から 'float *」に変換できません。 '" は次のように変更する必要があります:
p1 = (float *)p2;
void * とは異なり、キャスト型変換せずに任意のタイプのポインタを直接割り当てることができます:
void *p1;
int *p2;
p1 = p2;

しかし、これは void * をキャストせずに他の型のポインタに代入できるという意味ではありません。なぜなら、「型なし」には「型付き」を含めることができますが、「型付き」に「型なし」を含めることはできないからです。理由はとても簡単です。「男も女も人間である」とは言えますが、「人は男である」「人は女である」とは言えません。次のステートメントはコンパイル エラーになります:
void *p1;
int *p2;
p2 = p1;

プロンプト "'=' : 'void *' から 'int *' に変換できません。

3. void の使用

void キーワードを使用する場合の規則は次のとおりです:
規則 1 関数が値を返さない場合は、void 型として宣言する必要があります

C 言語では、値を返さない関数はすべて void 型として宣言されます。戻り値の型に制限があるため、コンパイラによって戻り値の整数値として処理されます。しかし、多くのプログラマはそれが 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 class

型として宣言する必要があります。これは、プログラムの読みやすさの要件だけでなく、プログラミングの標準化の要件でもあります。また、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 doesn't take 1parameters」というエラーメッセージが表示されます。

したがって、C であっても C++ であっても、関数がパラメーターを受け入れない場合は、パラメーターを void として指定する必要があります。

ルール 3: void ポインター型は慎重に使用してください

ANSI (American National Standards Institute) 規格によれば、void ポインターに対して算術演算を実行することはできません。つまり、次の演算は不正です。 //ANSI: エラー
pvoid += 1; //ANSI: エラー
//ANSI 標準がこれを識別する理由は、アルゴリズム演算のポインタがそれが指すデータ型のサイズを知っている必要があると主張しているためです。
//例:
int *pint;
pint++; //ANSI: 正解

pint++ の結果は、sizeof(int) を増やすことです。 (VC6.0でテストしたところ、sizeof(int)の倍数です)

しかし、有名なGNU(GNU's Not Unixの略)はそうは考えていません。 void * のアルゴリズム演算は char * と一致すると規定しています。

したがって、次のステートメントは GNU コンパイラーでは正しいです:
pvoid++; //GNU: 正しい
pvoid += 1; //GNU: 正しい

pvoid++ の実行結果は 1 増加します。 (VC6.0 でのテストは sizeof(int) の倍数です)

実際のプログラミングでは、ANSI 規格に準拠し、プログラムの移植性を向上させるために、次のように同じ機能を実現するコードを書くことができます。
void * pvoid;
(char *)pvoid++; //ANSI: 正しい; GNU: 正しい
(char *)pvoid += 1; //ANSI: 間違い; 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 に渡すことができ、これもメモリを真に反映します。操作関数 意味は、それが操作するオブジェクトは、メモリの種類に関係なく、単なるメモリの一部であるためです。 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 * 型です。標準ライブラリ関数の作成者はなんて知識があるのでしょう。

ルール 5 void は実数変数を表すことはできません

以下のコードはすべて void に実数変数を表現させようとしているため、すべて間違ったコードです。

void は抽象化を体現しています。たとえば、人は男性か女性(そしてニューハーフ?)のいずれかです。

voidの出現は抽象的なニーズのためだけです オブジェクト指向における「抽象基底クラス」の概念を正しく理解していれば、voidデータ型を理解するのは簡単です。抽象基本クラスのインスタンスを定義できないのと同様に、void (類推により void を「抽象データ型」と呼びます) 変数も定義できません。

4. まとめ
小さな空白には、プログラマーとして、より深いレベルで問題を考えることが多くの利益をもたらします。ポインタのタイプ (void*、char*、int*、float*...) に関係なく、デフォルトの初期値は 0xCCCCCCCC です//これはコンパイラごとに異なるはずですが、これは vc6 の場合です

#include#include
//#include
void main()
{
void *p1;
int a = 10;
int *p2 = &a;
cout < cout<<(int)*p2<<
p1=p2<< / !!!!!! 値を出力するには空の型演算を使用してください!
cout << (int)*p2 << endl;
}
/* 出力:
0xCCCCCCCC
10
10
10
*/

宣言と同時にNULLを代入し、すぐにNULLに設定削除後。


デバッグ バージョンのポインタのデフォルトの初期値は 0xCCCCCCCC、リリース バージョンの初期値は 0x0000000A (私のコンピュータでは VC6.0) です。ポインターに適切な初期化値がない場合は、NULL (0) に設定する必要があります。
良いプログラミング習慣を身に付けるには、ポインターを宣言して NULL に初期化します。ポインターで delete を使用する場合は、それを NULL に設定します。


以下の VC によって生成される未定義のポインター値は、このポインターが初期化されておらず、リリース状態では (偶然でない限り) この値と等しくないことを示すために使用されます。ポインターに適切な初期化値がない場合は、NULL (0) に設定する必要があります。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のおすすめ
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート