Index

HOME > プログラムTOP > C++




C++の基礎知識()

【ポインタ篇】

| ●宣言と代入 | ●ポインタと配列 | ●ポインタの計算 | ●関数ポインタ |
( 記述篇, データ型篇, 定数篇, 変数篇, 演算子篇, 関数篇, 制御文篇, 構造体篇, クラス篇, 番外篇 )

sasaraan programming

Exposition

●宣言と代入

 通常の変数はデータの値を保持しますが、ポインタは値がある場所(アドレス)を保持します。ポインタを表現する演算子には以下の二つが用意されています。なお、データ値とアドレスとは代入し合うことはできません。

* ・・・ アドレスにある値を示す
& ・・・ アドレスを示す

 ポインタは、上記の二つの演算子を用いて宣言します。この時、NULL を指定( int *p; p = NULL; )すると、どのアドレスも参照しない安全なポインタ宣言をすることができます。
種類宣言アドレス
変数int valuevalue&value
ポインタint *value (int* value でも可)*valuevalue
 ポインタは、アドレスを指定してから使用する必要があります。ポインタのデータ値を変えると、同じアドレスを指すすべての変数のデータ値も変わりますので注意が必要です。
void main() {   /* ポインタの操作例 */
    int n = 99; // 変数の宣言
    int *p;       // ポインタの宣言 (アドレス未確定のため、ここでの値の代入は危険)
    p = &n;      // ポインタにアドレスを指定(必須) : *p=99, n=99 (同じアドレスを参照)
    *p = 123;   // ポインタに値を代入 : *p=123, n=123 (nの値も変わる)
    n = 88;      // 変数に値を代入 : *p=88, n=88 (*pの値も変わる)
    n = *p;      // 変数にポインタが示す値を代入 : *p=88, n=88 (すでに同じアドレス)
}
 ポインタのポインタを宣言することもできます。これはあるポインタの配置されている場所(アドレス)を格納する時に利用されます。
void main() {    /* ポインタのポインタの例 */
    int s1[3] = {33, 44, 55} ;    // 配列の宣言
    int s2[3] = {77, 88, 99} ;    // 配列の宣言
    int *p[2] = {s1, s2} ;         // ポインタの配列の宣言
    int **t;     // "ポインタのポインタ" の宣言
    t = p;       // "ポインタのポインタ" に "ポインタの配列のアドレス" を代入
}

●ポインタと配列

 配列は解釈上はポインタと同じです。下記で宣言した配列 a[4] は、*a と同様の解釈をすることができます。
void main() {
    int a[4] = {33, 44, 55, 66} ;  // 通常の配列の宣言
    int *p = a ;        // a (配列名)は配列の先頭要素のアドレスを指す
    int i;
    for (i=0; i<4; i++) printf("%p, %d, %d, %d\n", p+i, a[i], *(p+i), p[i]);  // 出力
} 
 この時、配列 a とポインタ *p との関係は次のようになります。
アドレス配列ポインタ1ポインタ2
&a[0] = p+0 = 0012FE74a[0]*pp[0]33
&a[1] = p+1 = 0012FE78a[1]*(p+1)p[1]44
&a[2] = p+2 = 0012FE82a[2]*(p+2)p[2]55
&a[3] = p+3 = 0012FE86a[3]*(p+3)p[3]66
・アドレス ・・・ int型なので4バイト(32ビット)毎にメモリが確保される(32ビット環境の時)
・配 列  ・・・ 配列変数での表現は添え字を使う
・ポインタ1 ・・・ ポインタでの表現(+は*より優先度が低いので括弧が必要)
・ポインタ2 ・・・ ポインタをアドレスの配列として記述することも可能
 文字列も文字の配列ですので同じように表現できます。ただし、最後に \0 があるので注意が必要です。また、文字列の場合、アドレスを代入せずに初期化が可能です。
void main() {
    char s[6] = "HELLO" ;    // 通常の文字列の宣言
    char *p = "HELLO" ;     // 文字列のポインタは初期化可(定数扱い)
    int i;
    for (i=0; i<6; i++) printf("%p, %c, %c, %c\n", p+i, s[i], *(p+i), p[i]);  // 出力
} 
 この時、文字列 s とポインタ *p との関係は次のようになります。
アドレス配列ポインタ1ポインタ2
&s[0] = p+0 = 0012FED0s[0]*pp[0]'H'
&s[1] = p+1 = 0012FED1s[1]*(p+1)p[1]'E'
&s[2] = p+2 = 0012FED2s[2]*(p+2)p[2]'L'
&s[3] = p+3 = 0012FED3s[3]*(p+3)p[3]'L'
&s[4] = p+4 = 0012FED4s[4]*(p+4)p[4]'O'
&s[5] = p+5 = 0012FED5s[5]*(p+5)p[5](\0)

●ポインタの計算

 ポインタは計算が可能です。ただし、加算(+)、減算(-)、インクリメント(++)、デクリメント(--)の四種類に限られます。
void main() {
    int n[4] = {50, 60, 70, 80} ;  // int型の配列(32ビット環境時では、1要素は4バイト)
    int *p ;
    p = n ;
    printf("%d\n", *p);       // = 50 : n[0]の値
    printf("%d\n", *p+1);    // = 51 : "*pの値" + 1
    printf("%d\n", *(p+1));  // = 60 : p + "1要素"のアドレスを参照  (n[1] を参照)
    printf("%d\n", (*p)++);  // = 50 : 計算後に "*pの値" + 1    (n[0]=51 に変更)
    printf("%d\n", *p++);    // = 51 : 計算後に p + "1要素" のアドレス (n[1] に移動)
    printf("%d\n", ++(*p));  // = 61 : = ++(*p) : "*pの値" + 1   (n[1]=61 に変更)
    printf("%d\n", *++p);    // = 70 : = *(++p) : p + "1要素" のアドレス(n[2] に移動)
}

●関数ポインタ

 関数にもポインタを利用することができます。()を含まない関数名は、その関数があるアドレスを指しますので、関数ポインタの宣言後は関数名を代入して使用します。。この時、引数を囲む()は、* よりも優先度が高いので宣言時は関数名を()で囲む必要があります。
void func_A(int m) ;        // 関数func_Aのプロトタイプ宣言(実体定義は略)
void func_B(int m) ;        // 関数func_Bのプロトタイプ宣言(実体定義は略)
void (*funt_temp)(int m) ;    // 関数ポインタの宣言
void main() {
    int n = 100 ;
    func_temp = func_A ;  // func_Aのアドレスを格納
    func_temp(n);            // func_A の実行
    func_temp = func_B ;  // func_Bのアドレスを格納
    func_temp(n);            // func_B の実行
}
 また、関数ポインタの配列を宣言することもできます。それぞれの要素には、同じ引数を持つ異なる関数のアドレスを格納することになります。

void (*funt_temp[5])(int m) ; // 関数ポインタの配列の宣言

www.sasaraan.net

(c) morijoh