どうも。ジュデッカの大穴から逃げたので今度は惑星直列による潮汐力を利用して自分自身を引きちぎれないか考えている者です。
今回はメモリの中がどれだけ広大なのかを解説します(すっとぼけ
なぜこのような記事を書いたのか
僕、実は転職活動がうまく行かず、メンタルが溶けているのですが、その溶けたメンタル&自殺願望の中でふと、大学生時代の教授の言葉が浮かんだのです。
- 変数というのはですね・・・箱のようなものでですね・・・
僕は大学時代以前からコードを書き続けているのですが、当時思ったのは・・・
先生・・・あなたは・・・間違っています・・・😅
まあ、初歩的なものほど教えるのが難しい上に、ポインタの前提概念の変数は解説のプロである教授でも間違って説明したりします。しかし、間違った知識を前提にして次の知識を習得すると、大抵路頭に迷ってしまい、そのため、ポインタや参照渡し、構造体やクラス、データ構造とアルゴリズムなんかでお手上げになってしまう人がいるのです。
というわけで、今回は、プロでも解説に苦慮する、プログラミング初歩の初歩である変数について解説します。
読者のターゲット
- 変数は箱と覚えてしまって配列やポインタで泣いている人
- そもそも、変数は箱とか言われて「??? ????」って混乱してる人
- 暇でプロフェッショナルなQiita民 間違ってたら修正依頼やコメントを出して頂けると嬉しい・・・かもデス。
このページを読む時の注意
変数は箱と覚えている人は変数が何かを間違って覚えている可能性があるので「変数=箱」という概念は忘れていただいて問題ありません。ついでに、そういった教え方をしている人にこの記事を紹介してあげてください。
パソコンの中はどうなってるの?
ソフトウェア・エンジニア(or ハッカー)を志望する諸氏の中には、パソコンの中がどういった部品で作られているのか知らない人がいたりします。恥ずかしい
ぶっちゃけ、パソコンの中がどうなっているのか知らなくても、RubyやPythonなんかを扱えなくはないです。でも、その先のC、C++、Java、Go、あるいはRustなどといった、ちょっと高度な機能を扱う言語を使う時なんかだと、このパソコンの中身をある程度理解しておかないと、メモリをとても消費する太っちょアプリを作ってしまったり、ずっとCPUを専有し続けるような欲張りアプリを作ってしまうような事になりかねません。
**というわけで、**パソコンのケースを開けるとどんな部品があるのか説明したいと思います。
まず、パソコンの中は大雑把に、「演算装置」、「記憶装置」、「入出力装置」の3つで構成されている事は、学校の授業などで教わったと思います。まず、この3つを復習していきましょう。
演算装置
- CPU - 中央演算処理装置と言います。メモリから値を取り出して計算してメモリの中に戻したり、まあその他諸々の事(詳細については省略)をしてくれます。
- GPU - ちょっと高級なPCに付いていたりします。CPUだと遅すぎるような処理をここで行ったり、3Dデータを扱うような演算をやったり、画面にデータを高速に表示したりしてくれます。
記憶装置
- RAM - CPUや他の装置から受け取った値を保存したり、保存した値を取り出したりする事ができます。なお、PCの電源が切れるとメモリの中身は消えますが、値の高速な保存や取り出しが可能です。
- SSD / HDD - RAM上に置いておくと都合の悪い値、特に電源が切れても内容を保存しておきたいような値を保存する場所です。この中に保存されている値はPCの電源が切れても中身は消えませんが、値の保存や取り出しはRAMと比べて速くありません。
入出力装置
- ディスプレイ - いまこのページを映し出している液晶テレビの事です。
- キーボード - なんかQとかWとかRとかDeleteとかEnterとかボタンがいっぱいある装置です。文字をパソコンに入力する時に使います。
- マウス - ネズミとかミ[検閲済]キーではありません。これを動かすことでディスプレイに表示されている「↗」を動かすことができます。↗は(C言語などの「ポインタ」と区別して)マウスポインタといいます。
- その他 - プリンタ、スキャナ、カメラ、マイク、etc...
プロの方の指摘が入らないように付け加えておくと、CPUやGPU等にも記憶領域(レジスタやバッファなど)が存在しますが、今回は注目しません。今回、注目するのはRAMです。
揮発性記憶装置と不揮発性記憶装置
はいなんかめんどくさい単語でてきたよ! とか言わずに付き合ってください❤
記憶装置には、RAMとSSD/HDDの2つがあると書きました。では、なぜ僕はSSDとHDDを分けずに合わせて書いたのかと、疑問に思っている読者諸氏がいてくれたらすごく嬉しいのですが、さて、なぜか。
答え: 電源が切れると中のデータが消えるかどうかの違いです。
- RAMは電源が切れると中のデータが消えます。
- SSD/HDDは電源が切れても中のデータは(ある一定期間は)残ります。
1.のような性質を持つ記憶装置を揮発性記憶装置、2.のような性質を持つ記憶装置を不揮発性記憶装置といいます。
ちなみに、昔の、199x年代のパソコンやアプリだと、RAMの値の管理がイマイチだったため、再起動すると問題が解決したなんて事がよくありました。
変数とは
さてここまで来ていよいよ本題です。変数とは何か。何度も申し上げますが、「変数はいわば箱である。」とドヤ顔で解説してる人、間違ってます。変数は箱ではありません。
プログラミングにおいて変数というのは、RAM上の、どこかに、いくらかのサイズで、保存された値に名前が付いたものです。例外1はありますが、基本的には、RAM上のどこかに、いくらかのサイズで、保存された値に名前が付いたものです。
変数の最大値、最小値、そしてサイズの関係
変数の宣言の方法や値の代入のやり方については他の記事に解説を譲るとして、C言語のような言語では、変数に最大値、最小値があるということを聞いたことがある人がいると思います。そう、こんな感じの・・・
型 | 最大値 | 最小値 |
---|---|---|
char |
$2^7 - 1$ | $-2^7$ |
short |
$2^{15} - 1$ | $-2^{15}$ |
int |
$2^{31} - 1$ | $-2^{31}$ |
long |
$2^{63} - 1$ | $-2^{63}$ |
uchar |
$2^8 - 1$ | $0$ |
ushort |
$2^{16} - 1$ | $0$ |
uint |
$2^{32} - 1$ | $0$ |
ulong |
$2^{64} - 1$ | $0$ |
浮動小数点型(float
とか、double
とか)はちょっと計算が別物なのでここでは省略しますが、注目してほしいのはこの最大値、「$2^7$」とかの上の「7」の部分。これはその変数の型で表現できる限界の値(i.e. 最小値&最大値)の絶対値を二進数で表現した時の桁数を示しています。
この変数の型で表現できる限界の値の絶対値を二進数で表現した時の桁数に、符号付きの変数の型(u
が頭に付いていないもの)なら1を、符号なしの変数の型(u
が頭に付いているもの)なら0を足したものを、**変数のサイズ(大きさ)**といいます。
例えば、char
型のサイズは7+1=8、uint
型のサイズは、32+0=32となります。
なぜ二進数なのか
読者の皆さんは中学生、いや今の時代、ひょっとすれば幼稚園児くらいから、「パソコンは電気が通っているか、通っていないかで考えるんだよ」と教わったはずです。実際、マイコンなんかでは、通電している状態をHigh(1)、通電していない状態をLow(0)などと呼んでいます。そして、そのような状態の集積の単位をビット(bit)と言います。
つまり??
ビットは2進数で表現されます。そして、上記の変数の型の最大値、最小値はその型のサイズのビット数で表現できる最大の、あるいは最小の数値なのです。例えば、char型の最大値は0111 1111
です。8桁目が0なのは、8桁目が符号ビットを示しているためです。これを計算すると、
$1+2+4+8+16+32+64=127=2^7-1$
ちょうど127になります。
負の数を表現するには?
ビットを使って負の数を表現したい場合、大体3種類の方法があります。
- 符号と絶対値方式 - 最後のビットを符号ビットとし、残りのビットに数値の絶対値を代入してしまう方式です。表現方式が手軽な反面、0の表現方法が2つ(+0と-0)があったり、四則演算、特に正の数と負の数の演算がややこしくなるので使われません。
- 桁ばき表現 - 何かしらの桁を足し合わせる(履かせる)方式です。例えばchar型を例に、-5を表現したい場合、128 + (-5) = 123、これを2進数で示すと、0111 1011になります。また、5を表現したい場合、128 + 5 = 133、これを2進数で示すと1000 0101になります。これによって、0の表現が統一され、四則演算の方法が若干楽になります。そのため、浮動小数点の表現の方法の一部にこの方式が取り入れられていますが、より(演算回路の設計的に)楽ちんな方法があるため、整数型ではこの方法は使われていません。
- 2の補数表現 - ビットを反転させ、これに1を足したものを負の数として表現する方法です。例えばchar型で、「-127」を表現したい場合、正の数127、つまり、「0111 1111」の反転、「1000 0000」に1を足したもの、つまり、「1000 0001」を-127とします。こうすることによって、2と同様に0の表現の統一と四則演算の単純化を図ることができ、また四則演算においては2.よりも単純な回路設計で済むという利点があります。このため、現在の整数型の負の数の表現にはこの表現方法が使われています。
符号なしの型の場合
符号なしの型の場合、そもそも正負の符号を表現しないので、符号ありの型と比べて、符号ビット分のより大きな正の数を表現できる反面、負の数は表現しません。つまり符号なしchar
型、つまりuchar
型の最大値は$2^8 - 1 = 255$、最小値は$0$となるのです。
タイトル回収: メモリがいかに広大か
8 bitsの集まりは、1 Byteと定義されています。
1024バイトの集まりは、KB(キロバイト)と定義されています。
1024KBの集まりは、MB(メガバイト)と定義されています。
1024MBの集まりは、GB(ギガバイト)と定義されています。
最近のパソコンのRAMは8GBなどが標準ですが、上記の事から単位を降ろしていくと、8GB = 8*1,024MB = 8,192MB = 8,192 * 1,024 KB = 8,388,608 KB = 8,388,608 * 1024 Bytes = 8,589,934,592 Bytes = 8,589,934,592 * 8 bits = 68,719,476,736 bits
えーっと、大体687億ビット分の容量を持っています。その中で、たとえ64ビットの整数型を1つ宣言した所で、0.00000009%程度しかメモリを食いません。言い換えれば、それだけミクロな領域に、あなたが宣言した変数が保存されているのです。
そして、そういったミクロな領域を管理するため、RAM上には1バイト毎に専用の値を割り当てて、管理しています。この値を「アドレス」と言い、ポインタを学習する上では重要な概念になります。
最後に
~~惑星直列自殺に失敗したら、~~次回はポインタについて書こうと思います。
では。
-
例えば、C言語ではCPUの記憶領域(レジスタと言います)に保存されている値に名前を付けることができます。だし、レジスタはアドレスを持たないので、ポインタの概念は基本的にありません。 ↩