目次
1 ポインタの基本概念
1 アドレス: メモリ内の異なるバイトを区別するために使用される番号
2 ポインタ: アドレスはポインタ、ポインタはアドレス
3 ポインタ変数:アドレスを格納する変数
4 &: このシンボルは、メモリ空間内の変数の最初のアドレスを取得し、式の型をアップグレードするために使用されます。たとえば、int型--->>int*型です。
5 *: この記号が = 式の右側にある場合、ポインタが指す空間の値を取得することを意味します (取得される空間のサイズはポインタの種類によって異なります)。式 = の左側にある場合は、ポインタが指す空間に右側の値が代入されることを意味します。式のタイプをダウングレードする効果もあります。たとえば、int* 型--->>int 型です。
次の図に示すように、整数 100 を格納するために 4 バイトが使用され、整数変数の最初のアドレスへのポインタを格納するために 8 バイトが使用されます。
2 セカンダリ ポインタ
変数のアドレスを格納する変数をポインタと呼びますが、ポインタを格納したいポインタはどうなるでしょうか? 次に、セカンダリ ポインタを使用します。
整数 = 0;
int *pnum = #num;
int **ppnum = &pnum;
説明するプログラムはこちら
#include <stdio.h>
int main(int argc, char const *argv[])
{
int num = 100;
int *pnum = #
int **ppnum = &pnum;
printf("num = %d\n",num);
printf("*pnum = %d\n",*pnum);
printf("**ppnum = %d\n",**ppnum);
printf("&num = %p\n",&num);
printf("pnum = %p\n",pnum);
printf("*ppnum = %p\n",*ppnum);
printf("&pnum = %p\n",&pnum);
printf("ppnum = %p\n",ppnum);
return 0;
}
ただし、セカンダリ ポインターは C の 2 か所で使用されます。
1 関数本体が関数本体の外でポインタ変数の値を変更したい場合、ポインタ変数のアドレスは第 2 レベルのポインタになります。
2 ポインター配列にパラメーターを渡す場合、配列の配列名は配列の最初のポインター要素へのポインターになります。
3 ポインタの算術演算
+ - ++ --
ポインタのオフセットは、ポイントされたデータ型のバイト空間のサイズです。
int* :4
文字* :1
ダブル* :8
int** :8
4 つの無効ポインタ
ボイド *p;
多くの場合、ストレージ メモリのアドレスを指すために使用され、領域のサイズを指す意味はありません。
void* 型のポインターは、* または ++ -- のフェッチに関連する操作には使用しないでください。
void * 型ポインターと他の型のポインター間の変換には、必須の型変換は必要ありません。
使用
関数のパラメーターとして、またはすべての型と互換性のあるポインターを表す関数の戻り値として使用されます。
1 memcpy (void *dest, const void *src, size_t n); メモリコピー関数のパラメータ定義
2 memcmp(const void *s1,const void *s2,size_t n);
質問例: メモリアドレスが 0x2000 の空間に整数 100 を代入してください (0x2000 は void * 型のアドレスです)
(*(int *)((void*)0x2000)) = 100; または (* (int *)0x2000) = 100;
5つの定数ポインタ
1. const int *p;
2. int const *p;
3. int *const p;
4. const int *const p;
5. int const *const p;
1 と 2 は同等で、const は *p によって変更され、p が指すスペースは変更できません (読み取り専用) が、p は変更できます。(strcpy 関数の const char *src パラメーターなど、ソースを変更することはできません)。使用することはできますが、変更することはできません。
3では、constはpによって変更され、pは変更できませんが、*pは変更できるため、初期化する必要があります。プログラムでは、初期化時にpはスペースのみを指すことができ、ポイントすることはできません。他の空間へのポインターですが、p 値が指す空間を変更するために使用できます。(たとえば、配列の配列名は常に最初の要素の最初のアドレスを指し、最初の要素の値を変更できます)。
4と5は同等で、constはpと*pで変更され、ポインタが指すアドレスと空間は変更できません。初期化する必要があります。
6 ポインタ配列と配列ポインタ
ポインタの 1 つの配列
ポインタの配列:
ポインタの配列は配列、つまりポインタの配列であり、この配列内の各要素はポインタです。
int *p[5];
たとえば、上記では配列 p が定義されており、配列には 5 つの要素があり、各要素は 8 バイト、合計 40 バイトで、各要素は整数変数へのポインターです。
C 言語では、通常、整数ポインター配列はあまり使用されず、文字ポインター配列 (char *pstr[5]) が使用されます。
理解するためのコード
#include <stdio.h>
int main(int argc, char const *argv[])
{
char *pstr[5] = {"hello","world","how","are","you",};
for(int i = 0;i < 5;i++)
{
printf("str[%d] = %s\n",i,pstr[i]);
}
return 0;
}
一般に、2 次元配列は文字列の格納に使用され、ポインタの配列は文字列の操作に使用されます。
たとえば、バブル ソートを使用して文字列の配列を並べ替えます。この場合、文字列の代わりにアドレスが交換されます。
以下のコード例に示すように、
in(int argc, char const *argv[])
{
char *pstr[5] = {"hello","world","how","are","you",};
int i = 0;
int j = 0;
char *ptmp = NULL;
for(i = 0;i < 5 -1;i++)
{
for(j = 0;j < 5 -1 - i;j ++)
{
if(strcmp(pstr[j],pstr[j + 1]) > 0)
{
ptmp = pstr[j];
pstr[j] = pstr[j + 1];
pstr[j +1] = ptmp;
}
}
}
for(i = 0; i < 5; i++)
{
printf("%s\n",pstr[i]);
}
return 0;
}
2 つの配列ポインタ
配列ポインタはポインタであり、ポインタは配列を指し、ポインタは 8 バイトを占有します。
int (*a)[5];
int a[5];
&a:int (*)[5]
1 1 次元配列の配列名と演算の場合、値不変型は配列ポインタにアップグレードされます。
2 配列ポインタに対する * 演算の場合、値不変型は配列の最初の要素へのポインタにダウングレードされます。
コードを説明する
#include <stdio.h>
int main(int argc, char const *argv[])
{
int (*p)[5] = NULL;
int a[5] = {1,2,3,4,5,};
p = &a;
printf("a = %p\n",a);
printf("&a = %p\n",&a);
printf("*a = %d\n",*a);
printf("*&a = %p\n",*&a);
printf("========================\n");
printf("p = %p\n",p);
printf("p + 1 = %p\n", p+1);
printf("*p = %p\n",*p);
printf("*p + 1 = %p\n", *p+1);
return 0;
}
7 ポインタと配列の関係
1 ポインタと 1 次元配列の関係
int a[5] = {1,2,3,4,5};
配列の array-name は、配列の最初の要素へのポインタです。
a = &a[0]
配列要素にアクセスする方法: a[n] = *(a + n)
2 ポインタと二次元配列の関係
2次元配列の配列名aは、配列の1行目の要素を指す配列ポインタである。
#include <stdio.h>
int main(int argc, char const *argv[])
{
int a[2][3] = {1,2,3,4,5,6};
printf("================================\n");
printf("&a[0][0] = %p\n",&a[0][0]);
printf("&a[0][1] = %p\n",&a[0][1]);
printf("&a[0][2] = %p\n",&a[0][2]);
printf("&a[1][0] = %p\n",&a[1][0]);
printf("&a[1][1] = %p\n",&a[1][1]);
printf("&a[1][2] = %p\n",&a[1][2]);
printf("================================\n");
printf("a = %p\n",a);
printf("a + 1 = %p\n",a + 1);
printf("================================\n");
printf("a[0] = %p\n",a[0]);
printf("a[0] + 1 = %p\n",a[0] + 1);
printf("a[1] = %p\n",a[1]);
printf("a[1] + 1 = %p\n",a[1] + 1);
printf("================================\n");
printf("*a[0] = %d\n",*a[0]);
printf("*(a[0] + 1)= %d\n",*(a[0] + 1));
printf("*a[1] = %d\n",*a[1]);
printf("*(a[1] + 1) = %d\n",*(a[1] + 1));
printf("================================\n");
printf("*a = %p\n",*a);
printf("*(a + 1) = %p\n",*(a + 1));
return 0;
}
配列要素にアクセスする 3 つの方法
a[m][n] = *(a[m] + n) = *(*(a + m) + n)
*(p + m * N + n)