南 晴輝のブログ -4ページ目

南 晴輝のブログ

マイクロプロセッサを応用した、自立動作型ロボットの開発を

目指します。

とくに ロボカップジャパンジュニア の

サッカー

レスキュー

に使用できるロボットを、目標とします。

皆さんこんにちは、南 晴輝 です。


今回は、演算子を使用する時のポイントを解説します。



演算子の優先順位


今までに紹介した演算子には優先順位があります。

例えば、足し算よりもかけ算の方が優先順位が高いので、

かけ算を先に行う等です。同じ優先順位のものは場合によっては左にある演算子から右に、

場合によっては逆に右から左に評価される場合もあります。例えば、引き算は左にある物から

評価されるので、普通の算数の場合と同じになります。

// 2012.08.07 変更

優先順位 演算子の種類 演算子


高 式 () [] ->※1 .※1

単項演算子 ! ~ ++ -- (キャスト) *※2 &※2 sizeof

二項演算子 剰余 * / %

加減 + -

シフト << >>

比較 < <= > >=

等値、非等値 == !=


論理積(ビット演算子) &

排他的論理和(ビット演算子) ^

論理和(ビット演算子) |

論理積 &&

論理和 ||

条件演算子 ? :

代入演算子、複合代入演算子 = += -= *= /= %= <<= >>= &= ^= |=

低 順次演算子 ,


 : 同順位の演算子が並んだ場合、左にある物から評価される

 : 同順位の演算子が並んだ場合、右にある物から評価される

※1 : 後述する構造体・共用体で使われる演算子
※2 : 後述するポインタで使われる演算子


これらの優先順位はすべて覚える必要はありません。

どちらかわからなくなった場合は括弧で囲めばいいのです。

逆に括弧をつけないで書こうとすると、バグの原因になったり、見直すときに見にくくなります。



また、論理演算子の「&&」「||」では注意が必要になります。「&&」の場合、1項目が「偽」になると

2項目以降は評価されません。逆に「||」では1項目が「真」になった時点で

2項目以降は評価されません。


int a, b, c;

if ( (a==1) || (b==2) || (c==3) )

c==3が評価されるのは「a!=1」でかつ「b!=2」の時だけです。


int a, b, c;

if ( (a==1) && (b==2) && (c==3) )

c==3が評価されるのは「a==1」でかつ「b==2」の時だけです。

逆にこの性質を用いると下の2例(aの値が10~20の間だけaの値が表示される)は同じになります。



#include <stdio.h>

void main(){
int a;
scanf("%d", &a);

(10 <= a) &&
(a <= 20) &&
printf("%d\n", a);
}


#include <stdio.h>

void main(){
int a;
scanf("%d", &a);

if ( 10 <= a && a <= 20)
printf("%d\n", a);
}

また、「if文」や「for文」、「while文」などで、「[条件式1]と[条件式2]が[真]なら」

という判断をする場合、

[条件式1]と[条件式2]の[偽]になる可能性が高い条件式を先に記述する方が、

処理速度が速くなることを意味しています。



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

void main(){
int a;

do{
a = rand();
printf("%d\n", a);
// }while( a != 10 && a <= 20000 );
}while( a <= 20000 && a != 10 );
}


※「rand()」関数はint型の乱数を返す関数です。これを利用するためには「stdlib.h」を

インクルードする必要があります。

上の例は、乱数を取得して、20000以下の数でかつ10でなければ繰り返すというプログラムです。

一般的に考えて、「乱数20000以下」という条件と、「乱数が10でない」という条件を考えた場合、

「偽」になる可能性は前者の条件の方が高いです。



式と値


C言語では「関数や式も値を持つ」という考え方を持っています。


すなわち「a = a + 1;」という文を考えたとき、変数「a」も、定数「1」も、 演算式「a + 1」も、

式「a = a + 1」も値を持っています。

式「a = a + 1」の値は演算式「a + 1」と同じです。

前に「「a = b = 100;」は実は複雑な使い方です」と書きました。前ページの表によると、

「=が2つある場合は右側の=が先に評価される」ことがわかります。したがって、「b = 100」 が

最初に評価されます。

上で解説したように、「b = 100」の値は [100]になるため、これが変数aに代入されます。

その結果、変数aにも変数bにも 100が代入されます。

では以下のような場合、aには何が代入されるでしょうか?


