web-dev-qa-db-ja.com

Cのvoidがvoidでないことを意味するのはなぜですか?

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はそれを「何もないか何か(ポインタの場合)」として使用していたのですか?

26
Naftuli Kay

キーワードvoid(ポインターではない)は、これらの言語では「何もない」ことを意味します。これは一貫しています。

お気づきのように、void*は、生のポインタ(CおよびC++)をサポートする言語では、「すべてを指すポインタ」を意味します。おっしゃるように、これはvoidが2つの異なることを意味するため、残念な決定です。

私は、voidを再利用して、「何もない」と「何も」を異なるコンテキストで再利用する背後にある歴史的な理由を見つけることができませんでしたが、Cは他のいくつかの場所でこれを行います。たとえば、staticは、コンテキストによって目的が異なります。 C言語には、実際にどのように考えているかに関係なく、この方法でキーワードを再利用する前例があります。

JavaとC#は十分に異なるため、これらの問題のいくつかを修正するために完全に中断する必要があります。 Javaおよび "安全な" C#も生のポインタを許可せず、簡単なC互換性を必要としません(安全でないC#行うポインタを許可しますが、C#コードの大部分は許可しませんこのカテゴリに分類されます。これにより、下位互換性を気にせずに少し変更することができます。これを行う1つの方法は、すべてのクラスが継承する階層のルートにクラスObjectを導入することです。 Object参照はvoid*と同じ機能を提供しますが、型の問題や生のメモリ管理の煩わしさはありません。

59
user22815

voidvoid*は2つの異なるものです。 Cでのvoidは、Javaでの場合とまったく同じことで、戻り値がありません。 void*は、型がないポインターです。

Cのすべてのポインターは、逆参照できる必要があります。 void*を逆参照した場合、どのタイプが取得されると思いますか? Cポインターは実行時の型情報を持たないので、コンパイル時に型がわかっている必要があります。

そのコンテキストを考えると、逆参照されたvoid*を使用して論理的に実行できるのはそれを無視することだけです。これは、void型が示す動作とまったく同じです。

32
Karl Bielefeldt

おそらく、voidを戻り値の型として考える方が便利でしょう。次に、2番目のメソッドは"型指定されていないポインタを返すメソッドです。"を読み取ります。

19
Robert Harvey

用語を少し厳しくしましょう。

オンラインC 2011標準 から:

6.2.5タイプ
...
19 voidタイプは、空の値のセットで構成されます。これは、完了できない不完全なオブジェクトタイプです。
...
6.3変換
...
6.3.2.2 void

1 void式(void型の式)の(存在しない)値は決して使用されず、暗黙的または明示的な変換(voidを除く)はそのような式に適用されません。他のタイプの式がvoid式として評価される場合、その値または指定子は破棄されます。 (void式は、その副作用について評価されます。)

6.3.2.3ポインタ

1 voidへのポインターは、任意のオブジェクト型へのポインターとの間で変換できます。オブジェクト型へのポインターは、voidへのポインターに変換され、再び元に戻されます。結果は、元のポインタと同じになります。

void式には値がありません(副作用がある場合もあります)。次のように、voidを返すように定義された関数がある場合:

void foo( void ) { ... }

その後、呼び出し

foo();

値に評価されません。結果がないため、結果を何にも割り当てることができません。

voidへのpointerは、基本的に「汎用」のポインタ型です。明示的なキャストを必要とせずに、void *の値を他のオブジェクトポインター型に割り当てることができます(そのため、すべてのCプログラマーは、mallocの結果をキャストするように叫びます)。

void *を直接逆参照することはできません。指すオブジェクトにアクセスする前に、まず別のオブジェクトポインター型に割り当てる必要があります。

voidポインターは、(多かれ少なかれ)汎用インターフェースを実装するために使用されます。正規の例は qsort ライブラリ関数で、型に対応した比較関数を提供する限り、任意の型の配列をソートできます。

はい、2つの異なる概念(値なしvs汎用ポインター)に同じキーワードを使用すると混乱を招きますが、前例がないわけではありません。 staticには、CとC++の両方で複数の異なる意味があります。

11
John Bode

JavaおよびC#では、メソッドの戻り値の型としてのvoidは、このメソッドは何も返さない、何も返さない、何も返さない、このメソッドから何も受信しないことを意味するようです。

そのステートメントは正しいです。 CおよびC++でも同様です。

cでは、戻り値の型としての、またはメソッドのパラメーター型としてのvoidは、別の意味を持ちます。

