C言語 1 (変数)

C言語の変数について説明します。

editor / text editor

C言語でプログラムを書くとなると、テキストエディタが必要になります。

text fileを編集するために作られたprogramがeditorです。そのままですね。

最も有名なエディタの1つがWindowsのnotepadでしょう。日本語Windowsだとメモ帳です。

昔からvi/vimとemacsが有名です。

GitHubの連中が作ったのがAtomです。
Visual Studio Codeも人気です。
Sublime Textも有名です。

viやemacsが嫌いな人はnanoでしょうか。

TeraPadもあります。EmEditorもあります。TextEditもあります。TextMateもあります。

どれかを選択して、生のtext fileを編集する必要があります。
余計なものが付いているとコンパイルできません。

初心者はVisual Studio Codeが良いでしょう。

main.cを作成する

main.cを作成します。

Windowsの場合はgit bashを使うなどしてください。
WindowsでPythonを使う

MacでTerminalを使う場合と、Windowsでgit bashを使う場合は下記をおすすめします。
作成されたファイルをエディタにDrag & Dropしてください。


$ vi main.c

viが立ち上がったらすぐに:wqで保存して終了させます。

または、下記。


$ touch main.c

UNIXの基本の操作が必要です。

cdを覚えましょう。
自分が移動する

lsも覚えましょう。
fileの一覧を取得する

pwdも覚えましょう。
自分が居る場所を確認する

変数 / variable

定義 : C言語の変数
C言語では、入れた値を保持し続ける容れ物が変数です。

いきなり初心者には難易度高めですが、アプリケーションとして成立するもので説明します。
下記はCSVのようなテキストファイルを読み込み、やりたい処理をさせるための雛形として利用できます。

使い方は下記です。


$ ./sample data.csv

ソースコードです。

sample.c

#include <stdio.h>
#include <stdlib.h>

#define LINE_BUFFER_SIZE 1024

int
main( int argc, char **argv )
{
    char line_buffer[LINE_BUFFER_SIZE];
    FILE *fp;

    if ( argc != 2 )
    {
        fprintf( stderr, "%s: need one runtime argument\n", argv[0] );
        exit(1);
    }

    if ( (fp = fopen(argv[1], "r")) == NULL )
    {
        fprintf( stderr, "%s: can't open '%s' .\n", argv[0], argv[1] );
        exit(2);
    }

    while ( fgets(line_buffer, LINE_BUFFER_SIZE, fp) != NULL )
    {
    }

    fclose(fp);
    return 0;
}

#define LINE_BUFFER_SIZE 1024

LINE_BUFFER_SIZEが1024に置き換わります。

int
main( int argc, char **argv )
{
    return 0;
}

main関数は特別な関数で、1つのプログラムの開始と終了は、main関数の中の記述と対応します。
関数については後述します。
main関数の中に処理を記述します。

main関数で、上記は入力をint argcとchar **argvとしています。

int integerの略で符号付き整数を意味する。
char characterの略で文字を意味する。
argc プログラム(=自分)と実行時引数の個数。./sample data.csvの場合は2になる。
argv argv[0]はプログラム(=自分)の文字列。argv[1]やargv[2]に引数の文字列が入る。

return 0;は出力が0を意味します。出力が0は正常終了を意味します。
shell scriptに慣れている人であれば、自分が作成したプログラムがある程度の規模のshell scriptの中で使うとして、自分が作成したプログラムがゼロを返してきたら、次の処理に進めるという記述をします。
main関数の最後まで処理が進んだら正常に終了したとしてゼロを返すようにしておく方が良いです。

ここまでの説明で変数はargcとargvです。それぞれ上記の表の内容を保持する容れ物として使います。

char line_buffer[LINE_BUFFER_SIZE];

テキストファイルから読み取った1行分の文字列を入れる為の変数。

FILE *fp;

ストレージの中の該当のテキストファイルの先頭を指し示すものを入れる為の変数。(計算機の中をきちんと考えるとストレージからメモリに持ってきて、など裏方が頑張っていて、説明が大変。)

whileループの中に、CSVやタブ区切りなどの入力に対して、やりたいことの処理を記述します。
whileループの中に複雑な処理を書いて「何でも対応可能」を目指すより、プログラムが簡単になるように入力の仕様を設計した方が良いです。

この場合はline_bufferに全ての価値が入るわけです。CSV形式として説明します。
それぞれの列の値を保持する変数を追加します。

line_buffer = AAA,BBB,CCC,DDD,EEE,FFF
row_0 = AAA
row_1 = BBB
row_2 = CCC
row_3 = DDD
row_4 = EEE
row_5 = FFF

上記を加工したり抽出したりするわけです。

#define

注意として負の符号を付けるときはいかなるところで展開されても問題が起こらないように括弧が必要です。

#define MINIMUM_THRESHOLD (-100)

このため正の値でも括弧を付けて統一して忘れないようにするということがよくあります。

#define LINE_BUFFER_SIZE (1024)

fprintf

file print formatの略です。