a = b == c;

「=」と「==」を比較すると「==」の方が優先順位が高いので、式「b == c」が評価されます。

その結果が変数「a」に代入されます。したがって、bとcが同じならaに「真(1)」が、

bとcが異なれば「偽(0)」が代入されます。


では、以下のような場合はどうなるでしょう?


#include <stdio.h>

void main(){
int a = 10, b = 10, c = 10;

if ( a == b == c )
printf("YES.\n");
}

aもbもcも10が代入されているので、「YES.」と表示されそうですが、

[==]が2つあった場合左から評価されます。したがって「a==b」が評価され、

同じなら 1、異なるなら0という結果になります。次にその結果とcの値を評価するので、偽になります。

したがって「YES.」とは表示されません。



条件式と値


条件式は「真」か「偽」でプログラムの流れが変わることは説明しました。

「真」というのは「0以外の値」で、「偽」というのは「0と等価」であることも説明しました。

したがって、「if文」などの条件式には、「==」や「>=」といった演算子だけでなくても

良いことになります。

つまり条件式の中に「a--」とか 「a = a + 1」とか書いても良いことになります。

以上のことから以下の例のように書き換えることもできます。

while(a != 0)

while(a)

while(a == 0)

while(!a)




以上で、C言語講座初級を終わります。


次回から、受講者からの質問についての回答を解説します。

 

最後まで、読んでくださりありがとうございました。


質問のある方は、以下のメールアドレスに質問をお寄せください。


e-mail : minami-1@ex.bw.dream.jp


よろしく、お願いします。


南 晴輝

ペタしてね      読者登録してね











皆さんこんにちは、南 晴輝 (みなみ せいき) です。


今回は、C言語で良くつかわれる演算子の続きです。



順次演算子


カンマで区切られたいくつかの式を左から順に実行します。このカンマのことを順次演算子と言います。


#include <stdio.h>

void main(){
int a = 0;

a=a+1, a=a*2, a=a+3;
printf("a = %d\n", a);
}


a = 5


#include <stdio.h>

void main(){
int a, b = 5;

// b*3がaに入る
a=(a=3, b+=a, b*3);
printf("a = %d\n", a);
}


a = 24

条件演算子(三項演算子)


条件式が「真」の場合、「偽」の場合それぞれに実行される文が短い場合に用いられる演算子です。

? : 「if~else」文と同じ

式1 ? 式2 : 式3

式1が真なら式2が実行される。

式1が偽なら式3が実行される。

したがって下の例は同じ働きをする。

if ( a > 10 )
b = a;
else
b = -a;

a > 10 ? (b = a) : (b = -a);


この演算子を入れ子にすることもできます。しかしこの演算子の入れ子は、

後でバグが出たときに見直したり、共同開発している他のプログラマーが見ると、

とても見にくいので、入れ子にすることはやめましょう。


if ( a > 10 ){
if ( a > 100 )
b = 2;
else
b = 1;
}
else
b = 0;

a > 10 ? a > 100 ? (b = 2) : (b = 1) : (b = 0);

// これも、上と同じ
b = (a > 10 ? a > 100 ? 2 : 1 : 0);

この演算子の結果は最後に実行された式の値になります。したがって右側の2行目の文では、

a=1000のとき実行されるのは「2」となり、演算子の結果も「2」となります。

それが変数bに代入されるので、最終的には「b = 2」となります。


sizeof演算子


sizeof演算子は、実際に変数がどのくらいの大きさのバイト数を使用しているかを調べる演算子です。

括弧の中は「変数名」もしくは「型」です。条件式が「真」の場合、

「偽」の場合それぞれに実行される文が短い場合に用いられる演算子です。


#include <stdio.h>

void main(){
long a;
char b;
float c;

printf(" a:%d \n b:%d \n c:%d \n double:%d \n", sizeof(a), sizeof(b), sizeof(c),
sizeof(double));
}

a:4
b:1
c:4
double:8


sizeof演算子は、配列にも利用できます。要素数10のshort型配列は、「20」という結果が得られます。

これを「sizeof(short)」で割ることで、要素数である「10」を得ることができます。

例えば配列を宣言し、その中身を0から連続する数字で初期化したい場合は、今までは左下のように

書いてきましたが、右下のように書けば配列の大きさが変わっても、

