ポインタを徹底解説!C言語を始める前に知っておきたい基礎知識
「ポインタって何のこと?」
「ポインタを使う場面を知りたい」
と思っていませんか?
C言語を勉強し始めたけど、ポインタについて理解できないと勉強が思うように進まないですよね。
では、ポインタを理解できればどのようなことに使えるのでしょうか?
そこで今回は、
- ポインタとは何か
- ポインタの使い方と定義方法
- ポインタ利用時によくあるエラー
について詳しく解説します。
この記事を見れば、ポインタを使ったC言語のコーディングが簡単になります。
これからC言語について理解を深めていく方や、ポインタの概念を理解したい方は、ぜひ最後まで読んでみてくださいね。
ポインタとは:指定された場所にある情報を使うこと
ポインタとはコンピュータ内に記憶されている情報を取り出すために、その情報の場所を指すプログラミングの文法のことです。
コンピュータに記憶された情報は必要のたびにその情報がある場所まで値を取得してきますが、必要のたびにすべての情報の中から探し出すのはコンピュータに負担がかかります。
そこでポインタを使ってあらかじめ情報がある場所を指し示すことで、計算に時間がかからず速い情報処理が可能になりました。
コンピュータに考える時間を減らさせるためにできたプログラミングの文法を、ポインタといいます。
また、今回の「ポインタ」のようなIT用語を理解していると、プログラミングの学習もスムーズになることがあります。
もしあなたがIT業界に興味があるのなら【DMM WEBCAMP】でプログラミング学習をすることをおすすめします!
国内最大規模プログラミングスクール【DMM WEBCAMP】は、未経験者向けのカリキュラムやライフコーチによる学習サポートなど、安心して学習が続けられる環境が整っています。
「なんとかして転職を成功させたい」「まずは基礎的なスキルだけつけたい」など、あなたのニーズに合わせて3つのコースから学習スタイルを選べるので、ぜひあなたに合った学習方法でIT業界の転職・就職の夢を実現させてください!
「今の働き方に不満はあるけど、日々の業務が忙しくてゆっくり考える時間がない…」
そんな悩みを持つ方に向けて【DMM WEBCAMP】では無料のキャリア相談を実施しています。
ビデオ通話で相談をすることができるため、仕事で忙しい方でもスキマ時間に気軽にカウンセリングを受けることも可能です!
プロのキャリアカウンセラーと一緒に、今後のキャリアについて考えてみませんか?
ポインタを理解するために覚えておきたい2つ前提知識
エンジニアやプログラミングの知識がある人はポインタについてもすぐ理解できるものです。
しかしプログラミングを始めたての人がポインタを理解するのは難しいでしょう。
そこでポインタの理解を深めるために覚えておきたいIT用語について解説します。
- メモリ
- アドレス
上記の単語がわかるとポインタがわかりやすくなります。
それでは詳しく見ていきましょう。
1.メモリ:コンピュータの情報を格納する場所
メモリとはコンピュータの中にある情報を格納する場所です。
情報はメモリ上にある「番地」という区切られたスペースに格納されており、必要に応じて出し入れされます。
また、メモリ上にあるスペースは、区切られた範囲によってそれぞれの名前がついています。
- 1bit :コンピュータが扱う情報の最小単位
- 1byte:8bit集まったのも
1bitには2進数で0か1のみの情報しか格納されません。
1bitを8個集めたものを1byteと呼び、byteが1列に並んだものをメモリと呼びます。
2.アドレス:情報を格納したそれぞれの場所についた名前
アドレスとはコンピュータに記憶された情報が格納されている場所についている名前のことです。
情報を格納した場所に名前がついていることで、コンピュータが情報を探しやすくなります。
アドレスは16進数で名前がつけられており、1byteごとにひとつの名前です。
ポインタでアドレス名を指示することで、瞬時に必要な情報にアクセスすることができます。
また、そもそもIT業界が全体的に、聞き慣れない用語を使う傾向にあります。
もしあなたが、プログラミング経験がそこまで多くないのなら、下記記事でまとめているIT業界の用語集も参考になります。
専門的な用語を「なんとなく知っている」という状況で過ごしていると、思わぬトラブルになることもあるため、この機会に覚えてきましょう。
IT業界用語47選!ジャンルを4つに分けて覚えておくべき言葉を解説C言語を学ぶ上でポインタの知識が必要な3つの理由
C言語を勉強するならポインタを学ぶべき理由は、以下の3つです。
- コーディングが楽になる
- プログラムの処理速度が速くなる
- メモリを節約できる
逆にいえば、C言語を扱う上でポインタが使えないと上記の恩恵が受けられないということになります。
開発者側にもメリットがあるため、必ず覚えましょう。
そもそもC言語についての理解を深めたい方は、下記でまとめた記事を参考にしながら、理解を深めていくことをおすすめします。
こちらの記事を参考にしつつ、これからお伝えする内容も確認してみてください。
1.コーディングが楽になる
ポインタを使うとコーディングが楽になります。
なぜならポインタはメモリ上に記憶されている数式や長いコードを参照できるからです。
ポインタで参照可能なのもとして代表的な情報は以下のとおりです。
- 関数
- 数値
- 計算式
- 文字列
書くのに10行必要だった「計算式A」があったとしても、ポインタを使えば「計算式Aを参照にする」という1行で済むのでコーディングが楽です。
情報は必ずメモリ上のどこかに記憶されているため、ポインタを使って特定の情報を指定するだけで開発者は時間の短縮になります。
簡単なプログラミングであれば、そこまで労力の差はないかもしれませんが、規模感が大きくなればなるほど「塵も積もれば山」となります。
大規模なタスクの場合、ポインタを活用するか否かは、工数にも差が出てくることでしょう。
2.プログラムの処理速度が速くなる
C言語でポインタを使うことでプログラムの処理速度が速くなります。
プログラムの処理速度が速くなる理由は以下のとおりです。
- 同じ計算式を何度もしなくてもよくなる
- 工数が多い計算もひとつのアドレスで済む
必要な情報が格納されているアドレスがわかっていれば、メモリ上のすべての情報から探さなくてよくなります。
コンピュータのメモリは普通のパソコンでも4GB(つまりアドレス4,000,000,000個分)ほどはあるので、アドレスがわかっているだけで処理速度が速くなるのはイメージできますよね。
ポインタを使ってコンピュータに最小限の労力で計算させることで、サクサク計算できるようになります。
3.メモリを節約できる
ポインタを使えばコンピュータ上のメモリを節約できるメリットがあります。
長い計算式やメモリを多く使う情報でも、ポインタは「その情報を参照にする」という分しかメモリを使わないからです。
極端な例ですが「計算式A」という情報があり、これを格納するのにメモリの50%を使う場合を考えてみます。
- まず「計算式A」を処理する →メモリの50%消費
- 「計算式A」の答えを表示する →メモリの10%消費
上記の例だと「計算式A」の答えを表示した時点でメモリの残りが40%しかないため、もう一度「計算式A」を処理することはできません。
ここでポインタを使って「1行目の計算式Aを参照にする」という指示を使えば、その指示に使うメモリの10%程度で済みます。
巨大な情報や何度も使う情報はポインタを使うことでメモリを大幅に節約できます。
メモリは有限なので少しでもスペースを空けて、コンピュータの処理速度を上げましょう。
また、C言語の理解を深めて、スキルのあるITエンジニアを目指すことを視野に入れている方も多いことかと思います。
もしあなたがスキルのあるエンジニアを目指したいなら、【DMM WEBCAMP】がおすすめです。
受講生の97%がプログラミング未経験・初心者の【DMM WEBCAMP】では、初心者のために開発した独自のカリキュラムがあるため安心して学習が進められます。
座学での学びももちろん大切ですが、スクールを通じて実践的なノウハウを理解することで、さらにハイスキルのエンジニアへ成長することが可能です。
もちろんある程度の予算は必要ですが、今後の将来が大きく変わる可能性があるため、少しでも興味があれば、スクールの無料相談を受けてみることを検討してもいいでしょう。
あなたにあった学習スタイルで「ITエンジニア」を目指してみてください!
「今の働き方に不満はあるけど、日々の業務が忙しくてゆっくり考える時間がない…」
そんな悩みを持つ方に向けて【DMM WEBCAMP】では無料のキャリア相談を実施しています。
ビデオ通話で相談をすることができるため、仕事で忙しい方でもスキマ時間に気軽にカウンセリングを受けることも可能です!
プロのキャリアカウンセラーと一緒に、今後のキャリアについて考えてみませんか?
ポインタが使えるメリットは参照渡しができること
C言語でポインタが使えるメリットとしては参照渡しができることにあります。
参照渡しとはポインタを使って関数や式などのアドレスを取得する方法です。
一般的にポインタを使う場合は値渡しという使い方をしており、変数に値を代入ようなときに使われます。
しかし参照渡しで取得する情報は以下のとおりです。
- 配列
- 構造体
- 関数
上記の情報は定義をしてしまえば「あるアドレスにある配列をここに代入する」のような使い方ができます。
逆にC言語で参照渡しをしないと関数や配列をすべて手書きでしないといけないため、非常に面倒なコーディングが必要です。
ポインタを使うとき必要な2つの定義
ポインタを使うときに必要な定義は以下の2つです。
- ポインタ変数の定義:*
- ポインタの指定場所の定義:&
上記を定義すればポインタとして値を参照できます。
ポインタを使ってC言語をうまく使いましょう。
それでは詳しい定義方法を解説していきます。
1.「*」を使って「ポインタ変数」を定義する
ポインタを使うときにはポインタ変数というのを「*」で定義します。
定義したポインタ変数に、指定したアドレスにある値や関数を代入できるのです。
ポインタ変数を定義する例は以下のとおりです。
- 定義方法:データ型 *変数名
- 使用例 :int *p
- 意味 :[p] は int型のポインタ変数である
ポインタではただ情報があるアドレスを示すだけでなく、どんな情報があるかをデータ型で示すことでコンピュータの処理速度が速くなります。
2.「&」を使って「ポインタの指定場所」を定義する
ポインタの指定場所は、変数名の前に「&」をつけて定義します。
ポインタの指定場所を決めると、あらかじめ決めておいたポインタ変数に指定した情報を代入するという命令を与えます。
代入方法は以下のとおりです。
- 定義方法:ポインタ変数名 = &取得先変数名
- 使用例 :p = &q
- 意味 :ポインタ変数[p] に変数[q] のアドレスを代入
ポインタ変数と取得先の変数は先に定義する必要があり、そのふたつには同じデータ型を入力しましょう。
ポインタ演算で配列から特定の情報を取得する方法
ポインタ演算を使うと配列などの連続したアドレス情報からの操作が楽になります。
配列を指定した後に、その配列の先頭の情報からどれくらい移動した値を持ってくるかを指定するのがポインタ演算という使い方です。
注意すべき点は、データ型によって演算のプラスされる値が変わってくるということです。
- char型 :1ポインタ変数につき、アドレス値は1増える
- int型 :1ポインタ変数につき、アドレス値は4増える
- float型 :1ポインタ変数につき、アドレス値は4増える
- double型:1ポインタ変数につき、アドレス値は8増える
配列なので隣り合うアドレスを指定しますが、その情報に何byte使っているかで隣のアドレス値がが変わります。
つまりアドレス何個分使うデータ型かによって、次のアドレスの先頭は遠くなるのです。
例えばint型のアドレスのサイズが4byte分なので計算上では+1移動する命令を与えていますが、実際は1アドレス分である4バイト先のアドレス名が表示されるということになります。
文字や情報の量によってそのデータ型では何byte使うとかが変わってくるので、そこは注意が必要です。
ポインタでポインタの情報を取得する方法
ポインタには「多重間接参照」という使い方があります。
多重間接参照とは、ポインタを使って別のポインタの情報を指すことです。
ポインタのポインタとして定義したい情報の前に「**」をつけることで定義します。
具体的な定義方法は以下のとおりです。
- 定義方法:データ型 **変数名
- 使用例 :[int **p]
- 意味 :[int *p]と定義したポインタの変数を代入する
指定するポインタ変数を void型にしておけば、戻り値を配列として出力したいときも異なるデータ型を出力できます。
あまり普段使いする方法ではありませんが、ほかに代用の聞くやり方でもないためポインタを覚えるときに一緒に覚えましょう。
ポインタの特殊な使い方を3つ紹介
ポインタの基本的な使い方としてメモリ上にある値を参照するという内容でご紹介してきました。
そこで次にポインタの特殊な使い方として、以下の3つ紹介します。
- 配列ポインタ
- 構造体ポインタ
- 関数ポインタ
メモリ上に保管してある単体の情報ではなく、連続した複数の情報を取得したいときに使える方法です。
なのでこれから紹介する3つは、参照渡しの具体的な使い方ともいえるでしょう。
C言語は参照渡しを使いこなすことが作業効率をあげることに繋がります。
それでは詳しい使い方を紹介していきます。
1.配列ポインタ:連続した値を取り出せる
配列をポインタで指定することで、同じデータ型の連続した値を取り出すことができます。
配列のようにリスト化されている情報を一度作ってしまえばポインタで何度でも取り出し可能なので、コーディングが楽です。
配列をポインタで指定する場面として以下があります。
- 名簿
- テストの点数
- 電車の時刻表リスト
配列を使うときの注意点としては、同じデータ型でないと使えないという点です。
整数なら整数だけ、文字列なら文字列だけの連続した情報である必要があります。
配列をポインタとして指定するときは、同じデータ型の連続した値であることを確認しましょう。
2.構造体ポインタ:関連する項目をまとめて取り出せる
構造体ポインタは複数の項目をまとめてひとかたまりとして取り出すことができる使い方です。
かたまりとしてまとめた関連する項目を「メンバ」と呼び、変数や文字列などの連続していない異なるデータ型の情報でもまとめて指定できます。
例えば、個人情報を指定するときにも使えます。
- 名前
- 年齢
- 生年月日
- 住所
上記のように名前・住所などの文字列と年齢・生年月日などの数字を1つのメンバとして指定する場合に、構造体ポインタを利用します。
3.関数ポインタ:状況に応じてさまざまな関数を呼び出せる
関数ポインタとは関数が格納されたメモリのアドレスを取得するというポインタの使い方です。
代入する用の値や関数をいくつか用意し、事前に定義しておいた関数ポインタに入力することで情報を取り出します。
つまり、取り出す関数のアドレスを変更することで値が変化する変数で、動的に扱えるのがポイントです。
使い方の例は以下のとおりです。
- 「関数ポインタA」を定義する
- 「関数1」を定義→ [ 2+3 ]
- 「関数2」を定義→ [ 2×3 ]
ここで「関数ポインタA」に「関数1」を代入すると戻り値は 5、「関数2」を代入すると戻り値は 6になります。
また関数には配列や構造体も指定できます。
自由度が高く色んな使い方ができるのが関数ポインタです。
ポインタを特殊な使い方をするときの3つの定義方法
ポインタで参照渡しなどの特殊な使い方をする場合でも、事前に定義する必要があります。
ポインタの3つの特殊な使い方に対する定義方法は以下のとおりです。
- 配列ポインタ :データ型 配列名 [ 要素数 ] ;
- 構造体ポインタ:struct 構造体名 { メンバ1 ; メンバ2 ; }
- 関数ポインタ :データ型 ( * 関数名) (データ型 , データ型) ;
ポインタとはあくまで情報がある場所を示すことです。
その情報を扱うときには、その情報がどんなデータ型であるかまで定義する必要があります。
それでは詳しい定義方法について見ていきましょう。
1.配列ポインタの定義:データ型 配列名 [ 要素数 ] ;
配列ポインタの定義は以下のように表現します。
データ型 配列名 [ 要素数 ] ;
先頭にデータの型を宣言し、[]内にその配列で使う要素数を指定します。
またこの定義式の [要素数] の後ろに ={ } と入れて {} 内に要素を入れておけば、今回の配列で使う要素として指定することが可能です。
配列の具体的な定義の仕方は、以下のようになります。
- 要素を指定せず定義:int p [5] ;
- 要素を指定して定義:int q [5] = { 0, 1, 2, 3, 4 } ;
なお配列で使用するデータ型は同じである必要があるため、宣言したデータ型の要素だけが扱えることになります。
同じデータ型の連続する情報を使う場合には、配列ポインタを使いましょう。
2.構造体ポインタの定義:struct 構造体名 { メンバ1 ; メンバ2 ; }
構造体ポインタの定義は以下のように表現します。
struct 構造体名{ メンバ1 ; メンバ2 ; }
構造体を定義するときは先頭に structをつけて、後ろの {}内にメンバと呼ばれる構造体の要素を指定します。
それぞれのメンバにはデータ型も指定する必要があるため、「データ型 メンバ名 ;」と指定しましょう。
具体的な定義の例として、以下では構造体で個人情報を出力する式をまとめました。
- 構造体の定義:struct preson { char name; int age; int birth; char add; }
実際のコード内ではわかりやすく表記するために縦に書いていくのが一般的です。
個人情報のように数字や文字列など、出力したいデータ型が異なる場合は構造体ポインタを使いましょう。
3.関数ポインタの定義:データ型 ( * 関数名) (データ型 , データ型) ;
関数ポインタの定義は以下のように表現します。
データ型 ( * 関数名) (データ型 , データ型);
式の先頭には戻り値のデータ型を宣言し、後半には今回の関数ポインタに使う引数の数とそれぞれのデータ型が必要です。
また関数ポインタを使うときは、関数ポインタに代入するための関数も定義する必要があります。
それでは関数ポインタと関数の定義の具体例をあわせてみていきましょう。
- 関数ポインタの定義:void int ( *p) ( int q, int r) ;
- 関数1の定義 :int sum(int q, int r) { return q + r ; }
- 関数2の定義 :int mul(int q, int r) { return q × r ; }
- qとrの値の定義 :q = 2 , r = 3
上記の場合、関数ポインタに関数1を代入すれば足し算の結果が出力されて戻り値は「5」となり、関数2なら掛け算の結果「6」となります。
関数ポインタとは参照する関数を変えることで戻り値が変わる、動的な関数です。
ポインタ使用時によくある2つのエラーと対処法
ポインタをきちんと理解できていないとよく起こりがちなエラーがあります。
以下の2つに注意しましょう。
- バッファオーバーラン
- ローカル変数を戻り値にしてしまう
これらはポインタを使っているとよくあるので対処法をあわせて覚えておきましょう。
それでは以下で詳しく解説していきます。
1.バッファオーバーラン
バッファオーバーランとは確保していたメモリ数よりも多い情報量が入ろうとしたときに、隣り合うメモリに情報が上書きされてしまうバグのことです。
ポインタはアドレスの指定や操作をするため、間違ったアドレスを指定するだけでバッファオーバーランを起こしてしまうことがよくあります。
バッファオーバーランの具体的な例として、Wikipediaの電子メールアドレスで例えたものを以下に引用しました。
1. 200文字分の領域をバッファとして用意する。
引用:Wikipedia
2. ユーザが200文字より長いメールアドレスを入力する。
3. プログラムがバッファの大きさをチェックせずに入力データを書き込む。
4. バッファとして確保した領域をはみだしてデータが書き込まれてしまう。
開発者が想定しているメモリの領域より多い情報量をユーザーが入力してしまう場合もあります。
バッファオーバーランの対策はいくつかありますが、簡単な対策として「計算がループしたときの終了条件をつくる」という方法を覚えておきましょう。
対策をしないとプログラムが暴走して無限に情報が蓄積される可能性があるので、きちんと理解しておきましょう。
2.ローカル変数を戻り値にしてしまう
ポインタを扱うコードを書く場合は、ローカル変数を戻り値として指定しないように注意しましょう。
ローカル変数を使った関数内で処理が終わってしまっているため、ほかの関数の処理に移ったときに変数が反映されない可能性があるからです。
簡単な対策としては以下があります。
- 動的ローカル変数に変更する
- 動的なメモリ割り当てをおこなう
- グローバル変数に結果を格納するように変更する
- グローバル変数に結果を格納して、アドレスを返す
- 呼び出し元にポインタを渡し、そこに結果を格納する
意識すべきポイントは、関数Aで使った変数を関数Bに指定していないかという点です。
ローカル変数は使わず、関数の外で定義した変数(グローバル変数)を使いましょう。
まとめ:C言語を使うなら、初心者からプロまで使うポインタを必ず覚えよう!
この記事ではポインタとは何なのかについて紹介しました。
- ポインタと特定の情報がある場所を指すこと
- ポインタで参照渡しをするとムダなコーディングが省ける
- ポインタが指す情報によって定義方法が違うので注意する
ポインタはC言語を扱う上でコーディングや情報処理の無駄を省ける文法です。
C言語を使うならプロになってもポインタは使うので、今のうちに覚えましょう。
とくに今回紹介した方法はC言語ではよく使うので、あとは使いながら覚えていきましょう。