基礎 C言語
  1. 算術演算子

  2. 2進数の演算

  3. 条件式

  4. 繰り返し

  5. 条件分岐

  6. 配列

  7. 関数



算術演算子

算術演算子は、加減乗除を行うもので、算数のそれと同じものである。

加算       : +
減算       : -
乗算       : *
除算       : /
除算の余り : %


演算子の優先順位は、* / % は、3者の内、左にある方が優先、
+ - も、2者の内、左にある方が優先、
* / % は、左右に関係なく、+ - よりも優先度が高い。
また、()内の計算は優先順位が最も高い。

main()
{
    int m = 5;
    
    printf("%d %d", (m + 5) * 2, m + 5 * 2);
}

----- 出力結果 -----
20 15
--------------------
		


2進数の演算

2進数の演算には、符号無し整数型変数が使われることが多い(シフト演算の変数型を参照)。
シフト演算子( << や >> )を使うと、2進数のシフトが行われる。
シフトとは、ビットを全体的に左や右にずらすことである。
例えば、10011101 という2進数がある場合、左に1つシフトすると
100111010 となる。
シフト演算で格納先の変数のサイズから、はみ出した部分は、
切り捨てられ、何もないところからシフトしたビットは、0 になる。

main()
{
    unsigned char m = 0xff;
    
    printf("1バイトの16進数値 ff を左に 2 シフトすると %x\n", m << 2);
    printf("1バイトの16進数値 ff を右に 1 シフトすると %x\n", m >> 1);
}

----- 出力結果 -----
3fc 7f
--------------------

16進数の ff は、2進数の 11111111 であり、3fc は、1111111100 
7f は、1111111 である。

// シフト演算の格納先変数が、符号無し1バイト整数の場合
main()
{
    unsigned char m = 0xff;
    unsigned char n1,n2;
    
    n1 = m << 2;
    n2 = m >> 1;
    
    printf("1バイトの16進数値 ff を左に 2 シフトすると %x\n", n1);
    printf("1バイトの16進数値 ff を右に 1 シフトすると %x\n", n2);
}

----- 出力結果 -----
fc 7f
--------------------

シフト演算は、算術演算に使われることがある。
左に、nシフトすると 2 のn乗倍したことに、
右に、nシフトすると 2 の1/n乗倍したことになるからである。
画像処理は、時間が掛かる場合が多く、極力処理時間を短縮したい。
そのため、2 のn乗倍するような計算がある場合は、処理時間が短縮できる
ため、シフト演算を用いる場合がある。

main()
{
    int n = 10;
    
    printf("%d の4倍は、%d", n, n << 2);
}

----- 出力結果 -----
10 の4倍は、40
--------------------

ビット処理演算子を使うと、2進数の各ビットごとの演算が行われる。
これは論理回路では、論理演算と呼ばれる。
C言語には、論理演算子と呼ばれるものがあるが、これは、条件式で使うものであり、
論理回路とコンピューター言語では、論理演算の意味が異なる。
ビット処理演算子は、主にフラグや数字のマスクに使う。

& : 論理積(AND)
| : 論理和(OR)
^ : 排他的論理和(XOR)
~ : 論理否定(NOT)

| は、[Shift] + [\]
~(チルダ)は、[Shift] + [^]

%x を %0?x にすると、? に足りない上位の桁が 0 で埋められる。

main()
{
    unsigned char m1 = 0xac;   // 2進数の 10101100
    unsigned char m2 = 0xf;    // 2進数の     1111
    unsigned char n1,n2,n3,n4; // 格納先変数
    
    n1 = m1 & m2;
    n2 = m1 | m2;
    n3 = m1 ^ m2;
    n4 = ~m1;
    
    printf("16進数値 ac と f の論理積       %02x\n", n1);
    printf("16進数値 ac と f の論理和       %02x\n", n2);
    printf("16進数値 ac と f の排他的論理和 %02x\n", n3);
    printf("16進数値 ac の論理否定          %02x\n", n4);
}