for文を変更する必要がありません。


#include <stdio.h>

void main(){
int a[20];
int i;

for ( i = 0 ; i < 20 ; i ++ )
a[i] = i;
}


#include <stdio.h>

void main(){
int a[20];
int i;

for ( i = 0 ; i < sizeof(a) / sizeof(int) ; i ++ )
a[i] = i;
}



読者登録してね

ペタしてね






皆さんこんにちは、みなみ せいき です。


今回は、C言語で良くつかわれる演算子の続きです。


じっくり読んでください。


シフト演算子


シフト演算とは論理演算と同じようにビットを操作する命令です。

具体的には、 2進数の各ビットをずらすことである。右にずらすか左にずらすかの

2種類の演算子が用意されています。

<< 左にシフト a=b<<c bを左にcビットずらす。
>> 右にシフト a=b>>c bを右にcビットずらす。


一概にシフトするといっても、ずらすデータが符号付き整数(singed)か、

符号なし整数 (unsigned)かによって、動作が異なります。





まず、符号なし整数のシフト命令について説明します。これらは論理シフトと言い、

ずらして足りなくなる1桁には無条件に0が入ります。


次に、符号付き整数のシフトを説明する。符号付き整数は、上位1bitが符号を表しています。

この符号を壊さないようにシフトさせます。

上図のように符号なし整数の「<<」と符号付き整数の「<<」は同じ働きをします。

しかし「>>」は符号付き整数と負号なし整数の場合で異なります。

さて、シフトを行うとデータはどうなるのでしょうか?まず、左にずらした時の例を示します。



このように左に1ビットずらすと、その値は2倍になります。

次に、右にずらすことを考えましょう。



右にずらす場合、符号付き整数と負号なし整数でこのような差がでます。

しかし結果的にはどちらの場合も二分の一になります。


*/

/*


キャスト演算子


キャスト(CAST)とは「型変換」のことです。下の例のように代入元と代入先の変数の型が異なる


場合、型変換を行う必要があります。一般に「char」型から「int」型への変換など、

小さい入れ物から大きな入れ物へは自動的にキャストしてくれます。


これを「暗黙の型変換」といいます。

int i;

char c = 'A';



i = c;


逆に大きな入れ物から小さな入れ物へのキャストは、コンパイラにもよりますが、

データが失われるために警告が出ます。

このような場合、コンパイラに「データが失われるかもしれないのは、わかっているよ。」

と教えなければなりません。これを「明示的な型変換」と言います。

int i = 300;
char c ;

// c = i; // 警告が出るかもしれない
c = (char)i; // 明示的な型変換


また、整数と小数(実数)の関係も場合によっては明示的な型変換が必要になります。

下の例では明示的な型変換はしていません。その次の例では明示的な型変換をしています。

キャストしなければわり算の結果は整数になってしまいます。

#include <stdio.h>

void main(){
int a = 300, b = 7;
float f = a / 7;

printf("%f\n", f);
}

#include <stdio.h>

void main(){
int a = 300, b = 7;
float f = (float)a / 7;

printf("%f\n", f);
}


42.000000
42.857143



ペタしてね
なお、floatやdouble、後述するポインタ型には使用できません。

皆さんこんにちは、みなみ せいき です。


今回は、すこしややこしいですが、C言語で良くつかわれる演算子なので、


じっくり読んでください。



複合代入演算子


複合代入演算子とは、演算と代入を一つの演算子にまとめたものです。

インクリメント演算子やデクリメント演算子も複合代入演算子の一種と言うことができます。

複合代入演算子には以下のような種類があります。


+= a+=b; aにbを加えた値をaに代入する a=a+b

-= a-=b; aからbを引いた値をaに代入する a=a-b

*= a*=b; aとbを乗じた値をaに代入する a=a*b

/= a/=b; a÷bをaに代入する a=a/b

%= a%=b; a÷bの余りをaに代入する a=a%b

>>= a>>=b; aをbビット右にシフトした値をaに代入する a=a>>b

<<= a<<=b; aをbビット左にシフトした値をaに代入する a=a<<b

&= a&=b; aとbの論理積をaに代入する a=a&b

|= a|=b; aとbの論理和をaに代入する a=a|b

^= a^=b; aとbの排他的論理和をaに代入する a=a^b


ビット演算子


ビット演算子とは、各変数をビット単位で処理(計算)するための演算子で、

以下のような種類があります。

& ビット積(AND) a=b&c; bとcの各ビット毎の論理積をとり、その結果をaに代入する。

| ビット和(OR) a=b|c; bとcの各ビット毎の論理和をとり、その結果をaに代入する。

^ ビット排他的論理和(XOR) a=b^c; bとcの各ビット毎の排他的論理和をとり、その結果をaに代入する。

~ ビット否定(NOT) a=~b; bの各ビットを反転しその結果をaに代入する。

AND

b c 結果(a)
0 0 0
0 1 0
1 0 0
1 1 1


AND演算はビットを0でマスクするのに使用します。マスクとは、あるビットを 0もしくは1に

変えることを言います。

例えば、奇数か偶数かを調べるためには、最下位を残して0でマスクします。

その値が1なら奇数、0なら偶数と言うことになります。
*/


