Home English

Kuina-chan

くいなちゃんOct 17, 2017


6さいからのプログラミング」第6話では、メインメモリを活用してコンピュータに様々な計算をさせる方法を解説します。

メインメモリの領域

第5話では関数について解説しましたが、引数の扱い方にはあまり触れませんでした。 今回は、引数を読み書きする方法や、もっと幅広くメインメモリを読み書きする方法について解説します。
メインメモリ上に値が読み書きできれば様々なことが実現できそうです。 しかし、メインメモリにはプログラム自身が配置されていたり、他のアプリが使用中の情報も存在しています。 このため、何も考えずにメインメモリ上の値を書き換えると問題が起こります(メインメモリの書き換え)。
メインメモリの書き換え
メインメモリの書き換え
そこでプログラムからメインメモリ上に値を保存したいときには、自由に書き換えても良い領域を用意して、その領域を使用していきます。 通常この「自由に書き換えても良い領域」は、WindowsやAndroidなどのOSが用意してくれますのでそれを使います。 この領域にはメインメモリ上の領域の種類のような種類があります。
メインメモリ上の領域の種類
領域の名前 解説
スタック領域 最もよく使う領域です。 関数が呼び出されたときに確保され、関数を抜けるときに解放されます。
静的領域 プログラムが起動したときに確保され、プログラムが終了したときにOSに返される領域です。
ヒープ領域 好きなときに確保し、好きなときに解放できる領域です。
これらの領域を用意することを「確保かくほ」といい、不要になったらメインメモリを無駄遣いしないために「解放かいほう」してOSに返します。
スタック領域は、最もよく使われる領域で、引数で渡された値はここに配置されます。 関数内で一時的にメインメモリを利用したいときにも基本的にこの領域を使います。 以前にも説明した通り、スタック領域は「荷物置き場」のようなイメージで、上に値を積んでは上から順に降ろしていく構造になります(スタック領域)。
スタック領域
スタック領域
静的領域は、プログラムの実行中はずっと同じアドレスに存在し続けている領域です。 関数を抜けても解放されたくないような、プログラム全体を通して保持したい値に使います。
ヒープ領域は、好きなときに好きなだけ確保できる融通の利く領域です。 しかし解放を自分でする必要があり、解放を忘れるとメインメモリにどんどん無駄な領域が溜まる「メモリリーク」という問題が発生します。 また、確保するタイミングでOSがメインメモリから空き領域を探しますので、わずかに時間がかかります。 言語によっては確保した領域が不要になると自動的に解放する「ガベージコレクション」という便利な仕組みがあったりします。

変数

メインメモリ上の値を読み書きする際にアドレスを直接指定していたのでは解りにくいため、多くの言語では「変数へんすう」というものが使われます。 「変数」とは、メインメモリ上に作った箱のようなもので、「名前」と「型」を持っています(変数)。
変数
変数
この例では「n」という名前の、int型の変数を作成しています。 変数には値を入れることができ、この例では「5」が入っています。
変数とは、メインメモリを安全に使うための仕組みと言えます。 この変数nに5を入れることは、結局メインメモリの「A4~A7番地」に「00 00 00 05」を書き込むことと同じですが、名前と型を持たせることで、別の型の値が入るのを防いだり、うっかり「A8番地」に書き込んでしまうことも避けられます。
このint型の変数nを、C言語上で作成するには「int n;」と書きます。 C言語での変数の作り方は、「型 変数名;」です。
「int n;」を関数の中に書いた場合は、変数はスタック領域に作られます。 つまり、関数が呼び出されたときにメインメモリ上に領域が確保され、関数を抜けるときに解放される変数となります。 これを「ローカル変数へんすう」といいます(ローカル変数)。
ローカル変数
ローカル変数
関数の引数も、ローカル変数です。
「int n;」を関数の外に書いた場合は、変数は静的領域に作られます。 つまり、プログラムが実行されたときに確保され、プログラムを終了するときに解放される変数となります。 これを「グローバル変数へんすう」といいます(グローバル変数)。
グローバル変数
グローバル変数
プログラム自体も静的領域に配置されます。 このほか、C言語で「"Hello, world!\n"」と書いたときの文字列リテラルなども静的領域に配置されます。
実際にC言語で変数を作るソースコードを書くと、変数を使ったソースコードのようになります。
int a;
  
void f(int c)
{
  int b;
}
変数を使ったソースコード
変数aは関数の外にあるので、グローバル変数です。 変数bは関数fの中なので、ローカル変数です。 引数はローカル変数として扱われるので、引数cもローカル変数です。

演算子

それでは、これまで解説したリテラルや変数を使って、コンピュータに様々な計算をさせましょう。 多くの言語では、数式を書くのと同じ書き方で計算式が表現できます。 例えばC言語では、C言語の式のように書けます。
#include <stdio.h>
  
