Home English

Kuina-chan

くいなちゃんDec 15, 2017


6さいからのプログラミング」第10話は、これまでに紹介しきれなかった知っておくべき細かな機能を補足します。

識別子の命名

多くのプログラミング言語では大文字「ABC…」と小文字「abc…」とを区別し、例えば変数「X」と変数「x」は別の変数になります。
また多くの言語では、アルファベット「ABC…Zabc…z」と数字「012…9」とアンダースコア「_」が、変数名や関数名などに使用できます。 ただし先頭に数字が付けられないなどの決まりがあります。
全角文字が変数名などに使用できる言語もありますが、入力が大変だったりソースコードが読みにくくなることもあるため、通常は半角文字が使われます。 JavaScriptではさらに「$」の記号が命名に使えます。

改行とスペース

多くの言語では、line_feed_and_space.cのようにソースコード上で比較的自由に改行したりスペースを挟んだりできます。
#include      <stdio.h>
  int
main(
  void){
  printf
("Hello, world!\n"
);return
  
0
  ;}
line_feed_and_space.c
ただし無秩序に改行やスペースを入れるとソースコードが読みにくくなるため、一定のルールに従って書かれます。 また改行はスペース1つ分として解釈されることが多く、「if」の「i」と「f」の間で改行することはできません。
改行やスペースのルールや、大文字小文字のルールなどを合わせたものを「コーディング規約きやく」と呼びます。 コーディング規約は世界に1つだけ存在しているわけではなく、開発チームごとに用意されます。 一人で作るプログラムでは、自分が読み書きしやすいコーディング規約を定めておくと良いでしょう。

コメント

ソースコード中に、日本語や英語などで好きなメッセージを書き込むことができます。 これは「コメント」と呼ばれ、プログラムの動作には影響しません。 C言語では「/*」と「*/」で囲んだ部分がコメントとして、コンパイル時に除外されます(comment.c)。
#include <stdio.h>
  
/* メイン関数 */
int main(void)
{
/*
  printf("Hello, world!\n");
*/
  return 0;
}
comment.c
この例のように、プログラムの一部の動作を無効化したいときにもコメントが役立ちます。
C++やC#では、この「/*」「*/」形式のコメントの他に、「//」形式のコメントも書けます。 これは「//」を書いた箇所からその行の終わりまでをコメント扱いします。 comment.csはC#での例です。
class HelloWorld
{
  // メイン関数
  static void Main(string[] args)
  {
    // System.Console.WriteLine("Hello, world!");
  }
}
comment.cs

16進数と8進数

ほとんどの言語では16進数の数値がソースコード中に書けます。 C言語やJavaなどで16進数を書くには、頭に「0x」を付けます。 例えばA0は「0xA0」です。 またあまり使いませんが、8進数の場合は頭に「0」を付けて、「037」のように書けます。 注意が必要なのは、10進数のつもりで「0123」と書くと8進数になってしまう点です。

指数表記

多くの言語では「a×(10のb乗)」の形の小数が書けます。 これを「指数表記しすうひょうき」といい、非常に大きな数や小さな数を書くときに使われます。 例えばC言語やJavaなどでは「6.02×(10の23乗)」は「6.02e23」と書け、「1×(10の-9乗)」は「1e-9」のように書けます。 この「e」は指数部を意味する「exponent」に由来します。

初期化

C言語やJavaなどで「int a; int b; int c;」のように同じ型の変数を複数作成するときは、「int a, b, c;」のようにまとめて作成できます。
変数は、作成と同時に値を代入することもできます。 例えばC言語で「int n;」を作成して「n=2;」と代入する処理は、「int n=2;」のようにまとめることができます。 これらはJava、C#など、多くの言語で同様です。
またC言語の配列は、配列作成時にまとめて全要素に代入することができます(initialization.c)。
int main(void)
{
  char a[4] = { 'A', 'B', 'C', '\0' };
  char b[] = "ABC";
  return 0;
}
initialization.c
このとき、配列a、bは両方とも「'A'」「'B'」「'C'」「'\0'」の4要素となります。 「{}」とカンマ区切りで各要素の値が指定でき、char型の配列に限っては4行目のように{}の代わりに文字列を指定することもできます。 4行目のbのように要素数を省略すると、指定した値が丁度収まる要素数(この場合は4)に自動的になります。
C言語では構造体も同様に、構造体作成時にまとめて代入できます(initialization2.c)。
struct person
{
  char name[32];
  int age;
  int array[3];
};
  
int main(void)
{
  struct person x = { "Kuina-chan", 6, { 1, 2, 3 } };
  return 0;
}
initialization2.c
ちなみに作成しただけで一度も代入していない変数には、0ではなく、以前その領域が使われていた際のメインメモリ上のデータの残骸が入っていることがありますので、変数を作成した後には代入を忘れないようにしましょう。

再帰呼び出し

