JavaおよびC#のような強く型付けされた言語では、メソッドの戻り値の型としてのvoid
(またはVoid
)は次のことを意味するようです:
このメソッドは何も返しません。何もない。返品不可。このメソッドからは何も受け取りません。
本当に奇妙なのは、Cではvoid
が戻り値の型として、またはメソッドのパラメーター型としても、
それは本当に何でもかまいません。調べるにはソースコードを読む必要があります。幸運を。ポインターの場合は、自分が何をしているかを本当に理解している必要があります。
Cの次の例を考えてみます。
void describe(void *thing)
{
Object *obj = thing;
printf("%s.\n", obj->description);
}
void *move(void *location, Direction direction)
{
void *next = NULL;
// logic!
return next;
}
明らかに、2番目のメソッドはポインターを返します。ポインターは、定義上は何でもかまいません。
CはJavaおよびC#より古いので、なぜこれらの言語はvoid
を「何もない」という意味で採用しているのに、Cはそれを「何もないか何か(ポインタの場合)」として使用していたのですか?
キーワードvoid
(ポインターではない)は、これらの言語では「何もない」ことを意味します。これは一貫しています。
お気づきのように、void*
は、生のポインタ(CおよびC++)をサポートする言語では、「すべてを指すポインタ」を意味します。おっしゃるように、これはvoid
が2つの異なることを意味するため、残念な決定です。
私は、void
を再利用して、「何もない」と「何も」を異なるコンテキストで再利用する背後にある歴史的な理由を見つけることができませんでしたが、Cは他のいくつかの場所でこれを行います。たとえば、static
は、コンテキストによって目的が異なります。 C言語には、実際にどのように考えているかに関係なく、この方法でキーワードを再利用する前例があります。
JavaとC#は十分に異なるため、これらの問題のいくつかを修正するために完全に中断する必要があります。 Javaおよび "安全な" C#も生のポインタを許可せず、簡単なC互換性を必要としません(安全でないC#行うポインタを許可しますが、C#コードの大部分は許可しませんこのカテゴリに分類されます。これにより、下位互換性を気にせずに少し変更することができます。これを行う1つの方法は、すべてのクラスが継承する階層のルートにクラスObject
を導入することです。 Object
参照はvoid*
と同じ機能を提供しますが、型の問題や生のメモリ管理の煩わしさはありません。
void
とvoid*
は2つの異なるものです。 Cでのvoid
は、Javaでの場合とまったく同じことで、戻り値がありません。 void*
は、型がないポインターです。
Cのすべてのポインターは、逆参照できる必要があります。 void*
を逆参照した場合、どのタイプが取得されると思いますか? Cポインターは実行時の型情報を持たないので、コンパイル時に型がわかっている必要があります。
そのコンテキストを考えると、逆参照されたvoid*
を使用して論理的に実行できるのはそれを無視することだけです。これは、void
型が示す動作とまったく同じです。
おそらく、void
を戻り値の型として考える方が便利でしょう。次に、2番目のメソッドは"型指定されていないポインタを返すメソッドです。"を読み取ります。
用語を少し厳しくしましょう。
オンラインC 2011標準 から:
6.2.5タイプ
...
19void
タイプは、空の値のセットで構成されます。これは、完了できない不完全なオブジェクトタイプです。
...
6.3変換
...
6.3.2.2 void
1void
式(void
型の式)の(存在しない)値は決して使用されず、暗黙的または明示的な変換(void
を除く)はそのような式に適用されません。他のタイプの式がvoid
式として評価される場合、その値または指定子は破棄されます。 (void
式は、その副作用について評価されます。)
6.3.2.3ポインタ
1void
へのポインターは、任意のオブジェクト型へのポインターとの間で変換できます。オブジェクト型へのポインターは、voidへのポインターに変換され、再び元に戻されます。結果は、元のポインタと同じになります。
void
式には値がありません(副作用がある場合もあります)。次のように、void
を返すように定義された関数がある場合:
void foo( void ) { ... }
その後、呼び出し
foo();
値に評価されません。結果がないため、結果を何にも割り当てることができません。
void
へのpointerは、基本的に「汎用」のポインタ型です。明示的なキャストを必要とせずに、void *
の値を他のオブジェクトポインター型に割り当てることができます(そのため、すべてのCプログラマーは、malloc
の結果をキャストするように叫びます)。
void *
を直接逆参照することはできません。指すオブジェクトにアクセスする前に、まず別のオブジェクトポインター型に割り当てる必要があります。
void
ポインターは、(多かれ少なかれ)汎用インターフェースを実装するために使用されます。正規の例は qsort
ライブラリ関数で、型に対応した比較関数を提供する限り、任意の型の配列をソートできます。
はい、2つの異なる概念(値なしvs汎用ポインター)に同じキーワードを使用すると混乱を招きますが、前例がないわけではありません。 static
には、CとC++の両方で複数の異なる意味があります。
JavaおよびC#では、メソッドの戻り値の型としてのvoidは、このメソッドは何も返さない、何も返さない、何も返さない、このメソッドから何も受信しないことを意味するようです。
そのステートメントは正しいです。 CおよびC++でも同様です。
cでは、戻り値の型としての、またはメソッドのパラメーター型としてのvoidは、別の意味を持ちます。
そのステートメントは正しくありません。 CまたはC++での戻り値の型としてのvoid
は、C#およびJavaでの戻り値と同じことを意味します。 void
とvoid*
を混同しています。 それらは完全に異なります。
void
とvoid*
が2つのまったく異なるものを意味するのは紛らわしくないですか?
うん。
ポインタとは何ですか? voidポインターとは何ですか?
ポインターは値逆参照の場合があります。 有効ポインターを逆参照すると、ポイント先の型の保存場所が得られます。 void pointerは、特定の指す型を持たないポインターです。値がdereferencedになる前に、convertedより具体的なポインタタイプにstorage locationを生成する必要があります。
C、C++、JavaおよびC#でもvoidポインターは同じですか?
Javaにはvoidポインターはありません。 C#はそうします。これらは、CおよびC++と同じです。特定の型が関連付けられていないポインター値で、参照を解除してから格納場所を作成する前に、より具体的な型に変換する必要があります。
CはJavaおよびC#よりも古いため、これらの言語がvoidを「何もない」を意味するものとして採用したのに、Cはそれを「何もないまたは何か(ポインタの場合)」として使用したのはなぜですか?
質問は虚偽を前提としているため、一貫性がありません。もっと良い質問をしてみましょう。
JavaとC#が
void
が有効な戻り値の型であるという規則を採用した理由は、たとえば、Visual Basicの規則を使用して関数が無効になることはなく、サブルーチンが常に無効に戻りますか?
Javaまたはvoid
が戻り値の型である言語からC#にアクセスするプログラマに慣れるため).
C#が
void*
の意味がvoid
とはまったく異なるという紛らわしい規則を採用したのはなぜですか?
void*
がポインタ型である言語からC#にアクセスするプログラマに慣れるため。
_void describe(void *thing);
void *move(void *location, Direction direction);
_
最初の関数は何も返しません。 2番目の関数はvoidポインターを返します。私はその2番目の関数を
_void* move(void* location, Direction direction);
_
「ボイド」を「意味なし」の意味と考えると役立つ場合があります。 describe
の戻り値は「意味なし」です。そのような関数から値を返すことは違法です。なぜなら、戻り値には意味がないとコンパイラに伝えたからです。その関数からの戻り値は意味がないのでキャプチャできません。 void
型の変数は、意味がないため宣言できません。たとえば、_void nonsense;
_はナンセンスであり、実際には違法です。また、void _void void_array[42];
_の配列も意味がないことに注意してください。
Void(_void*
_)へのポインターは、何か違うものです。これはポインタであり、配列ではありません。 _void*
_は、「意味のない」何かを指すポインタを意味するものとして読み取ります。ポインターは「意味なし」です。つまり、そのようなポインターを逆参照しても意味がありません。そうしようとすることは実際には違法です。 voidポインターを逆参照するコードはコンパイルされません。
それで、voidポインターを逆参照できず、voidの配列を作成できない場合、どのように使用できますか?その答えは、_void*
_との間で任意の型へのポインターをキャストできることです。ポインタを_void*
_にキャストし、元の型へのポインタにキャストすると、元のポインタと同じ値が生成されます。 C標準では、この動作が保証されています。 Cで非常に多くのvoidポインターが表示される主な理由は、この機能により、非常に非オブジェクト指向言語で情報の非表示とオブジェクトベースのプログラミングを実装する方法が提供されるためです。
最後に、move
がvoid *move(...)
ではなくvoid* move(...)
として宣言されていることがよくある理由は、タイプではなく名前の横にアスタリスクを付けることは、 C.その理由は、次の宣言で大きな問題が発生するためです。_int* iptr, jptr;
_これは、int
への2つのポインターを宣言していると思わせます。あなたは違う。この宣言は、jptr
へのポインタではなく、int
をint
にします。適切な宣言は_int *iptr, *jptr;
_です。宣言ステートメントごとに1つの変数のみを宣言するという習慣に固執する場合は、アスタリスクがタイプに属しているように見せかけることができます。しかし、あなたがふりをしていることを覚えておいてください。
簡単に言えば違いはありませんです。いくつか例を挙げましょう。
Carというクラスがあるとします。
Car obj = anotherCar; // obj is now of type Car
Car* obj = new Car(); // obj is now a pointer of type Car
void obj = 0; // obj has no type
void* = new Car(); // obj is a pointer with no type
ここでの混乱は、void
とvoid*
の定義に基づいていると思います。戻り値の型void
は「何も返さない」を意味し、戻り値の型void*
は「型なしのポインタを返す」ことを意味します。
これは、ポインタが特殊なケースであるためです。ポインタオブジェクトは、有効なオブジェクトがあるかどうかにかかわらず、常にメモリ内の場所を指します。 void* car
がsomethingを指しているため、型が定義されていないため、void*
がバイト、Word、Car、または自転車を参照するかどうかは関係ありません。後で定義するのは私たち次第です。
C#やJavaなどの言語では、メモリが管理され、ポインターの概念がObjectクラスにロールオーバーされます。 「なし」タイプのオブジェクトへの参照を持つ代わりに、少なくともオブジェクトが「オブジェクト」タイプであることがわかります。その理由を説明させてください。
従来のC/C++では、オブジェクトを作成してそのポインターを削除すると、そのオブジェクトにアクセスできなくなります。これはメモリリークとして知られています。
C#/ Javaでは、すべてがオブジェクトであり、これによりランタイムはすべてのオブジェクトを追跡できます。私のオブジェクトがCarであることを必ずしも認識する必要はありません。単に、Objectクラスによって既に処理されているいくつかの基本情報を知る必要があるだけです。
私たちのプログラマにとって、これはC/C++で手動で処理しなければならない情報の多くがObjectクラスによって処理され、ポインタである特別なケースの必要性を排除することを意味します。
Cでは、任意の型のものへのポインターを持つことができます[ポインターは参照の型として動作します]。ポインタは、既知のタイプのオブジェクトを識別する場合と、任意の(不明な)タイプのオブジェクトを識別する場合があります。 Cの作成者は、「特定の型のモノへのポインタ」と同じ「任意の型のモノへのポインタ」の一般的な構文を使用することを選択しました。後者の場合の構文では、型が何であるかを指定するためにトークンが必要なので、前者の構文では、型がわかっている場合、そのトークンが属する場所に何かを置く必要があります。 Cは、そのためにキーワード「void」を使用することを選択しました。
Pascalでは、Cと同様に、任意のタイプのものへのポインターを持つことができます。 Pascalのより一般的な方言では、「任意の型のモノへのポインタ」も許可しますが、「特定の型のモノへのポインタ」に使用するのと同じ構文を使用するのではなく、単に前者をPointer
と呼びます。 。
Javaでは、ユーザー定義の変数型はありません。すべてがプリミティブまたはヒープオブジェクト参照です。タイプ「特定のタイプのヒープオブジェクトへの参照」または「任意のタイプのヒープオブジェクトへの参照」を定義できます。前者は、特別な句読点を付けずに型名を使用するだけで、それが参照であることを示します(それは他のものにはなり得ないため)。後者は型名Object
を使用します。
の用法 void*
任意の型の何かへのポインターの命名法は、CとC++のような直接派生物に固有のものとして私が知る限り、私が知っている他の言語は、明確に名前が付けられた型を使用するだけでこの概念を処理します。
C#およびJavaでは、すべてのクラスはObject
クラスから派生しています。したがって、「何か」への参照を渡したいときはいつでも、タイプObject
の参照を使用できます。
これらの言語では、その目的でvoidポインターを使用する必要がないため、void
はここで「何か」または「何も」を意味する必要はありません。