int main(void)
{
  printf("%d\n", 1 + (5 - 3) * 4 / 2);
  return 0;
}
C言語の式
「1+(5-3)*4/2」というのは、数式でいう「1+(5-3)×4÷2」と同様です。 「×」「÷」という記号の半角文字は存在しないので、ほとんどの言語では「*」「/」で代用されます。 数式と同様に、掛け算や割り算は足し算や引き算よりも先に計算されます。 括弧で計算順序も変えられます。 このプログラムを実行すると、画面には「5」が表示されます。
「+-*/」といった記号は、値を演算するので「演算子えんざんし」と呼ばれます。 この他にもたくさんの演算子が存在します。 以下に主要なものを紹介します。

代入演算子

代入演算子だいにゅうえんざんし」は、変数に値を入れる演算子です。 C言語やJavaなどでは「=」の記号が使われますが、数学のイコールとは意味が違うので注意してください。 「=」の右側に書いた値を、左側に書いた変数に代入します(代入演算子)。
#include <stdio.h>
  
int main(void)
{
  int n;
  n = 3 + 4;
  printf("%d\n", n);
  return 0;
}
代入演算子
6行目で「3+4」の値を「n」に代入しています。 nには「7」が入りますので、画面には「7」が表示されます。

比較演算子

比較演算子ひかくえんざんし」は、2つの値を比較する演算子です。 C言語やJavaなどで2つの値が等しいか否かを比較するときは「==」の演算子を使います。 これは数学のイコールに近いです。
比較演算子は、比較条件を満たせば論理型の「真」が返り、満たさなければ「偽」が返ります。 例えばC++やC#では「==」の左右の値が等しければbool型の「trueトゥルー」が、異なれば「falseフォルス」が返ります。 trueは真、falseは偽を意味する値です。 ただし、C言語の古典的な仕様には論理型が存在しないため、「==」の左右が等しければint型の「0以外」の値が、異なれば「0」が返ります。
C言語の比較演算子には他に、C言語の比較演算子のものがあります。
C言語の比較演算子
表記 比較条件
== 左右が等しければ真
!= 左右が異なれば真
< 左が小さければ真
> 左が大きければ真
<= 左が小さいか等しければ真
>= 左が大きいか等しければ真

論理演算子

論理演算子ろんりえんざんし」という演算子もあり、これは日本語で「または」「かつ」の意味を表すものです。 「または」を意味するものを「論理和ろんりわ」といい、C言語では「||」で表します。 「かつ」は「論理積ろんりせき」といい、C言語では「&&」です。 例えばC言語で「n==1||n==2」と書くと「nは1に等しい、または、nは2に等しい」という条件式になります。
実際にはこれらは、論理型の2つの値を受け取って論理型の結果を返すだけの演算子です(論理和(||)の演算)(論理積(&&)の演算)。
論理和(||)の演算
左側の値 右側の値 演算結果
論理積(&&)の演算
左側の値 右側の値 演算結果
例えば「真&&偽」の結果は「偽」の値になります。
また論理演算子には、真偽を反転させる「論理否定」があり、C言語では「!」の記号を使って「!(n==1)」のように書きます(論理否定(!)の演算)。
論理否定(!)の演算
右側の値 演算結果

ビット演算子

C言語やJavaなどには、数値を1ビット単位で操作するための「ビット演算子えんざんし」があります。 論理型では1つの値に1バイト以上の領域を消費しますが、ビット演算子を用いると1ビット単位で操作できるのでメインメモリの領域を節約できます(論理型とビット演算)。
論理型とビット演算
論理型とビット演算
ビット演算子には、0を偽、1を真とみなして各ビットに対し論理和や論理積を行う演算子や、ビット全体を左右にずらす「ビットシフト」などがあります。

その他の演算子

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」です。 この他にも様々な演算子が存在します。

演算子の優先順位と結合規則

「+」「-」よりも「*」「/」のほうが先に計算されると言いましたが、同様にすべての演算子には計算の「優先順位ゆうせんじゅんい」が定められています。 C言語で「n==1||n==2+3」と書くと、「(n==1)||(n==(2+3))」と解釈されます。
また、「1+2+3」と書くと「(1+2)+3」と解釈されますが、「a*=b*=c」と書くと「a*=(b*=c)」と解釈されます。 このように同じ優先順位を並べたときには左から計算されるものと右から計算されるものがあり、これを「結合規則けつごうきそく」といいます。 左から計算されるものを「左結合ひだりけつごう」、右から計算されるものを「右結合みぎけつごう」といいます。
今回は、メインメモリに値を保存する「変数」と、それを用いて計算する「演算子」について解説しました。 次回は、プログラムを上から下へと実行させるだけでなく、さらに条件に応じて分岐や繰り返しを行う方法について解説していきます。
1508204829ja