関数がその関数自身を呼び出すことを「再帰さいき」といいます。
再帰は数学の漸化式と相性が良く、例えば「a0=3, an=an-1+2」という数学の漸化式はC言語でrecursion.cのように書けます。
#include <stdio.h>
  
int a(int n)
{
  if (n == 0)
  {
    return 3;
  }
  else
  {
    return a(n - 1) + 2;
  }
}
  
int main(void)
{
  printf("%d\n", a(10));
  return 0;
}
recursion.c
このプログラムの関数aは、a(0)が呼ばれた場合は、7行目により3を返します。 a(1)が呼ばれた場合は、11行目により「a(1-1)+2」を返そうとしますが、ここで自分自身の関数a(0)を呼び出します。 a(0)は3を返すため、「a(1-1)+2」は「3+2」、つまり「5」です。
同様にa(10)が呼ばれた場合を追ってみると、11行目によりa(9)、a(8)、a(7)…と順に呼ばれ、a(0)にたどり着いて「3」を返したのち、呼び出された順序を逆にさかのぼって値を返していきます。
再帰は慣れるまでは扱いにくいですが、データ探索の処理などで多用します。
またHaskellなどの関数型言語では、反復処理が基本的に再帰で書かれるなど、再帰はよく使われます。 先ほどの「a0=3, an=an-1+2」という漸化式をHaskellで書くと、recursion.hsのようになります。
a 0 = 3
a n = a (n - 1) + 2
main = print $ a 10
recursion.hs
1~2行目に漸化式がほぼそのまま書かれており、3行目でa(10)の値を画面に表示する旨が書かれています。 C言語に比べてシンプルなソースコードになっています。

ヒープ領域

ヒープ領域とは、プログラムの実行中に自由に確保や解放ができるメインメモリの領域でした。 C言語でこの領域を扱うには、確保に「mallocマロック」関数、解放に「freeフリー」関数を使います。 これらの関数は、stdlib.h内に存在します(heap.c)。
#include <stdlib.h>
  
int main(void)
{
  int* p;
  p = malloc(sizeof(int) * 3);
  p[0] = 100;
  p[1] = 200;
  p[2] = 300;
  free(p);
  return 0;
}
heap.c
malloc関数は、引数に渡したサイズ分をヒープ領域から確保し、そのアドレスを返します。 mallocは「Memory ALLOCation」の略です。
sizeofサイズオブ」演算子は、型のサイズをバイト単位で返す演算子です。 「sizeof(int)」と書くと、int型変数1つ分のサイズが返ります。 6行目では「sizeof(int)*3」としているので、丁度「int p[3];」の配列に相当する領域が確保されます。 よって7~9行目のように読み書きできます。
最後に10行目の「free(p);」で、ヒープから確保した領域を解放しています。 これを書かないと、いつまでも解放されない領域がメインメモリに残り続ける問題「メモリリーク」が発生します。

関数ポインタ

C言語には、関数のアドレスを入れられるポインタが存在します。 これを「関数かんすうポインタ」といいます。 関数の名前だけを書くと、その関数が静的領域に配置されたときの、アドレスとなります(function_pointer.c)。
#include <stdio.h>
#include <math.h>
  
int main(void)
{
  double(*p)(double);
  p = sin;
  printf("%f\n", p(0.0));
  return 0;
}
function_pointer.c
7行目に「sin」とだけ書いていますが、こうするとsin関数のアドレスが返り、ポインタpに入ります。 sin関数は「double sin(double x){~}」として、math.h内で作られていると以前説明しましたが、このsin関数のアドレスを入れる関数ポインタの型は「double(*)(double)」型となります。 そしてこの型のポインタをpという名前で作成するには、6行目のように「double(*p)(double);」と書きます。
関数ポインタに関数のアドレスを入れたものは、その関数と同様に扱えます。 8行目に「p(0.0)」と書かれていますが、これは元のsin関数を「sin(0.0)」と呼び出すことと等しいです。
関数ポインタの使い道としては、例えば関数ポインタを関数の引数にすることで、「渡された関数を10回呼び出す関数」のような関数が作れます(call_ten_times.c)。
#include <stdio.h>
#include <math.h>
  
void call_ten_times(double(*p)(double))
{
  int i;
  for (i = 0; i < 10; i++)
  {
    printf("%f\n", p((double)i));
  }
}
  
int main(void)
{
  call_ten_times(sin);
  return 0;
}
call_ten_times.c
この例では、call_ten_times関数の引数pに渡された関数が10回呼び出されます。 7行目のfor文でiを0~9まで数えながら10回繰り返し、9行目でiの値を関数ポインタpに渡して結果を画面に表示しています。 15行目でこのcall_ten_times関数にsin関数を渡していますので、このプログラムはsin(0.0)、sin(1.0)、sin(2.0)、…、sin(9.0)の結果を表示します。
今回は、今までに紹介しきれなかった細かな機能を補足しました。 次回は、目的を達成するにはどのような処理をプログラムで書けば良いかについて解説します。
1513346846ja