----- 出力結果 -----
16進数値 ac と f の論理積       0c
16進数値 ac と f の論理和       af
16進数値 ac と f の排他的論理和 a3
16進数値 ac の論理否定          53
--------------------

16進数の  c は 2進数の     1100
16進数の af は 2進数の 10101111
16進数の a3 は 2進数の 10100011
16進数の 53 は 2進数の  1010011

論理積は、2つの数字をビットごとに掛け算する。
どちらかに 0 が入っていれば、結果は 0 になる。
1 になるのは、両方とも 1 の場合だけである。
ビット処理演算は、1 を真(しん)、 0 を偽(ぎ)と言う。
しかし、何かが正しいのでも間違っているのでもない。呼び方だけである。
英語にすると、TRUE と FALSE になる。

   10101100
&  00001111
--------------
   00001100

論理和は、どちらかに、1 が入っていれば、結果は 1 になる。
0 になるのは、両方とも 0 の場合だけである。

   10101100
|  00001111
--------------
   10101111

排他的論理和は、双方の数値が異なる場合は 1 、同じ場合は 0 となる。

   10101100
^  00001111
--------------
   10100011

論理否定は、各ビットの値を反転させる。
1 の補数の事である。

~  10101100
--------------
   01010011
		


条件式

条件式は、繰り返しのループ終了条件や条件分岐の分岐条件として使われる。
条件式の結果は、TRUE(真) か FALSE(偽) のみであり、
TRUE とは整数の 0 以外 のことであり、FALSE とは 0 のことである。
関係演算子と否定演算子と論理演算子は、条件式でのみ使われる。
関係演算子は、算数と同じ意味である。ただし、= は == である。
否定演算子は、変数や関係演算子の計算結果などが、FALSE の場合、TRUE とする。
論理演算子は、変数や否定演算子や関係演算子の結果に対して、演算を行う。
演算の優先順位は、算術演算子 > 関係演算子 > 否定演算子 > 論理演算子である。
条件式を正反対の条件式にしたい場合は、その条件式の演算子をすべて逆にすると良い。
例えば、(x < 5 && flag) という条件式があれば、(x >= 5 || !flag) とすれば、逆の条件になる。

[関係演算子]
<  : より小さい場合 TRUE、そうでなければ FALSE
>  : より大きい場合 TRUE、そうでなければ FALSE
<= : 以下の場合     TRUE、そうでなければ FALSE
>= : 以上の場合     TRUE、そうでなければ FALSE
== : 等しい場合     TRUE、そうでなければ FALSE
!= : 等しくない場合 TRUE、そうでなければ FALSE

[否定演算子]
!  : FALSE の場合は TRUE、TRUE の場合は FALSE

[論理演算子]
&& : 両方が TRUE の場合にのみ TRUE、そうでなければ FALSE
|| : どちらかが TRUE であれば TRUE、そうでなければ FALSE

main()
{
    printf("5 < 3 は %d\n", 5 < 3);
    printf("5 < 3 の否定は %d\n", !(5 < 3));
    printf("5 < 3 かつ !(5 < 3) は %d\n", 5 < 3 && !(5 < 3));
    printf("5 < 3 または !(5 < 3) は %d\n", 5 < 3 || !(5 < 3));
}

----- 出力結果 -----
5 < 3 は 0
5 < 3 の否定は 1
5 < 3 かつ !(5 < 3) は 0
5 < 3 または !(5 < 3) は 1
--------------------
		


繰り返し

次の例は、変数 i を 0 から 1 ずつインクリメントして、そのたびに、
i の値を出力している。
i の値が、5 以上になったら、繰り返しは終了する。
i が 5 のときは、実行されない。

main()
{
    int i;
    
    for(i=0; i<5; i++){
        printf("%d\n", i);
    }
}