/*
#include <stdio.h>

void main(){
int i;

scanf("%d", &i);
if ( i & 1 )
printf("奇数です\n");
else
printf("偶数です\n");
}
*/



/*

OR

b c 結果(a)
0 0 0
0 1 1
1 0 1
1 1 1


OR演算はビットを1でマスクするのに使用します。


XOR

b c 結果(a)
0 0 0
0 1 1
1 0 1
1 1 0


XOR演算はビットを必要な部分だけ反転させるのに使用します。


XOR : エクスクルーシブ オアです。


NOT

b 結果(a)
0 1
1 0


NOT演算は1の補数を表します。1の補数とは全てのビットを反転しものです。


*/


ビット演算子 は、どんな時に使われるのか,よくわからないとおもいます。


組み込みプログラミングでは、CPUの内部レジスターの1ビットだけを処理するとか、


周辺LSI のコマンドレジスターなどの設定に使われます。






演算子

算術演算子
四則演算子 +, -, *, /, %
符号変換 -

比較演算子
大小比較 < > <= >=
等価、非等価 == !=

論理演算子
論理否定 !
論理積 &&
論理和 ||

インクリメント演算子 ++

デクリメント演算子 --

ビット演算子
論理積 &
論理和 |
排他的論理和 ^
論理否定(1の補数) ~

シフト演算子
左シフト <<
右シフト >>

アドレス演算子 &

間接演算子 *

代入演算子
代入 =
四則演算 += -= *= /= %=
シフト演算 <<= >>=
ビット演算 &= |= ^=

cast演算子 (型) 例えば、(int), (char)

順次演算子 ,

条件(三項演算子) ? :

sizeof演算子 sizeof


インクリメント演算子、デクリメント演算子について


下記に例を示すように、変数を1増やす時は、「i = i + 1」としてきました。

ところが「変数を1増やす」という操作は、頻繁にあるので別の演算子が用意されています。

その演算子を使用すると下記のようになります。



int i;
for ( i = 0 ; i < 10 ; i = i + 1 ) // i = i + 1
printf("%d\n", i);


int i;
for ( i = 0 ; i < 10 ; i++ ) // i++
printf("%d\n", i);

「++」がインクリメント演算子です。

「変数の値を1減らす」演算子はデクリメント演算子と言い、「--」です。

これらの演算子は、「i++」のように変数の後に書くことも、「++i」のように変数の前に

書くこともできます。

ところが「j = i++」のように式の中に組み込むと、変数の前に書いた場合と、

変数の後に書いた場合の動作が異なります。

変数の前に書いた場合は、式を評価する前にインクリメントします。

逆に 変数の後に書いた場合は、式を評価した後にインクリメントします。

デクリメント演算子「--」も同様です。

*/


/*
#include <stdio.h>

void main(){
int a, b;

a = 3;
b = ++a;

printf("a = %d, b = %d\n", a, b); // a= 4, b = 4


a = 3;
b = a++;

printf("a = %d, b = %d\n", a, b); // a= 4, b = 3

}

*/

/*
ただし、1つの式の中に同じ変数に対するインクリメント演算子が複数あっては、いけません。

もちろんデクリメント演算子も同じです。

例えば、「b = a++ + a++」は、よくありません。コンパイラによって異なった評価になります。

「printf("%d %d\n", ++a, ++a);」というのも、よくありません。aが0のとき「1 2」と表示されるか

「2 1」と評価されるのか不明です。

*/