
1メインメモリの領域
第4話では関数について解説しましたが、引数の扱い方にはあまり触れませんでした。 今回は、引数を読み書きする方法や、もっと幅広くメインメモリを読み書きする方法について解説します。
メインメモリ上に値が読み書きできれば様々なことが実現できそうです。 しかし、メインメモリにはプログラム自身が配置されていたり、他のアプリが使用中の情報も存在しています。 このため、何も考えずにメインメモリ上の値を書き換えると問題が起こります(図1-1)。

そこでプログラムからメインメモリ上に値を保存したいときには、自由に書き換えても良い領域を用意して、その領域を使用していきます。 通常この「自由に書き換えても良い領域」は、WindowsやAndroidなどのOSが用意してくれますのでそれを使います。 この領域には表1-1のような種類があります。
領域の名前 | 解説 |
---|---|
スタック領域 | 最もよく使う領域です。 関数が呼び出されたときに確保され、関数を抜けるときに解放されます。 |
静的領域 | プログラムが起動したときに確保され、プログラムが終了したときにOSに返される領域です。 |
ヒープ領域 | 好きなときに確保し、好きなときに解放できる領域です。 |
確保した領域は、不要になったらメインメモリを無駄遣いしないために「解放」してOSに返します。
「スタック領域」は、最もよく使われる領域で、引数で渡された値はここに配置されます。 関数内で一時的にメインメモリを利用したいときにも基本的にこの領域を使います。 以前にも説明した通り、スタック領域は荷物置き場のようなイメージで、上に値を積んでは上から順に降ろしていく構造になります(図1-2)。

「静的領域」は、プログラムの起動時に確保され、プログラムの実行中はずっと同じアドレスに存在し続けている領域です。 関数を抜けても解放されたくないような、プログラム全体を通して保持したい値に使います。
「ヒープ領域」は、好きなときに好きなだけ確保できる融通の利く領域です。 しかし解放を自分でする必要があり、解放を忘れるとメインメモリにどんどん無駄な領域が溜まる「メモリリーク」という問題が発生します。 また、確保するタイミングでOSがメインメモリから空き領域を探しますので、わずかに時間がかかります。 言語によっては確保した領域が不要になると自動的に解放する「ガベージコレクション」という便利な仕組みがあったりします。
2変数
メインメモリ上の値を読み書きする際にアドレスを直接指定していたのでは解りにくいため、多くの言語では「変数」というものが使われます。 変数とは、メインメモリ上に作った箱のようなもので、「名前」と「型」を持っています(図2-1)。

この例では「n」という名前の、int型の変数を作成しています。 変数には値を入れることができ、この例では「5」が入っています。
変数とは、メインメモリを安全に使うための仕組みと言えます。 この変数nに5を入れることは、結局メインメモリの「A4~A7番地」に「00 00 00 05」を書き込むことと同じですが、名前と型を持たせることで、別の型の値が入るのを防いだり、うっかり「A8番地」に書き込んでしまうことも避けられます。
このint型の変数nを、C言語上で作成するには「int n;」と書きます。 C言語やJavaなどでの変数の作り方は、「型 変数名;」です。
「int n;」を関数の中に書いた場合は、変数はスタック領域に作られます。 つまり、関数が呼び出されたときにメインメモリ上に領域が確保され、関数を抜けるときに解放される変数となります。 これを「ローカル変数」といいます(図2-2)。

関数の引数も、ローカル変数で、スタック領域に作られます。
「int n;」を関数の外に書いた場合は、変数は静的領域に作られます。 つまり、プログラムが実行されたときに確保され、プログラムを終了するときに解放される変数となります。 これを「グローバル変数」といいます(図2-3)。

プログラム自体も静的領域に配置されます。 このほか、C言語で「"Hello, world!\n"」と書いたときの文字列リテラルなども静的領域に配置されます。
C言語で変数を作るソースコードの例は、図2-4のようになります。
- int a;
-
- void f(int c)
- {
- int b;
- }
変数aは関数の外にあるので、グローバル変数です。 変数bは関数fの中なので、ローカル変数です。 引数はローカル変数として扱われるので、引数cもローカル変数です。
3演算子
それでは、これまで解説したリテラルや変数を使って、コンピュータに様々な計算をさせましょう。 多くの言語では、数式を書くのと同じ書き方で計算式が表現できます。 例えばC言語では、図3-1のように書けます。
- #include <stdio.h>
-
- int main(void)
- {
- printf("%d\n", 1 + (5 - 3) * 4 / 2);
- return 0;
- }
「1+(5-3)*4/2」というのは、数式でいう「









」と同様です。 数式と同様に、掛け算や割り算は足し算や引き算よりも先に計算されます。 括弧で計算順序も変えられます。 このプログラムを実行すると、画面には「5」が表示されます。