----- 出力結果 -----
0
1
2
3
4
--------------------

インクリメントとは、呼び出されるたびに変数の値を 1 ずつ
増やすことである。

int i;
i += 1;

とすると 変数 i の値は、1 加算されて、1 になる。
i += 5; とすると、5 加算される。
+ を - にすると、減算され、* や / にすると乗算、除算される。
i += 1; と i -= 1; だけは、i ++; や i --; と書くことも出来る。

main()
{
    int i;
    i ++;
    i += 5;
    i *= 10;
    printf("%d", i);
}

----- 出力結果 -----
60
--------------------

+= 1 や ++ を使うのがインクリメントであり、-= 1 や -- を使うのが
デクリメントである。
増減幅が 2 以上の場合は、2 インクリメントするなどと言えば良い。
+=, -=, *=, /= なども = と同じ代入演算子である。
++ は、インクリメント演算子、-- は、デクリメント演算子と呼ばれる。

int i;
i ++;

は、

int i,j;
j = i + 1;
i = j;

と全く同じ事である。これらの演算子を使うとコードが短くなる。

繰り返しは、for文 以外に、while文 や do〜while文 がある。
次の例は、上の for 文と同じ処理を行う。while文 や do〜while文
は、()内の条件式を満たす間、{}内の処理を繰り返す。

main()
{
    int i;
    while(i < 5)
    {
        printf("%d\n", i);
        i ++;
    }
}

do〜while文 は条件式の判定が処理の最後になるため、
while文 とは違い、一度は必ず処理が行われる。

main()
{
    int i = 10;
    do{
        printf("%d\n", i);
        i ++;
    }while(i < 5);
}

----- 出力結果 -----
10
--------------------

インクリメント演算子とデクリメント演算子には、
前置きと後置きがあり、動作が少し違う。

main()
{
    int i,j;
    while(i < 5)
    {
        printf("%d %d\n", i ++, ++ j);
    }
}

----- 出力結果 -----
0 1
1 2
2 3
3 4
4 5
--------------------

i ++ が後置きで、++ i が前置きである。
後置きは、実行後に加減算されるのに対し、前置きは
実行と同時に行われる。
次のような使い方をする場合は、両者の動作に違いはない。

main()
{
    int i;
    while(i < 5)
    {
        printf("%d\n", i);
        ++ i;
    }
}

繰り返し文を途中で抜けたい場合は、break 文や、goto 文を使い、
処理をその回だけ飛ばしたい場合は、continue 文を使う。
これらは、次の項で説明する条件分岐文と併用する。

i が 3 になったら、ループを抜ける

main()
{
    int i;
    
    for(i=0; i<5; i++){
        printf("%d\n", i);
        if(i == 3)
            break;
    }
}

----- 出力結果 -----
0
1
2
3
--------------------

goto 文は、指定したラベルの位置に飛ぶ。
多重ループを一度に抜けたい場合などに使える。

main()
{
    int i,j;
    
    for(i=0; i<3; i++){
        for(j=0; j<3; j++){
            printf("%d %d\n", i, j);
            if(i == 1 && j == 1)
                goto stop;
        }
    }
stop:;
}

----- 出力結果 -----
0 0
0 1
0 2
1 0
1 1
--------------------

次の例は、i が奇数の場合は、処理を実行しない

main()
{
    int i;
    
    for(i=0; i<5; i++){
        if(i % 2){ // i が奇数の場合 TRUE
            continue;
        }
        printf("%d\n", i);
    }
}

----- 出力結果 -----
0
2
4
--------------------

		


条件分岐

条件分岐は、条件式が TRUE の場合に実行される。
if 文と switch 文がある。

if の条件式が TRUE の場合は、if の処理が実行され、
FALSE の場合は、else if の判定に移り、それが TRUE
の場合は、else if の処理が実行され、それも FALSE
の場合は else の処理が実行される。
else は、どれにも当てはまらなかった場合である。
else if は、いくつ書いても良い。
else if や else は、必要なければ書かなくても良い。