そのステートメントは正しくありません。 CまたはC++での戻り値の型としてのvoidは、C#およびJavaでの戻り値と同じことを意味します。 voidvoid*を混同しています。 それらは完全に異なります。

voidvoid*が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#にアクセスするプログラマに慣れるため。

9
Eric Lippert
_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ポインターが表示される主な理由は、この機能により、非常に非オブジェクト指向言語で情報の非表示とオブジェクトベースのプログラミングを実装する方法が提供されるためです。

最後に、movevoid *move(...)ではなくvoid* move(...)として宣言されていることがよくある理由は、タイプではなく名前の横にアスタリスクを付けることは、 C.その理由は、次の宣言で大きな問題が発生するためです。_int* iptr, jptr;_これは、intへの2つのポインターを宣言していると思わせます。あなたは違う。この宣言は、jptrへのポインタではなく、intintにします。適切な宣言は_int *iptr, *jptr;_です。宣言ステートメントごとに1つの変数のみを宣言するという習慣に固執する場合は、アスタリスクがタイプに属しているように見せかけることができます。しかし、あなたがふりをしていることを覚えておいてください。

5
David Hammen

簡単に言えば違いはありませんです。いくつか例を挙げましょう。

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

ここでの混乱は、voidvoid*の定義に基づいていると思います。戻り値の型voidは「何も返さない」を意味し、戻り値の型void*は「型なしのポインタを返す」ことを意味します。

これは、ポインタが特殊なケースであるためです。ポインタオブジェクトは、有効なオブジェクトがあるかどうかにかかわらず、常にメモリ内の場所を指します。 void* carsomethingを指しているため、型が定義されていないため、void*がバイト、Word、Car、または自転車を参照するかどうかは関係ありません。後で定義するのは私たち次第です。

C#やJavaなどの言語では、メモリが管理され、ポインターの概念がObjectクラスにロールオーバーされます。 「なし」タイプのオブジェクトへの参照を持つ代わりに、少なくともオブジェクトが「オブジェクト」タイプであることがわかります。その理由を説明させてください。

従来のC/C++では、オブジェクトを作成してそのポインターを削除すると、そのオブジェクトにアクセスできなくなります。これはメモリリークとして知られています。

C#/ Javaでは、すべてがオブジェクトであり、これによりランタイムはすべてのオブジェクトを追跡できます。私のオブジェクトがCarであることを必ずしも認識する必要はありません。単に、Objectクラスによって既に処理されているいくつかの基本情報を知る必要があるだけです。

私たちのプログラマにとって、これはC/C++で手動で処理しなければならない情報の多くがObjectクラスによって処理され、ポインタである特別なケースの必要性を排除することを意味します。

2
Thebluefish

Cでは、任意の型のものへのポインターを持つことができます[ポインターは参照の型として動作します]。ポインタは、既知のタイプのオブジェクトを識別する場合と、任意の(不明な)タイプのオブジェクトを識別する場合があります。 Cの作成者は、「特定の型のモノへのポインタ」と同じ「任意の型のモノへのポインタ」の一般的な構文を使用することを選択しました。後者の場合の構文では、型が何であるかを指定するためにトークンが必要なので、前者の構文では、型がわかっている場合、そのトークンが属する場所に何かを置く必要があります。 Cは、そのためにキーワード「void」を使用することを選択しました。

Pascalでは、Cと同様に、任意のタイプのものへのポインターを持つことができます。 Pascalのより一般的な方言では、「任意の型のモノへのポインタ」も許可しますが、「特定の型のモノへのポインタ」に使用するのと同じ構文を使用するのではなく、単に前者をPointerと呼びます。 。

Javaでは、ユーザー定義の変数型はありません。すべてがプリミティブまたはヒープオブジェクト参照です。タイプ「特定のタイプのヒープオブジェクトへの参照」または「任意のタイプのヒープオブジェクトへの参照」を定義できます。前者は、特別な句読点を付けずに型名を使用するだけで、それが参照であることを示します(それは他のものにはなり得ないため)。後者は型名Objectを使用します。

の用法 void*任意の型の何かへのポインターの命名法は、CとC++のような直接派生物に固有のものとして私が知る限り、私が知っている他の言語は、明確に名前が付けられた型を使用するだけでこの概念を処理します。

0
supercat

C#およびJavaでは、すべてのクラスはObjectクラスから派生しています。したがって、「何か」への参照を渡したいときはいつでも、タイプObjectの参照を使用できます。

これらの言語では、その目的でvoidポインターを使用する必要がないため、voidはここで「何か」または「何も」を意味する必要はありません。

0
Reg Edit