コラム
2024/09/20
第83回 プログラミングについて『いまさらですがC言語の門を叩こう その6〜 演算子と式 〜』
演算とは要するに計算のことで、演算子とは計算をさせる記号のことを言います。例えば、
1+2
という式では、 + が2つの値の加算を行なう演算子ということになり、
-5
という式では、 - が値 5 の符号を反転する演算子になります。
この様に、C言語の演算子には2つの値の演算を行なう二項演算子と、1つの値の演算を行なう単項演算子という2種類があります。
また、C言語の演算子には優先順位というものが決められています。例えば、
1+2*3
という式は、通常の数式に置き換えると 1+2x3 という意味になり、代数上での優先順位を同じく、2 と 3 を掛けてその値を 1 に加算するという順番になります。代数と同じで計算の順序を変更する場合にはカッコ ( ) を使用します。
(1+2)*3
とすれば、1 に 2 を加わえ、その値に 3 を掛けるということになります。
∴ 演算子の種類とその優先順位は書籍を参考にして下さい。
C言語には代入文というものがありません。FORTRANならば、
A=1+2 (FORTRAN)
a=1+2; (C言語)
という式は、1 に 2 を加え A に代入するという意味で、この式全体としては代入文と解釈されます。しかし、C言語は演算の内容はまったく同じなのですが、演算子 = は単なる代入を行う演算子であり、式全体としては代入文ではなく値と解釈されます。
ですのでFORTRANでは、
IF(A=1+2)THEN
という文では、カッコの中には値しか記述することができませんので、文法エラーになります。ところが、C言語は、
if(a=1+2){
のカッコの中は値と解釈されますのでエラーにはなりません。更にC言語は式として、
100;
と書いてもエラーにはならないのです。FORTRANでは式は代入文になっていなければならないのでエラーなのですが、C言語は値であればいいのでエラーにはなりません。このようにFORTRANなどの言語を勉強したことのある方にはすごく奇妙な感じがするとは思いますが、解釈の違いで許されるものとそうでないものが違ってくるのです。
言い忘れましたが、
a=i+j;
という式は、代数上では a は i+j と同じという意味(方程式)ですが、C言語の場合(殆どのプログラミング言語でも)では、 i と j を加算してその値を a に代入するという意味です。以前私の兄がプログラムの勉強をするというので入門書を読んでいたのですが、この意味を理解するのに何日もかかったことがありました。頭を切り替えて下さい。
* 演算子は乗算とポインタを表す演算子に使用されますので注意が必要です。
int i,j,k,*p;
:
i=j*k; ー(1)
i=j**p; ー(2)
i=j*p; ー(3)
(1)と(2)は値と値の掛け算になるので問題はありませんが、(3)は値とポインタ(アド レス)の掛け算になってしまうのでエラーになってしまいます。当然のことですがポイ ンタに対しては加算と減算しか許されていませんのでエラーになってしまうのです。
どうしてなのかC言語にはベキ乗の演算子がありません。FORTRANでは ** がベキ乗の演算子だったのですが、多分C言語の作者達もこの演算子を使用できるようにしたかったと思います。ところが、上の例の(2)を見てもらえば分かってもらえると思いますが、C言語で ** という演算子を使用できるようにすると、
i=j**p;
が j の p 乗なのか、j に *p を乗算するのかが判別できないのでC言語で採用することを断念したのではないかと私は勝手に思うのです。FORTRAN(VAX FOR TRAN)でもベキ乗の演算はコンパイラによって関数の呼び出しに変換されており、機械語そのものには対応されていませんので、C言語の作者達はこのような曖昧な表現になるのであれば、どうせ関数呼び出しに変換するのだからなくてもいいだろうと考えたか、逆に関数呼び出しにしなければならない演算子は使用したくなかったのかのどちらかだったのかも知れません。
加算とか乗算などの算術演算はコンピュータというものは同じ型のもの同士しか計算をすることができません。ですので、
1+2.0
という計算は整数型と実数型の演算ですのでコンパイラによって、
1.0+2.0
と実数型どうしの演算に置き換えられます。このことは1バイトの整数と2バイトの整数の間でも同様に発生します。具体的なことは書籍を参考にしてもらうことにして、ここでは式を書くときに注意しておかなければならないことをお話しします。
int i;
i=1/3*10; ー(1)
i=1/3.0*10; ー(2)
の場合2つの式の結果は全然違うのです。(1)は 0 になり、(2)は 3 になります。(1)が、
(1/3) * 10
で、整数型どうしの除算ですのでカッコの中は 0 になるのに対し、(2)は、
(1/3.0) * 10
で、カッコの中は整数と実数の除算ですので、
(1.0/3.0) * 10
となり、カッコの中は 0.33333333333333 となります。更にこの値に整数値 10 を掛けるときに 10.0 と実数に変換され、結果として 3.3333333333 となります。更に整数変数 i に代入するときに少数が切り捨てられるので 3 になるのです。このような式はプログラムを書いていると頻繁にでてきますので、バグの原因になることもあります。
関係ないかもしれませんが、コンパイラによっては次のようなこともあります。
int i;
i=10;
i=i*0.3;
printf("A=%d\n",i);
i=10*0.3;
printf("B=%d\n",i);
この結果が、
A=2
B=3
となるのです。不思議な感じもしますが本当にあるのです。この結果になるのは、Visual C++の16ビット版のコンパイラです。
i=10;
i=i*0.3;
は実行時に計算されるので多分実数値に誤差が出たのだろうと考えられます。これに対し、
i=10*0.3;
は定数と定数の演算ですのでコンパイル時に計算され、うまい具合に誤差が出なかったのだろうと推測されます。このように実数と整数の計算については予想もしないことが起こりますので注意して下さい。
C言語の単項演算子に ++ とか += といったものがあります。
i=i+1; ー(1)
i++; ー(2)
i=i+3; ー(3)
i+=3; ー(4)
(1)と(2)式、(3)と(4)式は全く同じ意味です。どちらの表現を使っても構いませんが、プログラムが読みやすい方を選んだ方がいいと思います。C言語の表現の簡潔さはこういうところにも現れていて、(1)は i に 1 を加え i に代入するという意味で、(2)は i を1つ増やすという意味になります。当然 i++ は i+=1 と同じ意味ですが、通常プログラムでは1つ増やしたり減らしたりすることが頻繁にありますので、プログラムを簡潔に表現するためにこのような演算子が設けられたのです。
今回は少し短いのですが、また次回。
1+2
という式では、 + が2つの値の加算を行なう演算子ということになり、
-5
という式では、 - が値 5 の符号を反転する演算子になります。
この様に、C言語の演算子には2つの値の演算を行なう二項演算子と、1つの値の演算を行なう単項演算子という2種類があります。
また、C言語の演算子には優先順位というものが決められています。例えば、
1+2*3
という式は、通常の数式に置き換えると 1+2x3 という意味になり、代数上での優先順位を同じく、2 と 3 を掛けてその値を 1 に加算するという順番になります。代数と同じで計算の順序を変更する場合にはカッコ ( ) を使用します。
(1+2)*3
とすれば、1 に 2 を加わえ、その値に 3 を掛けるということになります。
∴ 演算子の種類とその優先順位は書籍を参考にして下さい。
C言語には代入文というものがありません。FORTRANならば、
A=1+2 (FORTRAN)
a=1+2; (C言語)
という式は、1 に 2 を加え A に代入するという意味で、この式全体としては代入文と解釈されます。しかし、C言語は演算の内容はまったく同じなのですが、演算子 = は単なる代入を行う演算子であり、式全体としては代入文ではなく値と解釈されます。
ですのでFORTRANでは、
IF(A=1+2)THEN
という文では、カッコの中には値しか記述することができませんので、文法エラーになります。ところが、C言語は、
if(a=1+2){
のカッコの中は値と解釈されますのでエラーにはなりません。更にC言語は式として、
100;
と書いてもエラーにはならないのです。FORTRANでは式は代入文になっていなければならないのでエラーなのですが、C言語は値であればいいのでエラーにはなりません。このようにFORTRANなどの言語を勉強したことのある方にはすごく奇妙な感じがするとは思いますが、解釈の違いで許されるものとそうでないものが違ってくるのです。
言い忘れましたが、
a=i+j;
という式は、代数上では a は i+j と同じという意味(方程式)ですが、C言語の場合(殆どのプログラミング言語でも)では、 i と j を加算してその値を a に代入するという意味です。以前私の兄がプログラムの勉強をするというので入門書を読んでいたのですが、この意味を理解するのに何日もかかったことがありました。頭を切り替えて下さい。
* 演算子は乗算とポインタを表す演算子に使用されますので注意が必要です。
int i,j,k,*p;
:
i=j*k; ー(1)
i=j**p; ー(2)
i=j*p; ー(3)
(1)と(2)は値と値の掛け算になるので問題はありませんが、(3)は値とポインタ(アド レス)の掛け算になってしまうのでエラーになってしまいます。当然のことですがポイ ンタに対しては加算と減算しか許されていませんのでエラーになってしまうのです。
どうしてなのかC言語にはベキ乗の演算子がありません。FORTRANでは ** がベキ乗の演算子だったのですが、多分C言語の作者達もこの演算子を使用できるようにしたかったと思います。ところが、上の例の(2)を見てもらえば分かってもらえると思いますが、C言語で ** という演算子を使用できるようにすると、
i=j**p;
が j の p 乗なのか、j に *p を乗算するのかが判別できないのでC言語で採用することを断念したのではないかと私は勝手に思うのです。FORTRAN(VAX FOR TRAN)でもベキ乗の演算はコンパイラによって関数の呼び出しに変換されており、機械語そのものには対応されていませんので、C言語の作者達はこのような曖昧な表現になるのであれば、どうせ関数呼び出しに変換するのだからなくてもいいだろうと考えたか、逆に関数呼び出しにしなければならない演算子は使用したくなかったのかのどちらかだったのかも知れません。
加算とか乗算などの算術演算はコンピュータというものは同じ型のもの同士しか計算をすることができません。ですので、
1+2.0
という計算は整数型と実数型の演算ですのでコンパイラによって、
1.0+2.0
と実数型どうしの演算に置き換えられます。このことは1バイトの整数と2バイトの整数の間でも同様に発生します。具体的なことは書籍を参考にしてもらうことにして、ここでは式を書くときに注意しておかなければならないことをお話しします。
int i;
i=1/3*10; ー(1)
i=1/3.0*10; ー(2)
の場合2つの式の結果は全然違うのです。(1)は 0 になり、(2)は 3 になります。(1)が、
(1/3) * 10
で、整数型どうしの除算ですのでカッコの中は 0 になるのに対し、(2)は、
(1/3.0) * 10
で、カッコの中は整数と実数の除算ですので、
(1.0/3.0) * 10
となり、カッコの中は 0.33333333333333 となります。更にこの値に整数値 10 を掛けるときに 10.0 と実数に変換され、結果として 3.3333333333 となります。更に整数変数 i に代入するときに少数が切り捨てられるので 3 になるのです。このような式はプログラムを書いていると頻繁にでてきますので、バグの原因になることもあります。
関係ないかもしれませんが、コンパイラによっては次のようなこともあります。
int i;
i=10;
i=i*0.3;
printf("A=%d\n",i);
i=10*0.3;
printf("B=%d\n",i);
この結果が、
A=2
B=3
となるのです。不思議な感じもしますが本当にあるのです。この結果になるのは、Visual C++の16ビット版のコンパイラです。
i=10;
i=i*0.3;
は実行時に計算されるので多分実数値に誤差が出たのだろうと考えられます。これに対し、
i=10*0.3;
は定数と定数の演算ですのでコンパイル時に計算され、うまい具合に誤差が出なかったのだろうと推測されます。このように実数と整数の計算については予想もしないことが起こりますので注意して下さい。
C言語の単項演算子に ++ とか += といったものがあります。
i=i+1; ー(1)
i++; ー(2)
i=i+3; ー(3)
i+=3; ー(4)
(1)と(2)式、(3)と(4)式は全く同じ意味です。どちらの表現を使っても構いませんが、プログラムが読みやすい方を選んだ方がいいと思います。C言語の表現の簡潔さはこういうところにも現れていて、(1)は i に 1 を加え i に代入するという意味で、(2)は i を1つ増やすという意味になります。当然 i++ は i+=1 と同じ意味ですが、通常プログラムでは1つ増やしたり減らしたりすることが頻繁にありますので、プログラムを簡潔に表現するためにこのような演算子が設けられたのです。
今回は少し短いのですが、また次回。