関係演算子と論理演算子

関係演算子

>より大きい
>=より大きいか等しい
<より小さい
<=より小さいか等しい
==等しい
!=等しくない

論理演算子

&&AND(論理積)
||OR(論理和)
!NOT(否定)

関係演算子と論理演算子の優先順位
上のものほど優先順位が高い

!
> >= < <=
== !=
&&
||

関係演算子と論理演算子は結果が真のとき 1 を、偽のとき 0 を生成する。次のプログラムは2つの整数を入力し、一連の関係演算と論理演算を行って結果を出力する。出力はすべて 1 か 0 になる。

#include

int main(void)
{
    int i, j;

    printf("Input 1st number: ");
    scanf("%d", &i);
    printf("Input 2nd number: ");
    scanf("%d", &j);

    printf("i < j: %d\n", i < j);
    printf("i <= j: %d\n", i <= j);
    printf("i == j: %d\n", i == j);
    printf("i > j: %d\n", i > j);
    printf("i >= j: %d\n", i >= j);

    printf("i && j: %d\n", i && j);
    printf("i || j: %d\n", i || j);
    printf("!i !j: %d %d\n", !i, !j);

    return 0;
}

実行結果:

takatoh@nightschool $ ./sample_2_7
Input 1st number: 5
Input 2nd number: 7
i < j: 1
i <= j: 1
i == j: 0
i > j: 0
i >= j: 0
i && j: 1
i || j: 1
!i !j: 0 0

エスケープ文字

C には次のようなエスケープ文字がある。

\bバックスペース
\fページ送り
\n改行
\r復帰
\t水平タブ
\”二重引用符
\’引用符
\0ヌル
\\円マーク
\v垂直タブ
\aベル
\?疑問符
\N8進定数(Nは8進数)
\xN16進定数(Nは16進数)

このうち \n はもう使っている。あとは引用符とか水平タブとかがよく使いそうかな。

特に重要なのは \0 (ヌル文字)だ。C で文字列を扱うときには終端文字としてヌル文字が必要になる。これはそのうち出てくるはず。

ところで上のとおりだとすると8進数の0も \0 になるんだけど、それってどうなんだろ。もっとも8進数って使いどことがわからないけど。

あと、エスケープ文字はあくまで文字定数なので、char 型の変数に代入するときは引用符で囲む必要がある。

char ch;

ch = '\t';

みたいに。

次のプログラムは、1 から 10 に対して、各行にその数、2乗、3乗を表示する。

#include

int main(void)
{
    int i;

    for (i = 1; i < 11; i++) {
        printf("%d\t%d\t%d\n", i, i * i, i * i * i);
    }

    return 0;
}

実行結果:

takatoh@nightschool $ ./sample_2_6
1	1	1
2	4	8
3	9	27
4	16	64
5	25	125
6	36	216
7	49	343
8	64	512
9	81	729
10	100	1000

インクリメント演算子とデクリメント演算子

前のエントリで for ループのインクリメント部をこう書いた。

i = i + 1

インクリメント演算子を使うとこう書ける。

i++

インクリメント演算子は変数の値を1大きくする演算子だ。同様に1小さくするデクリメント演算子もある。

i--

デクリメント演算子を使った for ループの例:

for (i = 100; i > 0; i--) {
    printf("%d", i);
}

インクリメント演算子とデクリメント演算子には、変数の後につける書き方と、変数の前につける書き方がある。上の例の場合にはどっちでも変わらないけど、場合によっては結果に影響することもある。

試してみよう。このプログラムは、「11 10」と出力する。

#include

int main(void)
{
    int i, j;

    i = 10;
    j = i++;

    printf("%d %d\n", i, j);

    return 0;
}
takatoh@nightschool $ ./sample_2_5a
11 10

これに対してこっちのプログラムは「11 11」と出力する。

#include

int main(void)
{
    int i, j;

    i = 10;
    j = ++i;

    printf("%d %d\n", i, j);

    return 0;
}
takatoh@nightschool $ ./sample_2_5b
11 11

違うのは9行目だけだ。i++ が j に代入したあとに i をインクリメントするのに対して、++i は代入の前にインクリメントする。結果として j の値が違うものになるわけだ。これはデクリメント演算子でも同じ。気をつけないとね。

forループ

for ループの一般的な形式は次の通り。

for (初期設定部; 条件判定部; インクリメント部) {
    文1;
    文2;
    ....
}

実行する文が1つの時はコードブロックにしなくてもいい。

次のプログラムは、17 から 100 の間で 17 で割り切れる数を表示する。

#include

int main(void)
{
    int i;

    for (i = 17; i < 101; i = i + 1) {
        if (i % 17 == 0) {
            printf("%d\n", i);
        }
    }
}

実行:

takatoh@nightschool $ ./sample_2_4
17
34
51
68
85

コードブロック

if や else は基本的に次に続く1つの文しか実行しない。複数の文を実行したいときには、それらの文を中括弧 { } で囲ってやる。こうすることで複数の文を1つのまとまりとして実行してくれる。これをコードブロック、または複文と呼ぶ。
コードブロックは単一の文を書けるところならどこにでも書ける。