「+-*/」といった記号は、値を演算するので「演算子」と呼ばれます。 この他にもたくさんの演算子が存在します。 以下に主要なものを紹介します。
3.1代入演算子
「代入演算子」は、変数に値を入れる演算子です。 C言語やJavaなどでは「=」の記号が使われますが、数学のイコールとは意味が違うので注意してください。 「=」の右側に書いた値を、左側に書いた変数に代入します(図3-2)。
- #include <stdio.h>
-
- int main(void)
- {
- int n;
- n = 3 + 4;
- printf("%d\n", n);
- return 0;
- }
6行目で「3+4」の値を「n」に代入しています。 nには「7」が入りますので、画面には「7」が表示されます。
3.2比較演算子
「比較演算子」は、2つの値を比較する演算子です。 C言語やJavaなどで2つの値が等しいか否かを比較するときは「==」の演算子を使います。 これは数学のイコールに近いです。
比較演算子は、比較条件を満たせば論理型の「真」が返り、満たさなければ「偽」が返ります。 例えばC++やC#では「==」の左右の値が等しければbool型の「true」が、異なれば「false」が返ります。 trueは真、falseは偽を意味する値です。 ただし、C言語の古典的な仕様には論理型が存在しないため、「==」の左右が等しければint型の「0以外」の値が、異なれば「0」が返ります。
C言語の比較演算子には他に、表3-1のものがあります。
表記 | 比較条件 |
---|---|
== | 左右が等しければ真 |
!= | 左右が異なれば真 |
< | 左が小さければ真 |
> | 左が大きければ真 |
<= | 左が小さいか等しければ真 |
>= | 左が大きいか等しければ真 |
3.3論理演算子
「論理演算子」という演算子もあり、これは日本語で「または」「かつ」の意味を表すものです。 「または」を意味するものを「論理和」といい、C言語では「||」で表します。 「かつ」は「論理積」といい、C言語では「&&」です。 例えばC言語で「n==1||n==2」と書くと「nは1に等しい、または、nは2に等しい」という条件式になります。
実際にはこれらは、論理型の2つの値を受け取って論理型の結果を返すだけの演算子です(表3-2)(表3-3)。
左側の値 | 右側の値 | 演算結果 |
---|---|---|
偽 | 偽 | 偽 |
偽 | 真 | 真 |
真 | 偽 | 真 |
真 | 真 | 真 |
左側の値 | 右側の値 | 演算結果 |
---|---|---|
偽 | 偽 | 偽 |
偽 | 真 | 偽 |
真 | 偽 | 偽 |
真 | 真 | 真 |
例えば「真&&偽」の結果は「偽」の値になります。
また論理演算子には、真偽を反転させる「論理否定」があり、C言語では「!」の記号を使って「!(n==1)」のように書きます(表3-4)。
右側の値 | 演算結果 |
---|---|
偽 | 真 |
真 | 偽 |
3.4ビット演算子
C言語やJavaなどには、数値を1ビット単位で操作するための「ビット演算子」があります。 論理型では1つの値に1バイト以上の領域を消費しますが、ビット演算子を用いると1ビット単位で操作できるのでメインメモリの領域を節約できます(図3-3)。

ビット演算子には、0を偽、1を真とみなして各ビットに対し論理和や論理積を行う演算子や、ビット全体を左右にずらす「ビットシフト」などがあります。
3.5その他の演算子
C言語やJavaなどで「n=n+5;」と書くことは、変数nの値を5増やすことになります。 例えば、nに2が入っていたときに「n=n+5;」を実行すると、nは7になり、元の値から5増えたことがわかります。 このような操作はよく行われるので、C言語やJavaなどには「+=」という演算子が用意され、「n=n+5;」は「n+=5;」と書けるようになっています。 足し算だけでなく、「-=」「*=」「/=」なども用意されており、例えば「n*=2;」と書くと「n=n*2;」と同等なので、nの値が2倍になります。
さらに、「n=n+1;」と「n=n-1;」のような、変数を1だけ増減させたい場面は多いため、「n=n+1」のことは「n++;」もしくは「++n;」と書け、「n=n-1;」のことは「n--;」もしくは「--n;」と書けるようになっています。 C言語の拡張版である「C++」という言語の名前には、C言語を一歩進める意味が込められています。
値の型を他の型に変換する演算子「キャスト演算子」もよく使われます。 型を変換する操作を「キャスト」といい、C言語では変換後の型を括弧で囲んで表現します。 例えばdouble型の値「5.3」をint型にキャストするときは「(int)5.3」と書きます。 この場合、int型は整数型で小数が扱えないため、通常はキャスト時に小数点以下が切り捨てられて「5」になります。
また、割り算の余りを計算する「剰余演算子」もたまに使われます。 C言語やJavaなどでは「%」の記号を用います。 5÷3の余りは2ですので、「5%3」は「2」です。 この他にも様々な演算子が存在します。
4演算子の優先順位と結合規則
「+」「-」よりも「*」「/」のほうが先に計算されると言いましたが、同様にすべての演算子には計算の「優先順位」が定められています。 C言語で「n==1||n==2+3」と書くと、「(n==1)||(n==(2+3))」と解釈されます。
また、「1+2+3」と書くと「(1+2)+3」と解釈されますが、「a*=b*=c」と書くと「a*=(b*=c)」と解釈されます。 このように同じ優先順位を並べたときには左から計算されるものと右から計算されるものがあり、これを「結合規則」といいます。 左から計算されるものを「左結合」、右から計算されるものを「右結合」といいます。
今回は、メインメモリに値を保存する「変数」と、それを用いて計算する「演算子」について解説しました。 次回は、プログラムを上から下へと実行させるだけでなく、さらに条件に応じて分岐や繰り返しを行う方法について解説していきます!