if, for, while は、処理が1行しかない場合は、{} を省略できる。

switch は条件式の結果が整数の場合にのみ使える。
switch が使える場合は使った方が分かりやすいコードになる。
default は、if 文の else に相当する。

main()
{
    int n;

    printf("このウィンドウをクリック後、何か数字を入力して、Enter キーを押してください\n");
    scanf("%d", &n);
    if(n > 5)
        printf("%d は 5 よりも大きい\n", n);
    else if(n < 5)
        printf("%d は 5 よりも小さい\n", n);
    else
        printf("%d は 5 である\n", n);
}


main()
{
    int n;

    printf("このウィンドウをクリック後、1, 2, 3 のいずれかの数字を入力して、
Enter キーを押してください\n");
    scanf("%d", &n);
    switch (n)
    {
    case 1:
        printf("入力された数字は 1");
        break;
    case 2:
        printf("入力された数字は 2");
        break;
    case 3:
        printf("入力された数字は 3");
        break;
    default:
        printf("1, 2, 3 以外の数字が入力されました");
    }
}

論理積(&&)は、左から真偽を判定し、偽と判定したら、その時点で条件式を抜け、次の条件式の判定は行わない。
そのため、以下の2つの例は、同じ意味である。

main()
{
    char s[] = {"abcdefg"};
    int  i = -1;
    if(i >= 0 && s[i] != '\0'){
        printf("%c", s[i]);
    }
}

main()
{
    char s[] = {"abcdefg"};
    int  i = -1;
    if(i >= 0){
        if(s[i] != '\0'){
            printf("%c", s[i]);
        }
    }
}
		


配列

配列は、変数の一種であり、複数の変数を一まとめにしたものである。
使用する変数の数が多い場合は、配列を使った方が良い場合がある。

    int a,b,c,d,e,f,g,h,i,j;
    a = 0; b = 1; c = 2; ・・・

とするよりも、

    int i;
    int k[10];
    for ( i=0; i<10; i++ )
        k[i] = i;

とした方が、コードが短くなり、コーディングの間違いも起こしにくい。
int k[10]; が配列の宣言であり、int 型変数 10 個分のサイズのメモリ領域が確保される。

配列の1つ1つの変数は、要素と呼び、k[0],k[1],k[2]...として操作できる。
宣言より下の[]内の数字は、添え字(そえじ)と呼び、0 から始まる整数である。
これは、配列の何番目の要素を操作するかを指定するのに使う。

    main()
    {
        int n[3];
        
        n[0] = 5;
        n[1] = 8;
        n[2] = 9;
        
        printf("%d %d %d", n[0], n[1], n[2]);
    }

    ----- 出力結果 -----
    5 8 9
    --------------------

文字の配列は特殊であり、文字列と呼ぶ。
int や float 型などの配列の初期化は、

    int   n[5] = {0, 1, 2, 3, 4};
    float f[5] = {0, 0.5f, 0.2f, 0.3f, 0.7f};

とするが、文字列だけは、

    char  s[5] = "abcd";

とすることもできる。文字列は、配列の最期に、ヌル文字(\0)を入れなくてはならないため、
配列サイズよりも1つ小さい数の文字しか入れられない。
ヌル文字は、SBCS や MBCS の場合は、1 バイトの 0 、Unicode の場合は、
2 バイトの 0 の事である。
ヌル文字は、初期化の場合は特に何もしなくても、配列サイズより1つ以上小さい数の
文字しか入れなかったら、勝手に入っている。
半角文字は 1 バイトだが、全角文字は 2 バイト必要であるため、
MBCS の場合は、文字列の必要バイト数は(文字数×2 + 1)、
Unicode の場合は(文字数×2 + 2)である。
通常、どんな統合開発環境でも、プログラマーが特に指定しない場合は、
MBCS が選択されている。

    char  s[11] = "あいうえお";

変数は、初期化を行わなかったら、0 で初期化されるが、
文字列の場合は、配列の最初の要素に変なデータが入っているので、

    char  s[5] = "";

とした方が良い場合もある。

文字列の出力は、%s を使う。文字列の1要素だけを出力するときは、%c である。

    main()
    {
        char  s[14] = "sample string";
        
        printf("%s\n", s);
        printf("%c", s[2]);
    }

    ----- 出力結果 -----
    sample string
    m
    --------------------

配列を初期化する場合は、配列サイズを省略可能である。

    int   n[] = {0, 1, 2, 3, 4};
    float f[] = {0, 0.5f, 0.2f, 0.3f, 0.7f};
    char  s[] = "abcd";

この場合、必要最小限の配列サイズが確保される。

この項目については、変数・配列・ポインタも参照
		


関数

関数は、何かの処理を行うために複数の関数を記述したものである。
基本的に、引数を渡すと、戻り値が返ってくるようになっている。
めじろ++ は、C でも C++ でも戻り値がない場合は、戻り値の型を省略できるが、VC++ の C++ は、その場合は必ず void をつけなくてはならない。
main 関数の戻り値は、void 以外に int もある。
main 関数の前の int が戻り値のデータ型で、return 文で、その型の値を返す。
以下のコードの、// はコメントアウトの記号で、その位置からのその行の記述は、プログラムには反映されない。主に、コードに注釈をつけたり、デバッグ時に一時的に処理を外すのに使う。 複数行のコメントアウトには、// の代わりに /**/ も使える。/* と */ で囲まれた記述もコメントアウトされる。
// C++ で戻り値なしの main 関数
void main()
{
    printf("hello");
}

// 戻り値が int 型 の main 関数
int main()
{
    printf("hello");
    return 0;
}
		
大きな関数は、複数の小さな関数に分割すると、コードが見やすくなる。
何度も使う処理も専用の関数を作ると使い回しが出来る。
main 関数から呼び出す自作関数の名前は、プログラマーが決める。
呼び出される関数は、呼び出し側の関数よりも前に記述しなくてはならない。
Get_Round は、1つの浮動小数点数を引数にとって、四捨五入した結果を戻り値とする自作関数である。引数は、f1, f2 としている。
戻り値のデータ型を int としたので、整数値を戻り値として返している。
main 関数では、その戻り値を変数 r1, r2 に格納し、Show_Text 関数で出力している。
Get_Round 関数内で 浮動小数点数 (f + 0.5f) に (int) としているが、これは、あるデータ型を強制的に別のデータ型に変換する手法で、キャストと呼ばれる。浮動小数点数を整数にキャストすると小数点以下が切り捨てられて整数型になる。
必ずしも、あらゆるデータ型間でキャストが出来るわけではない。
+ 0.5f の + は、算術演算子と呼ばれ、算数の + と同じ意味である。
int Get_Round(float f)
{
    int val = (int)(f + 0.5f);
    return val;
}

Show_Text(float f, int r)
{
    printf("%f を小数点以下で四捨五入すると %d\n", f, r);
}

main()
{
    float f1 = 12.3f;
    float f2 = 5.7f;
    int   r1,r2;

    r1 = Get_Round(f1);
    r2 = Get_Round(f2);

    Show_Text(f1, r1);
    Show_Text(f2, r2);
}

----- 出力結果 -----
12.300000 を小数点以下で四捨五入すると 12
5.700000 を小数点以下で四捨五入すると 6
--------------------
		
「呼び出される関数は、呼び出し側の関数よりも前に記述しなくてはならない」と書いたが、関数の宣言を前に行っていれば、順番は逆になっても良い。関数の宣言はプロトタイプと呼ぶ。

Show_Text(); // プロトタイプ

main()
{
    Show_Text();
}

Show_Text()
{
    printf("hello");
}