プログラミング

C言語のポインタに関する補足説明

投稿日:2018年3月11日 更新日:

前回、C言語のポインタに関する解説記事を書きまして、そこそこの反響を貰うとともにいくつかの指摘を受けました。
前回の記事では幾つか「ウソではないけど真実と違う」記述がありまして、その点を補足としていくつか説明します。
今回、初学者向けではなく中級者以降の人でないとナンノコッチャ度が高いのでお付き合いください。

仕様と実装のごちゃまぜ

前回の記事で行った最も激しいダマシがこれです。
C言語とは仕様であり、実装はコンパイラに依存します

前回、暗黙的にgccを利用して実験をしており、特にアドレス周りの記述はLinuxの64bit環境を前提としたコーディングが成されています。
しかし、究極的にはポインタがメモリアドレスであることすら仕様には明記されていません
ポインタ=アドレス、というのは、一部のコンパイラが勝手に決めた仕様に他ならないわけです。

特に、現在ナマのC言語が求められる局面というのは、一部の低レイヤーな挙動を制御するシステムに限られます。
具体的にはマイコンであるとか、ハードウェアの組み込みソフトであるとか、ファームウェアであるとか、ミドルウェアであるとかです。
つまり、皆さんが普段触れているようなWebアプリやスマホアプリ、Windows用プログラムの大半はC言語では書かれていない。
もはやニッチな分野でしか使われない特殊言語な訳です。
で、特殊言語ゆえに、環境ごとに実装が大きく異なります
なので、「C言語」と一口に言っても、その実装は様々であり、もはや「特殊な実装である方が普通」と言っても過言ではないわけです。

「コンパイラ」を便利に使いすぎている

前回、「コンパイラ」という言葉に全てを任せ過ぎています。
実際、gccにおいて「コンパイラ」の役割は以下のように細分化できます。

  • プリプロセッサ(#includeなどの展開)
  • コンパイラ(アセンブリへの変換)
  • アセンブラ(アセンブリから機械語へ変換し、オブジェクトファイル(.o)を生成)
  • リンカ(オブジェクトファイルを束ねて実行可能ファイルを作成)

これらを全てまとめて「コンパイラ」と呼称してしまっているわけです。

まぁ実際、gccはコマンド一つでソースファイル(.c)を実行可能ファイル(.outとか.exe)にできるわけですし、初学者が上記の呼称を意識する必要はないんですが。中級者以降なら分けられているのは知っておかないといけませんね。

なので、全てを「コンパイラ」が決めているというのは、そこそこ強烈なウソなわけです。

物理メモリと仮想メモリの概念の省略

ぶっちゃけ、仮想メモリというのはOSの用意する機能であってC言語が意識するところではないです。

ただ、実際に緑と黒のゲジゲジに乗っているのは物理メモリであり、Linuxで実行するファイルでポインタが指すアドレスは仮想メモリであります。
そして前回の記事で書いた「0x7fffffffe494」とかいうアドレスは全て仮想メモリです。
なので実際は「0x7fffffffe494」という場所が物理メモリ上にあるわけではなく、プロセスが持つ仮想メモリ空間上のアドレスなわけです。
実際に利用するときにはOSによって物理メモリへのマッピングが行われますが、物理メモリ上のアドレスというのはそれこそ誰も気にする必要のないところです。脆弱性を突きたいわけでなければ。

スタック領域とかヒープ領域とかうんぬん

変数のアドレスの場所、というのは宣言の仕方によって変化します。
ローカル変数であれば基本的にスタック領域に置かれると思いますが、グローバル変数だったりstatic宣言されていたりするとスタックではない領域に置かれることもあるでしょう。その場合、OSのメモリマッピングに依存して場所が決まることになります。多分。

また、スタック領域が絶対的にアドレスを決め打ちできるという保証もありません。スタック内でのメモリ配置はコンパイラによって行われますが、スタック領域がどこに取られるか、というのは正直自分は詳しくないです。
メモリマップを見る感じだとvdsoがスタックの前後に入りがちですがどういう法則なのやら。
またmallocコマンド等でヒープ領域を動的に確保した場合、それこそ動的に確保しているのでアドレスがどこになるか、というのは完全に未知数です。

とにかく変数のアドレス参照は相対的に行われるので、絶対アドレスを指定した参照なんて普通やっちゃいけないです。

そのため、メモリのアドレスをコンパイラが決めている、というのはいろいろな意味でウソのある表現だったわけです。

おわりに

前回の記事はなんだかんだ初学者向けのため、「本当はこうだけどちょっとウソをついておこう」という記述がちらほらありました。
その辺ツッコミを入れている方が多く、みんなマジメに読んでいるんだなぁって思いました。
そういう人は既にポインタについて詳しいハズなので、たのしくツッコミを入れてもらえれば良いです。

また、プログラミング初学者の人はC言語なんてあえて触ろうとせず、他の言語からとっつく方があなたの身と健康のためになると、と言っておきましょう。

-プログラミング

執筆者:

関連記事

GeoJSONで市町村境界をマージして都道府県境界にしたい(実践編)

前々回と前回で問題を整理して、ようやく実践編です。 まず実物のリンク貼りましょう。GitHubに上げました。 今回はGo言語で書いてますが、ポイントがいくつかあります。 ちなみに言語としてGoを選択し …

no image

ロリポップでSQLite3を使うときはpythonでpysqlite3-binaryだ

このブログはロリポップで運用しています。 公式にSQLite3の使用も許可されていて、モジュール叩けば動くんですがいかんせんバージョンが低い。 確認するとバージョンが3.7.17。なかなかに古いです。 …

YouTube Data APIをGoogle Apps Script(GAS)から使おう

YouTubeってAPIから色々な情報を取ることができるんですよ。 APIの情報はリファレンスにまとまってるんですが、APIキーだのOAuth2.0だの、使い始めるまでがまぁまぁ面倒なんですね。 で、 …

GeoJSONで市町村境界をマージして都道府県境界にしたい(その2)

GeoJSONのPolygonをマージしたい第2回です。 前回、純粋な多角形の統合ではなくて、領域が被らない多角形の統合になるのでグラフ問題として解くことができるという説明をしました。 今回はどうやっ …

no image

“API”をなるべく分かりやすく説明してみる

“API”という言葉が一般にもよく使われ始めています。 しかし、非エンジニアにとっては馴染みのない言葉で、しかも謎の英略語なので、一部の人々からは「APIがなくなった」(=AP …