ファイルに対してprintfできる関数です。
この場合stderrファイル識別子に対してprintfする方がお作法として良いのでfprintfを使いファイル指定のところにstderrを入れています。

fopen

file openの略です。

ファイルをオープンする関数です。

r 読み込み用としてオープンする。書き込みできない。既にあるファイルを指定する。
w 書き込み用としてオープンする。基本的にファイルは新規作成となるように自分で管理する。

fgets

file get stringの略です。

取り出す文字の個数の上限を決めて、該当のファイルから1行を取り出します。
ファイルの最後まで読み、もう読むものが無いとき、fgetsはNULLを返します。

exit

上記のように、プログラムを継続させても意味がないときにプログラムをそこで終了させます。
このときにゼロ以外の値に対して仕様を決めて、どのエラーが発生したかがわかるように設計できます。

定数 / constant

constを付けることで、上書きできなくなります。つまり変数ではなく定数になります。

main.c

int
main( void )
{
    const float MATH_PI = 3.141592f;
    return 0;
}

floatは浮動小数点の値を意味します。

グローバル変数 + ファイルスコープ / global variable + file scope

定数はファイルの中のどこから読まれても問題ないので、外に出すことの方が多いです。
これをグローバル変数と言います。

開発の規模が大きくなり複数の.hや.cを扱うようになり、他のファイルからも読んだり書いたりするときはextern宣言します。
externしない限りは他のファイルからは読んだり書いたりできません。

main.c

const float MATH_PI = 3.141592f;

int
main( void )
{
    return 0;
}

下記のように使います。

main.c

const float MATH_PI = 3.141592f;

float degree_to_radian(float degree);
float radian_to_degree(float radian);

int
main( void )
{
    float deg = 90.0f;
    float rad = 0.0f;

    rad = degree_to_radian(deg);

    deg = radian_to_degree(rad);

    return 0;
}

float degree_to_radian(float degree)
{
    return (degree * MATH_PI / 180.0f);
}

float radian_to_degree(float radian)
{
    return (radian * 180.0f / MATH_PI);
}

staticとexternは仕事では重要なのですが、大学ではやらないでしょう。
複数ファイルを扱う規模の開発の説明のところで説明します。

グローバル変数でも定数なら問題とはならないでしょうが、例えば下記の場合は危険とされます。

main.c

const float MATH_PI = 3.141592f;
float deg = 90.0f;
float rad = 0.0f;

int
main( void )
{
    return 0;
}

機能が追加され、関数が追加されていく度にdegとradが好き勝手に上書きされて他の処理に悪さする可能性が増えます。
特に複数の人で開発する場合は危険です。

ですが、ここで主張したいのは、グローバル変数を使うのは絶対にやめようではなくて、用語を定義したい、全体を考えた設計をしよう、です。

100人とかで開発すると、グローバルスコープに大きい構造体を置いて担当以外のデータにアクセスしない、という管理でないと進捗がなかなかだせないということになり、誰でもアクセス可能な重要なデータ群という恐ろしいものが自然発生します。でも、正常に動作します。

大学でやる範囲ではグローバル変数を使う必要はないし、管理が増えるので使うべきでないと思います。

型 / type

計算機の内部では、という話しをします。

数値を0と1で、電圧のLOWとHIGHで扱うために設計することを考えます。

uint8_tは、話しが簡単です。unsignedでintegerで8bitの型を意味します。
8bitの符号なし整数です。
0から255までを2進数で表現できます。

uint16_tもuint32_tも同様です。
0000_0000 : 0
0000_0001 : 1
0000_0010 : 2
0000_0011 : 3
0000_0100 : 4
0000_0101 : 5
0000_0110 : 6
0000_0111 : 7
0000_1000 : 8
以下略。

次。
int8_tの話しで挫折する人が結構います。signedでintegerで8bitの型を意味します。
8bitの符号付き整数です。
0から127まではuint8_tと同様です。2の補数です。
0111_1110 : 126
0111_1111 : 127
1000_0000 : -128
1000_0001 : -127
1000_0010 : -126
以下略。

最上位ビットを符号に使う設計が最も理解しやすいと思います(1000_0001 : -1)が、計算機内部の計算が楽になるとして2の補数が選ばれ続けています。
自分でいくつか試してみましょう。

というわけで、型の情報が無いとビット列の値が何なのか決めることができません。

動的型付け言語であっても裏側で型が無いと値が何かわからないので、混み行った場合は型を強く意識することになります。

定数で整数であっても、このくらいはわかっていないとconst intの説明ができないのです。

職業プログラマーは上記を知らなくてもあまり問題になることはありません。ときどき必要に迫られるため、そのときは書籍などを紐解くことになります。

floatは、よく考えたな、という設計です。

#define

#defineの高度なものもインターネットや書籍で整理され、昔よりは積極的に使おうという気風と思います。
これもいつか説明します。

広告

IT開発関連書とビジネス書が豊富な翔泳社の通販『SEshop』
さくらのレンタルサーバ
ムームードメイン
Oisix(おいしっくす)
らでぃっしゅぼーや
珈琲きゃろっと
エプソムソルト




«       »