C ポインタの算術演算

C ポインタ

C のポインタは数値で表されるアドレスです。したがって、ポインタに対して算術演算を実行できます。ポインタに対しては、++、--、+、- の 4 つの算術演算を実行できます。

仮にptrptr はアドレス 1000 を指す整数型ポインタであり、int は 32 ビット整数です。このポインタに対して次の算術演算を実行してみましょう。

ptr++

上記の演算を実行した後、ptrptr は位置 1004 を指すことになります。ptr が 1 回インクリメントされるたびに、次の整数の位置を指すことになり、つまり現在の位置から 4 バイト後ろに移動します。この演算は、メモリ位置にある実際の値に影響を与えることなく、ポインタを次のメモリ位置に移動させます。もしptrアドレス 1000 の文字を指している場合、上記の演算によりポインタは位置 1001 を指すことになります。次の文字の位置が 1001 だからです。

まとめると次のようになります:

  • ポインタをインクリメントするたびに、実際には次の要素の記憶領域を指すことになります。
  • ポインタをデクリメントするたびに、前の要素の記憶領域を指すことになります。
  • ポインタがインクリメント/デクリメントされるときに移動するバイト数は、ポインタが指す変数のデータ型の長さに依存します。たとえば int は 4 バイトです。

ポインタをインクリメントする

ポインタをインクリメントするとは、ポインタを次のメモリ位置に指し示すことを意味します。

ポインタのインクリメント操作は、ポインタが指すデータ型に応じて適切なメモリオフセットを行います。

プログラムでは配列の代わりにポインタを使用することがよくあります。変数ポインタはインクリメントできますが、配列はインクリメントできないためです。配列はポインタ定数とみなせます。次のプログラムでは、変数ポインタをインクリメントして、配列の各要素に順番にアクセスしています:

を指し、値は 40

#include <stdio.h> const int MAX = 3; int main () { // 整数配列を定義する int var[] = {10, 100, 200}; // 整数変数 i と整数ポインタ ptr を定義する int i, *ptr; // ポインタ ptr を配列 var の先頭アドレスに指し示す ptr = var; // 配列をループで走査する for ( i = 0; i < MAX; i++) { // 現在のポインタ ptr が指すアドレスを表示する printf("格納アドレス:var[%d] = %p\n", i, ptr ); // 現在のポインタ ptr が指すアドレスの値を表示する printf("格納値:var[%d] = %d\n", i, *ptr ); // ポインタ ptr を次の配列要素の位置に移動する ptr++; } return 0; }

上記のコードをコンパイルして実行すると、次の結果が生成されます:

存储地址:var[0] = e4a298cc
存储值:var[0] = 10
存储地址:var[1] = e4a298d0
存储值:var[1] = 100
存储地址:var[2] = e4a298d4
存储值:var[2] = 200

文字ポインタをインクリメントする:

例

#include <stdio.h>

int main() {
    char str[] = "Hello";
    char *ptr = str;  // ポインタを文字列の最初の文字に指し示す

    printf("初期文字: %c\n", *ptr);  // H を出力する

    ptr++;  // ポインタをインクリメントして、次の文字を指すようにする
    printf("インクリメント後の文字: %c\n", *ptr);  // e を出力する

    return 0;
}

この例では、ptr++ によってポインタが str

上記のコードをコンパイルして実行すると、次の結果が生成されます:

初始字符: H
递增后字符: e

構造体ポインタをインクリメントする:

例

#include <stdio.h>

struct Point {
    int x;
    int y;
};

int main() {
    struct Point points[] = {{1, 2}, {3, 4}, {5, 6}};
    struct Point *ptr = points;  // ポインタを構造体配列の最初の要素に指し示す

    printf("初期点: (%d, %d)\n", ptr->x, ptr->y);  // (1, 2) を出力する

    ptr++;  // ポインタをインクリメントして、次の構造体を指すようにする
    printf("インクリメント後の点: (%d, %d)\n", ptr->x, ptr->y);  // (3, 4) を出力する

    return 0;
}

この例では、ptr++ によってポインタが points

上記のコードをコンパイルして実行すると、次の結果が生成されます:

初始点: (1, 2)
递增后点: (3, 4)

ポインタをデクリメントする

ポインタをデクリメントするとは、ポインタを前のメモリ位置に指し示すことを意味します。ポインタのインクリメントと同様に、ポインタのデクリメント操作も、ポインタが指すデータ型に応じて適切なメモリオフセットを行います。

ポインタに対してデクリメント演算を行うと、そのデータ型のバイト数だけ値が減算されます。次のようになります:

例

#include <stdio.h> int main() { int arr[] = {10, 20, 30, 40, 50}; int *ptr = &arr[4]; // ポインタを配列の最後の要素に指し示す printf("初期値: %d\n", *ptr); // 50 を出力する ptr--; // ポインタをデクリメントして、前の整数要素を指すようにする printf("デクリメント後の値: %d\n", *ptr); // 40 を出力する ptr--; // もう一度ポインタをデクリメントする printf("再度デクリメントした後の値: %d\n", *ptr); // 30 を出力する return 0; }

上記のコードをコンパイルして実行すると、次の結果が生成されます:

初始值: 50
递减后值: 40
再次递减后值: 30

文字ポインタをデクリメントする:

例

#include <stdio.h>

int main() {
    char str[] = "Hello";
    char *ptr = &str[4];  // ポインタを文字列の最後の文字 'o' に指し示す

    printf("初期文字: %c\n", *ptr);  // o を出力する

    ptr--;  // ポインタをデクリメントして、前の文字を指すようにする
    printf("デクリメント後の文字: %c\n", *ptr);  // l を出力する

    ptr--;  // もう一度ポインタをデクリメントする
    printf("再度デクリメントした後の文字: %c\n", *ptr);  // l を出力する

    return 0;
}

上記のコードをコンパイルして実行すると、次の結果が生成されます:

初始字符: o
递减后字符: l
再次递减后字符: l

構造体ポインタをデクリメントする:

例

#include <stdio.h>

struct Point {
    int x;
    int y;
};

int main() {
    struct Point points[] = {{1, 2}, {3, 4}, {5, 6}};
    struct Point *ptr = &points[2];  // ポインタを構造体配列の最後の要素に指し示す

    printf("初期点: (%d, %d)\n", ptr->x, ptr->y);  // (5, 6) を出力する

    ptr--;  // ポインタをデクリメントして、前の構造体を指すようにする
    printf("デクリメント後の点: (%d, %d)\n", ptr->x, ptr->y);  // (3, 4) を出力する

    ptr--;  // もう一度ポインタをデクリメントする
    printf("再度デクリメントした後の点: (%d, %d)\n", ptr->x, ptr->y);  // (1, 2) を出力する

    return 0;
}

上記のコードをコンパイルして実行すると、次の結果が生成されます:

初始点: (5, 6)
递减后点: (3, 4)
再次递减后点: (1, 2)

ポインタの比較

C 言語では、ポインタを比較してそれらの関係を判定できます。ポインタの比較は、主に 2 つのポインタが同じメモリ位置を指しているかどうか、または一方のポインタが他方のポインタより前か後ろかを判定するために使用されます。

ポインタは関係演算子を使用して比較できます。たとえば、==、!=、<、>、<=と>=p1 と p2 が関連する 2 つの変数(たとえば同じ配列内の異なる要素)を指している場合、p1 と p2 の大小比較ができます。

以下は、ポインタを比較して結果を出力する方法を示すサンプルコードです。

ポインタの等価比較:

例

#include <stdio.h> int main() { int a = 5; int b = 10; int *ptr1 = &a; int *ptr2 = &a; int *ptr3 = &b; if (ptr1 == ptr2) { printf("ptr1 と ptr2 は同じメモリアドレスを指しています\n"); // この行が出力されます } else { printf("ptr1 と ptr2 は異なるメモリアドレスを指しています\n"); } if (ptr1 != ptr3) { printf("ptr1 と ptr3 は異なるメモリアドレスを指しています\n"); // この行が出力されます } else { printf("ptr1 と ptr3 は同じメモリアドレスを指しています\n"); } return 0; }

上記のコードをコンパイルして実行すると、次の結果が生成されます:

ptr1 和 ptr2 指向相同的内存地址
ptr1 和 ptr3 指向不同的内存地址

ポインタの大小比較:

例

#include <stdio.h>

int main() {
    int arr[] = {10, 20, 30, 40, 50};
    int *ptr1 = &arr[1];  // arr
    int *ptr2 = &arr[3];  // arr

    if (ptr1 < ptr2) {
        printf("ptr1 は ptr2 より前にある\n");  // この行が出力されます
    } else {
        printf("ptr1 は ptr2 より後ろ、または同じ位置にある\n");
    }

    if (ptr1 > ptr2) {
        printf("ptr1 は ptr2 より後ろにある\n");
    } else {
        printf("ptr1 は ptr2 より前、または同じ位置にある\n");  // この行が出力されます
    }

    return 0;
}

上記のコードをコンパイルして実行すると、次の結果が生成されます:

ptr1 在 ptr2 之前
ptr1 在 ptr2 之前或相同位置

配列を走査してポインタを比較する:

例

#include <stdio.h>

int main() {
    int arr[] = {10, 20, 30, 40, 50};
    int *start = arr;           // 配列の最初の要素を指す
    int *end = &arr[4];         // 配列の最後の要素を指す
    int *ptr;

    for (ptr = start; ptr <= end; ptr++) {
        printf("現在のポインタが指す値: %d\n", *ptr);
    }

    return 0;
}

上記のコードをコンパイルして実行すると、次の結果が生成されます:

当前指针指向的值: 10
当前指针指向的值: 20
当前指针指向的值: 30
当前指针指向的值: 40
当前指针指向的值: 50

まとめ

  • 等価比較(==)==と!=):2 つのポインタが同じメモリ位置を指しているかどうかを判定します。
  • 大小比較(<、>)<, >, <=, >=):通常、ポインタが配列やメモリブロックを走査するときに、一方のポインタが他方のポインタより前か後ろかを判定するために使用されます。
注意すべき点として、ポインタの比較は、同じ配列または同じメモリブロックを指している場合にのみ意味を持ち、それ以外の場合は動作が未定義になります。

C ポインタ

その他の拡張