車が故障したので、今日は授業を欠席しました。型へのポインターの配列は次のようになりますか?
void *ary[10];
ary[0] = new int();
ary[1] = new float();
私はまだそれが何であるかについてちょっと曖昧です。
これがC
コードの場合、指摘すべきことがいくつかあります。最初に(以下で説明するように)、new
無効ですC
。で有効ですがC++
、でこの方法でメモリを割り当てることはできませんC
。代わりに、 C ++の代わりにmalloc()
メモリを割り当てるためにを使用する必要があります(これらの関数はに含まれています)。free()
delete
#include <stdlib.h>
さて、あなたのコメントに応えて。ポインタが実際に何であるかについて混乱しているようです。ポインタは、メモリ内の場所を指すものです。つまり、ポインタはシステム上で何らかのタイプ(つまり、おそらくunsigned long
)であり、最終的にはいくつかの数値を保持します。この番号は、割り当てた一部のデータのメモリ位置です(実際には、仮想メモリのため、これらの番号は物理RAMアドレスに直接対応していないことに注意してください)。
だからC
あなたが以下のようなことをするとき
char* x = (char*)malloc(sizeof(char));
単一のメモリのチャンクを割り当てますchar
。からの戻り値malloc()
は、このメモリを使用するためvoid*
にキャストした理由です。char*
これは、オペレーティングシステムが使用するために割り当てたvoid*
(を呼び出すことによって)要求したメモリの場所です。malloc()
このメモリを実際に使用するには*
、メモリ位置を逆参照(つまり演算子)する必要があります。したがって、このメモリを割り当てた後、次のようなことを試すことができます
*x = 'a';
これで、メモリ位置の値x
は。になりa
ます。コードでは、これは次のように表すことができます
if(*x == 'a')
printf("True");
sizeof(int*) == sizeof(char*)
とはいえ、注意することが重要ですsizeof(int) != sizeof(char)
。これは重要な違いです。最初の比較でポインタアドレスのサイズを比較し、2番目の比較でデータ構造(またはこの場合はタイププリミティブ)のサイズを比較する方法に注目してください。これが重要である理由は、上記のコードが実際に機能することを可能にするためです。すべてのポインタアドレスは同じサイズであるためvoid*
、バッファを上書きすることなく、任意のポインタ型の配列を作成して格納できます(つまり、各ポインタはarray[i]
それぞれうまく収まります)。の力は、何でも(はい、技術的には何でも他のものにキャストできますが、これは時々便利です)void*
として渡すことができるということです。void*
ただし、の主な損失は、直接void*
できないことです。これを使って。そうは言っても、私がこのようなコードを持っている場合:
void passData(void* data)
{
char* x = (char*)data; // I _must_ do this to access the data
printf("%s\n", x);
}
int main()
{
char* x = (char*)malloc(10*sizeof(char));
*x = "test";
passData((void*)test);
free(x);
return 0;
}
使用するにはデータ型を知っているvoid*
必要があります(または、これを処理する方法を知るにはメタデータなどを使用する必要があります)。
メモリ割り当てとポインタが実際に何をするかについての基礎ができたので、あなたのコメントに対処します。ポインタは単にメモリアドレスを指しているので、同じメモリアドレスを指す2つのポインタがある場合は、同じデータにアクセスする方法が必要です。たとえば、以下のスニペットについて考えてみます。
#include <stdio.h>
int main()
{
int x = 0; // Stack variable
int* xPtr = &x; // & is the reference operator - it gives you the memory location of an object
x = 5;
printf("%d == %d\n", x, *xPtr); // xPtr == 5 since it points to the same memory location as x
*xPtr = 10;
printf("%d == %d\n", x, *xPtr); // x == 10 since we changed the value at the memory location of xPtr which is the same memory location which x is located at
return 0;
}
ポインタの概念にまだ慣れていない場合は、純粋にヒープメモリを扱っているため、以下の例の方が少しわかりやすいかもしれません。
#include <stdio.h>
int main()
{
int* xPtr = (int*)malloc(sizeof(int));
int* yPtr = xPtr; // Point yPtr to the same address as xPtr
*xPtr = 10; // The value of xPtr == 10 and so does the value of yPtr since they point to the same place
*yPtr = 12; // In a similar way, we modified the value again for both pointers.
yPtr = NULL; // Unset the memory location of yPtr - now yPtr no longer points to the same memory location as xPtr.
// In fact, now yPtr == NULL, so do _NOT_ try to dereference it or you will crash. In any event, the values of the pointers are now independent
*xPtr = 15; // Memory at the location xPtr points to has changed to 15. But yPtr no longer points to that location, so the value at the location yPtr points to has not changed.
yPtr = xPtr; // Change the memory location of yPtr to point to xPtr. Now yPtr's value is exactly the same as xPtr's value.
free(xPtr); // Only free ONE of the items pointing to the same memory location (otherwise you will double free)
return 0;
}
ご覧のとおり、メモリ内の特定の場所にあるデータにアクセスしているだけです。ポインタは、メモリ内のその場所を指しているだけです。ポインタを移動して別の場所を指すようにすることは、として知られていpointer manipulation
ます。そのため、ある場所でメモリを変更すると、そのメモリの場所を指す他のポインタの値が変更されます。
元の回答(元々タグ付けされた質問C++
)
実際には、あなたがしたことはもっとC-style
です。これは技術的には有効ですが(キャストする限り)、後でこれらのタイプを処理するには、どのタイプがどの位置にあるかを正確に把握し、適切にキャストバックする必要があります。
void
与えた例では、ポインターを保持できる配列を作成しました。つまり、void*
ほとんどすべての「バニラ」タイプです(技術的には何にでもキャストできますが、メモリに適切にアクセスしないと、必ずクラッシュします)。ただし、逆参照したり、実際の作業を行ったりすることはできないvoid*
ため、メモリをキャストバックするには、メモリが実際に何であるかを知る必要があります。いずれにせよ、デフォルト/空のコンストラクターC++
には使用しないことをお勧めします。そのため、単純に問題なく動作します。()
new float;
何も台無しにせずにこれを実行できる理由(つまり、データ構造が異なればサイズも異なる)は、ポインターが単純ptr_t
な型であるためです(多くのマシンでは、unsigned long
または類似のものである可能性があります)。したがって、現実的には、ポインタはメモリ内のある場所を指しているだけですが、指しているオブジェクトのサイズが異なっていても、すべてのポインタ(つまりアドレス)は同じサイズです。
これC++
を行うには、ポリモーフィズムを使用することをお勧めします。たとえば、すべてが(少なくとも基本クラスがの)クラスの「ボール」ポインタの配列を作成しますBall
。次に、それらをより具体的にすることができます。たとえば、野球、バスケットボールなどがあります。
適切なコンテキストなしで何をしようとしているのか完全にはわかりませんが、これは次のようになります。
#include <isotream>
using namespace std;
class Ball
{
public:
virtual void ballType() const = 0;
};
class Basketball : public Ball
{
public:
virtual void ballType() const {
cout << "Basketball" << endl;
}
};
class Baseball : public Ball
{
public:
virtual void ballType() const {
cout << "Baseball" << endl;
}
};
次に、一般的に、これを次のように使用する場合:
Ball* x[10];
x[0] = new Basketball;
x[1] = new Baseball;
x[0]->ballType();
x[1]->ballType();
delete x[0];
delete x[1];
これにより、「バスケットボール」の後に「野球」が印刷されます。あなたが何をしようとしているのかについてもう少しコンテキストを提供できれば、私はあなたの質問をターゲットにするためにこの応答をもう少しうまく調整することができます。
ポインタは「整数」の数値であり、メモリ内で必要なものを指すことができます。void* arrayがある場合は、arrayというvoidポインタがあることを示します。これは、使用方法によって異なります。intまたはに型キャストできます。他のものとオブジェクト。
ポインターの配列を作成するvoid*array [10]を使用する場合、ポインターの使用方法はあなた次第です。オブジェクトを作成したり、任意のデータ型に割り当てたりすることができますが、型指定のないvoidポインターであるため、これは型セーフではありません。また、データ型で使用するには、強力な型キャストが必要です。
技術的には、その配列内に異なるデータ型のすべてのポインターを含めることができます。