ポインタとは

ぽいんた

変数や配列の要素、関数などが置かれているメモリの番地を値として持つ変数。C言語では int *p のように宣言し、&x で x の番地を取り出し、*p で p が指す先の値を読み書きする。ポインタに整数を足すと、指す型の大きさを単位に進む。配列の名前は多くの式で先頭の要素へのポインタに変換され、a[i] は *(a + i) と同じ意味になる。どこも指さないことを表す値を NULL ポインタという。

「ポインタ」は、7分野の25問の練習問題に出てきます(C言語の基礎, データ構造とアルゴリズム, 命令セット・アセンブリなど)。

この用語の分野: C言語の基礎

宣言と基本の操作

int *p; は「int を指すポインタ」p の宣言です。p = &x; で変数 x の番地を入れると、*p = 5; は x に5を書き込みます。関数の引数は値渡しなので、呼び出し側の変数を関数の中で変えたいときは、その番地をポインタとして渡します。構造体へのポインタからメンバを使うときは、(*p).m を p->m と書きます。

ポインタの演算と配列

int へのポインタ p に1を足すと、番地は1バイトではなく sizeof(int) バイト(多くの環境で4バイト)進みます。配列の名前は、sizeof や & の対象になる場合などを除いて、先頭の要素へのポインタに変換されます。そのため関数の引数に配列を渡すと、実際には先頭の番地だけが渡り、関数の中の sizeof では配列全体の大きさは分かりません。

組込みでの使い方と注意

マイコンでは、周辺回路のレジスタの番地を (volatile uint32_t *)0x40020014 のように整数からポインタに変換して読み書きします。初期化していないポインタ、NULL ポインタ、解放した領域を指すダングリングポインタを通して読み書きすることは未定義動作で、OSのない環境では気づかないまま別のデータを壊すことがあります。

ポインタの練習問題に挑戦

配列と比べたときの連結リスト(各要素が次の要素の位置を指すポインタを持つリスト)の特徴として正しいものはどれか。

  1. A挿入する位置の直前の要素が分かっていれば、ほかの要素を動かさずにポインタの付け替えだけで挿入できるが、i 番目の要素を読むには先頭から順にたどる必要がある
  2. Bi 番目の要素の位置を計算で求められるので、どの要素も一定の時間 O(1) で読み出せる
  3. C要素がメモリ上に隙間なく連続して並ぶので、順に読むときにキャッシュやプリフェッチが効きやすい
答えと解説を見る

正解:A. 挿入する位置の直前の要素が分かっていれば、ほかの要素を動かさずにポインタの付け替えだけで挿入できるが、i 番目の要素を読むには先頭から順にたどる必要がある

連結リストでは、挿入や削除の位置が分かっていれば、前後のポインタを書き換えるだけで O(1) で済みます。一方、i 番目の要素を読むには先頭から i 回ポインタをたどる必要があり、O(n) かかります。配列は先頭の番地と要素の大きさから i 番目の番地を計算できるので O(1) で読めますが、途中への挿入には後ろの要素をずらす O(n) の手間がかかります。また、配列は要素が連続して並ぶためキャッシュやプリフェッチが効きやすく、メモリ上に散らばりやすい連結リストは、同じ O(n) の走査でも大幅に遅くなることがあります。

int は4バイト、ポインタは8バイトとする。int 型へのポインタ p の値(番地)が 0x1000 のとき、p + 3 の値はどれか。

  1. A0x100C
  2. B0x1012
  3. C0x1018
答えと解説を見る

正解:A. 0x100C

ポインタに整数を足すと、指している型の大きさを単位として進みます。int は4バイトなので、p + 3 は 3 × 4 = 12 バイト先、つまり 0x1000 + 0xC = 0x100C を指します。p + 3 は配列の要素 p[3] の番地で、*(p + 3) と p[3] は同じ意味です。char 型へのポインタなら1バイトずつ、double 型(8バイト)へのポインタなら8バイトずつ進みます。

よくある質問

ポインタの大きさはいくつですか?

多くの64ビットの環境では8バイト、32ビットのマイコンでは4バイトです。指す先の型が char でも double でも、データへのポインタの大きさは一般に同じです。ポインタの大きさは int の大きさと同じとは限らないので、番地を整数に入れるときは stdint.h の uintptr_t を使います。

const char *p と char *const p は何が違いますか?

const char *p は「const な char を指すポインタ」で、p を通して文字を書き換えることはできませんが、p 自体は別の番地に変えられます。char *const p は「ポインタ自体が const」で、p は変えられませんが、指す先は書き換えられます。const が * の左にあれば指す先、右にあればポインタ自体が固定されると読みます。

解説の中の「ポインタ」

「ポインタ」が出てくる問題(25問)

  1. 配列と比べたときの連結リスト(各要素が次の要素の位置を指すポインタを持つリスト)の特徴として正しいものはどれか。
  2. int は4バイト、ポインタは8バイトとする。int 型へのポインタ p の値(番地)が 0x1000 のとき、p + 3 の値はどれか。
  3. int は4バイト、ポインタは8バイトとする。上のプログラムを実行したときの出力として、正しいものはどれか。
  4. 上の関数 make が返すポインタについて、正しいものはどれか。
  5. C言語で NULL ポインタを参照した(p が NULL のときに *p を読んだ)ときの説明として、正しいものはどれか。
  6. RISC-Vの標準の呼び出し規約で、整数の引数が10個ある関数を呼ぶとき、9個目と10個目の引数はどこで渡されるか。
  7. ABI(アプリケーションバイナリインターフェース)の説明として正しいものはどれか。
  8. C言語の関数ポインタを使った呼び出し「(*fp)(x)」は、RISC-Vでは一般にどの命令で行われるか。
  9. 上の関数で、コンパイラが *b の読み込みをループの外に出す最適化を、そのままでは行えない理由はどれか。
  10. コンパイラの自動ベクトル化で使われる命令の種類として正しいものはどれか。
  11. プログラムが、OSが割り当てていないアドレスにアクセスしたときに一般的に起こることはどれか。
  12. CASを使ったロックフリーのデータ構造で問題になるABA問題の説明として正しいものはどれか。
  13. CUDAのユニファイドメモリ(統合メモリ)の説明として正しいものはどれか。
  14. CUDAで、GPUカーネル(__global__を付けた関数)が計算した結果を、CPU側のプログラムで受け取る方法として正しいものはどれか。
  15. 二分ヒープを、根を a[0] とする配列 a[0], a[1], a[2], … に、上の段から順に左から詰めて格納する。a[4] の子と親はどれか。

C言語の基礎の参考書を探す PR

Amazonの検索結果へのリンクです。Amazonのアソシエイトとして、半導体教室は適格販売により収入を得ています。

関連する用語

配列 未定義動作 引数 変数 連結リスト スタック ページフォルト マイコン(マイクロコントローラ) プリフェッチ 動的メモリ確保

用語集の一覧へ