次のプログラムは、メートル-フィート換算のプログラムをコードブロックを使って書いたもの。if と else のあとにそれぞれコードブロックが続いている。

#include

int main(void)
{
    float num;
    int choice;

    printf("1: feet to meter, 2: meter to feet\n");
    printf("Choice: ");
    scanf("%d", &choice);

    if (choice == 1) {
        printf("How feet? ");
        scanf("%f", &num);
        printf("%f\n", num / 3.28);
    } else {
        printf("How meters? ");
        scanf("%f", &num);
        printf("%f\n", num * 3.28);
    }

    return 0;
}

実行例:

takatoh@nightschool $ ./sample_2_3
1: feet to meter, 2: meter to feet
Choice: 2
How meters? 1.5
4.920000

else文

else 文は if 文と組み合わせて使う。if の条件式が偽だった時、else に続く文が実行される。

if (条件式)
    文1;
else
    文2;

次のプログラムは、2つの整数を入力から受け取りその商を出力するけど、2つ目の数が 0 だった場合にはメッセージを出力する。

#include

int main(void)
{
    int num1, num2;

    printf("Input first number? ");
    scanf("%d", &num1);

    printf("Input second number? ");
    scanf("%d", &num2);

    if (num2 == 0)
        printf("Could not devide by zero.\n");
    else
        printf("Answer: %d\n", num1 / num2);

    return 0;
}

実行例:

takatoh@nightschool $ ./sample_2_2
Input first number? 4
Input second number? 2
Answer: 2
takatoh@nightschool $ ./sample_2_2
Input first number? 5
Input second number? 0
Could not devide by zero.

if文

if文は、if に続く条件式が真の時だけ、文を実行する。

if (条件式)
    文;

C では 0 以外が真となり、0 だけが偽となる。ということは、条件式は 0 かそれ以外の整数に評価されるってことかな?

次のプログラムは、フィートをメートルに、またはメートルをフィートに換算する。

#include

int main(void)
{
    float num;
    int choice;

    printf("Input? ");
    scanf("%f", &num);

    printf("1: feet to meter, 2: meter to feet\n");
    printf("Choice: ");
    scanf("%d", &choice);

    if (choice == 1)
        printf("%f\n", num / 3.28);
    if (choice == 2)
        printf("%f\n", num * 3.28);

    return 0;
}

実行例:

takatoh@nightschool $ ./sample_2_1
Input? 1.5
1: feet to meter, 2: meter to feet
Choice: 2
4.920000

キーワード

ANSI C 標準では 32 のキーワード(予約語?)が規定されている。

auto double int struct
break else long switch
case enum register typedef
char extern return union
const float short unsigned
continue for signed void
default goto sizeof volatile
do if static while

また、よく使われる拡張キーワードには次のものがある。

asm _cs _ds _es
_ss cdecl far huge
interrupt near pascal

すべて小文字。キーワードは関数名や変数名には使えない。
なんか意味の想像できないのもあるけど、それはそのうち。

関数の戻り値

関数から戻り値を返すには return を使う。

return 値;

戻り値の値は関数定義の戻り値と同じじゃなければならない。戻り値が vold の場合は、単に return と書ける。

関数の途中で return に出会うと、そこで(戻り値を伴って)呼び出し元に制御が戻る。以降に記述があっても実行されない。

呼び出し側で戻り値を使うには、変数に代入すればいい。または、printf などの関数の引数としてそのまま使うこともできる。

#include

int square(int n);

int main(void)
{
    int i;
    int answer;

    printf("Input? ");
    scanf("%d", &i);

    answer = square(i);
    printf("Square is %d\n", answer);
}

int square(int n)
{
    return n * n;
}

実行結果:

takatoh@nightschool $ ./sample_1_8
Input? 5
Square is 25

関数の定義

関数の定義は、一般的には次の形式をしている。

戻り値の型 関数名(引数リスト)
{
    文;
    ....
    return 戻り値;
}

基本的には main 関数と同じだ。戻り値がない場合は void を指定する。また、引数がない場合にも vold と書いておく。戻り値は return で返す。

main 関数と違うのは、関数プロトタイプの宣言が必要だということ。関数プロトタイプは次のような形式をしている。

戻り値の型 関数名(引数リスト);

これを実際に関数を定義する前に書いておく(普通はファイルのはじめのほう)。

#include

void func1(void); // prototype of func1()

int main(void)
{
    printf("I like ");
    func1();
    printf(".\n");
}

void func1(void)
{
    printf("C");
}

実行結果:

takatoh@nightschool $ ./sample_1_7
I like C.

ところで、関数プロトタイプを書かないとどうなるだろう。上のプログラムの関数プロトタイプをコメントアウトしてコンパイルしてみると:

takatoh@nightschool $ gcc sample_1_7a.c -o sample_1_7a
sample_1_7a.c:13:6: warning: conflicting types for ‘func1’ [enabled by default]
 void func1(void)
      ^
sample_1_7a.c:9:5: note: previous implicit declaration of ‘func1’ was here
     func1();
     ^

何やらよくわからないけど、エラーになるってことはわかった。