文と式
学べる事
- 式とは
- 文とは
ここからは、前回とはまったく異なるアプローチで、C言語を説明してみます。
What is 式
基本事項のページ で、初期化を伴う変数の宣言は以下のようなコードでした。
int main() {
// データ型 識別子 代入演算子 式;
int x = 0;
}
ここで、 式 とはC言語の構文がとる一つの要素です。
この説明を、本ページを通して解説していきますが、最初のうちは分からないのが当たり前です。
まず、C言語において、以下はすべて式です。
01.1~0(~は単項演算子で、ビット反転)1 + 1"Hello"(また詳しく説明しますが、Helloに対する先頭アドレスを保持する値として評価されます)1 < 2(<は比較演算子。この式はtrueとして評価されます)(1 + 2) - 3 * 4 / 5 % 6(整数リテラルは、intでしたね。もちろんこの式の結果はintになります)
なにか思うところがあるかもしれませんが、C言語及び多くのプログラミング言語において、ただの整数リテラル0も、式 なのです。
あえて今のうちに説明するなら、C言語において 式 は演算子を含む必要はまったくないのです。
What is 文
int x = 1 + 1;
この初期化を伴う変数の宣言において、1 + 1は式ですね。
そして、これは 代入 文 です。
代入文は次のような構文(文法)をとるものでしたね。
データ型 識別子 代入演算子(=) 式 ;
例:
// データ型 識別子 代入演算子(=) 式 ;
int x = 0;
そうです。式は、代入文の構成要素となっています。
関数がもつ文
#include <stdio.h>
int main() {
printf("Hello, ");
printf("World");
printf("!\n");
int x = 1 + 1;
123; // 式; で文となる
return 0;
}
この例では、main関数は6つの文をもっています。
関数が)の後にとりうるブロック({})の中には、任意の数の文を書くことができます。
他の文の例をみてみましょう。
return文
return文の例をみてみましょう。
int main() {
return 0;
}
これは何もないプログラムですが、0は式です。
このことから、return文の構文は次のようなものであると考えることができます。
return 式;
他の例もみてみましょう。
int one_add_two() {
return 1 + 2;
}
この関数で、return文はreturnキーワードの後に1 + 2の式をとっています。
1と2はintの整数リテラルで、+は、二つの(二項の)被演算子(オペランド)をとる、加算を行う演算子(オペレータ)です。
また、変数は式の一部になり得ます。
以下の例で説明してみます。
int sum(int x, int y) {
return x + y;
}
return文がとる式に、x + y が書かれていますが、もちろん文法上のエラーはないですね。
変数の宣言でも同様です。
int main() {
int x = 1;
int y = 2;
int z = x + y;
// int z = 1 + 2; と同じ
}
変数の宣言において、代入演算子の後は式で、ここでは1, 2, x + y がそうですね!
このように、変数は式の一部になることが分かりましたか?
関数呼び出しは式?
また、値を返す関数は、式になります。
int sum(int x, int y) {
return x + y;
}
int main() {
int a = sum(1, 2) + 3 + sum(4, 5);
return 0;
}
ここで、sum()はintを返すので、有効な式で、aは15になりますね。
このように、ある例外を除いて関数呼び出しは式になります。
例外とは、返り値がvoidの式です。
void
void とは主に、「何もない」または「型がない」という概念を表す型で、キーワードです。
実際のところ、複数の役割をもつために、一つの言葉で纏めることは難しいです。
ですが、今までのとおり、voidを識別子に使うことは出来ません。
以下の例は、そのvoidの役割の一つである、関数の返り値の型がvoidになっている例をみてみましょう。
#include <stdio.h>
void f() {
printf("Hello\n");
// 値を返さないため、return 文には式がない
return;
}
int main() {
f();
return 0;
}
この例で、f()の返り値の型は、voidになっていることが分かります。
そして、return文の定義は、
return 式;
のようなものではなかったですか?
この例では、式が書かれていませんね。
関数の返り値の型に void を指定すると、その関数は値を返さないことを示します。
つまり、関数の返り値として void を使うことで、呼び出し側は戻り値を期待しなくてよいことが分かります。
式文
先ほども少し例に示しましたが、以下はすべて 式; という構文をとる式文です。(注: あまり一般的でない名前)
123;
1+1;
printf("Hello!"); // 実はprintf()の返り値は int: 標準出力された文字列のバイト数
x;
if 文
続いて、今まで避けてきたif文を以下に示します。
見たことがある人ならば、ある程度構文を予想できるのではないでしょうか。
if (式) {}
また、ブロックのすぐ後ろに、任意の数のelse if (式) {}と、任意のelse {}をとります。
if (式) {
} else if (式) {
} else if (式) {
} else {
}
ここで、最初のif文がもつ式が最終的に1に評価された場合、if文がとりうるブロック内が実行されます。
また、それが0と評価された場合、一つ下のelse ifが評価され、さらにその式が0であった場合、elseが実行されるか、elseが存在しない場合はif文が実行されないことになります。
0/1と評価されるとは、どういうことでしょうか?
C言語における論理演算は、等価演算( == )や非等価演算( != )、論理積( && )、論理和( || )などが挙げられます。
それらの論理演算が真のときは1、偽のときは0と評価(計算)されます。
(実際には、0意外のすべての値が真とされます)
逆に、先ほども言いましたが、ただの整数リテラルも式でした。
if (0 /* 偽 */) {
do_something();
}
このようなコードを書いても、明らかに構文上正しいですし、絶対に実行されないif文が完成することになります!
文と式
いきなりですが、問題です。
以下は、構文上問題のあるコードでしょうか?
int x = if (1 != 2) {
return 42;
} else {
return 0;
};
よく考えてみましょう。
変数の宣言の構文は、以下のようなものでした。
型 識別子 = 式;
さて、もしこのコードが正しいとするなら、if (1 != 2) {...}の;までの部分は式になります。
ですが、if文は if 文 でしたね?
そうです。
C言語では、if文は文であって式でないので、このコードは構文エラーとなります。
if文のブロックの中は、任意の数の文を期待する(もつ)ので、return 文 が来ていることについては正しいですが、これは関数を終了させる処理で、if文とは関係がありません。
また、"C言語では" と言いました。
これは、また逆に if式 が存在する言語があるということです。
それは前に登場したRustです。
Rustでは、以下のように書くことができます。(▶マークで実行ができるかもしれません)
Rust:
fn main() { let x = if 1 != 2 { 42 } else { 0 }; println!("{x}"); // 42 }
このように、式と文の区別がついたでしょうか?
if文がとりうる括弧の中は式であると具体化しましたが、多くは論理式や条件(condition)と呼ばれます。
課題
検索やこのページなどを読んで、他の制御構文を、ここで覚えた文と式の違いに着目して理解してください。