C言語のあれこれ7(配列を伴わないポインタの使用方法)
はじめに
以下の記事の続きになります。
C言語のあれこれ6(条件に関する演算子と条件分岐、繰り返し文の作成方法)
以下の順番で作成していきます。
- 変数と定数の作成方法
- 基本的な値型の種類と構造体の作成方法
- 関数の作成方法
- 計算するための演算子の説明
- 条件に関する演算子と条件分岐、繰り返し文の作成方法
- 配列を伴わないポインタの使用方法 <=今回の記事
- 配列を伴ったポインタの使用方法
- プリプロセッサの利用方法
追記
モチベーションが続いているうちに作り切ってしまおうと考えたため、連投します。(3回目)
本編
ここではポインタに関して記述します
ポインタとは
パソコンにはすぐに操作可能なメモリとすぐにアクセスはしないけど情報として保存するためのメモリがあります。
RAM(Random Access Memory)
レジスタに次ぐ一時的に利用するすぐに操作可能なメモリです。
よく言われるメモリ番地はこちらを指します。
ROM(Read Only Memory)
HDD(Hard Disk Drive)やSSD(Solid State Drive)など長期にかけて情報を保持する際にるようするメモリです。
よく言われるメモリーカードはこちらを指します。
ポインタはこのRAMのメモリ番地を保存するための変数を指します。
これらを利用するのは3つあります。
関数での操作を保持するため
基本的に関数で引数に指定した変数を新しく作るように動きます。
つまり、以下の変数は関数から出ると消えてしまいます。
- 引数
- 関数内で定義されたスタティック変数以外のローカル変数
関数を呼ぶ前の情報は残っていても関数に入る前に利用していた変数は利用することができません。
また、引数は値渡し(新しい変数を作成して引数として指定した値を保持させること)を行っているため、関数を抜けると変更した値も消えてしまいます。
そこで値そのものを渡すのではなく、値を確認できる場所を渡そうと考えたためポインタを利用します。
関数で利用する引数の容量を下げるため
構造体を思い浮かべていただけるとわかる通り、メモリ(RAM)をすごい量利用する場合があります。
そのため構造体の場所を保持するポインタを利用することで構造体で利用するメモリをまるまる用意せずに、
4byte(場合によっては8byte)分のみに抑えられるポインタを利用して処理を少し早めることができます。
(実際中ではメモリ上の場所を確保する場合は、利用するメモリ分空いている場所を必要とするためです。)
配列や文字列を可変長にするため
こちらは関数と少し違う形になります。
ポインタを利用した場合は配列のサイズや文字列のサイズを指定せずに作成することができます。
なぜこうなるのかといいますと以下の内容になります。
配列を作成する場合ははじめにその個数を必要とするため
配列を利用する場合は配列分のメモリ確保が必要なため先にその個数を指定する必要があります。
ポインタを利用してallocなどの関数を用いることであとからサイズを指定した配列を取得することができます。
文字列はchar型の配列であるため
文字列はC言語ではstring型がなく、char型の配列を用意する必要があります。
上記のように配列を作成する場合は先に必要個数のメモリを確保する必要があり、一度作ると配列のサイズを変更できません。
そこで、char* test = ""のように記述すると作成した文字列の先頭アドレス(メモリ番地)を保持することで可変長のように見せることが可能になります。
この時用意された値はポインタとして保持しない限りスコープから抜けると解放されるので注意が必要です。
char charactor = 'a';
char* str = "aaaaaaa";
ポインタの作成方法
例として以下の構造体を用意しておきます。
typedef struct TestStruct
{
int num;
}TestStruct;
構造体の作成方法は値型または構造体に対して*をつけるだけです。
基本的にはNULL または 0で初期化します。
int* test = 0;
TestStruct* testStruct = 0;
値型ポインタの値を格納および取得する方法
アドレス番地を見ているだけでは利用できないため、次のようにする必要があります。
値の取得
同じ型の変数名 = *ポインタ変数名;
値の格納
*ポインタ変数名 = 同じ型の値;
以下は例になります。
int val2 = *test;
TestStruct structVal2 = *testStruct;
*test = 20;
*testStruct = structVal2;
構造体ポインタのメンバー変数の格納および取得する方法
構造体ポインタも値型と同じようにアドレス番地のままでは利用できないため、次のようにする必要があります。
値の取得
同じ型の変数名 = ポインタ変数名->メンバー変数;
値の格納
ポインタ変数名->メンバー変数 = 同じ型の値;
以下は例になります。
val2 = testStruct->num;
testStruct->num = val2;
void ポインタ
voidのポインタを指定するとどんな型でも入る特殊な型になります。
ただし、通常のポインタのように値を取り出すことはできないため、値型や構造体のポインタに一度変換してから利用する必要があります。
この時、voidポインタを格納する型はどんなものでもいいため、値の取扱い方には注意が必要です。
void* v_pointer = test;
v_pointer = testStruct;
char* val3 = (char*)v_pointer;
Discussion