マスク計算
真偽だけのフラグを良く使うことがありますが、さらに種類を設けて1つの値で表現する時などに使います。
例えば、ログ出力用に値を設けて、更に、デバック用・警告用・エラー用・通知用など、場合によって指定したものだけ出したい時に、1つの値で表現できたら便利でしょう。別々にフラグを作るのも大変だし指定するのも至難の業です。
例:値debugに、intの各ビットをフラグとして利用し、各値を1つの値で指定する。
1bit目:デバック用
2bit目:警告用
3bit目:エラー用
4bit目:通知用
指定は各ビットのフラグを設定後の値を指定(分かりやすいので10進で指定しています)
#include <stdio.h> void testlog(unsigned int flg) { printf("flg(%d) =>",flg); if ( flg & 0x01 ) { printf("DEBUG,"); } if ( flg & 0x02 ) { printf("WARN,"); } if ( flg & 0x04 ) { printf("SYSTEM,"); } if ( flg & 0x08 ) { printf("INFO,"); } printf("\n"); } int main() { unsigned int debug = 0; for(debug=0; debug<16; debug++) { testlog(debug); } }
$ gcc main.c $ ./a.out flg(0) => flg(1) =>DEBUG, flg(2) =>WARN, flg(3) =>DEBUG,WARN, flg(4) =>SYSTEM, flg(5) =>DEBUG,SYSTEM, flg(6) =>WARN,SYSTEM, flg(7) =>DEBUG,WARN,SYSTEM, flg(8) =>INFO, flg(9) =>DEBUG,INFO, flg(10) =>WARN,INFO, flg(11) =>DEBUG,WARN,INFO, flg(12) =>SYSTEM,INFO, flg(13) =>DEBUG,SYSTEM,INFO, flg(14) =>WARN,SYSTEM,INFO, flg(15) =>DEBUG,WARN,SYSTEM,INFO,
ビット計算
パソコンの扱う値は突き詰めれば0と1(2進数)です。char型の値も1byteで8bitですから、8個のビットが並んでいます。
これらビットを計算で数式がなりたっています。予め2進数を覚えておきましょう。
00000000 (10進数で0です) 00000001(10進数で1です) 00000010(10進数で2です)
■論理積(AND)
どちらも1の場合1になる
00000101 00000011 ---------------------- 00000001
■論理和(OR)
どちらかが1の場合1になる
00000101 00000011 ---------------------- 00000111
■NOT論理積(NAND)
どちらも1の場合0になる
00000101 00000011 ---------------------- 11111110
■NOT論理和(NOR)
どちらかが1の場合0になる
00000101 00000011 ---------------------- 11111000
反転っす!
ifなので真(1以上)か偽(0)を判定する場合に、結果を入れ替えて解釈して欲しいことがあります。
これは単に反対の判定文すれば良いんですが、関数の結果で0を真として扱いたい、とか反対で判定すると、他の判定とことなり意味的に真なのか偽なのか統一感がなくなってバグを作り出す原因にもなります。あくまでもコーディングとしては変わりないのですが、、、値の前に[!]をつけることにより反転されます。
#include <stdio.h> int main() { int a; a =1; printf("%d\n",a); if ( a ) { printf("test %d\n",a); } printf("%d\n",!a); if ( !a ) { printf("test2 %d\n",!a); } }
結果
$ gcc main.c $ ./a.out 1 test 1 0
マクロ
プログラムを長く書き始めると、決まった値や簡単な書式を繰り返し書くのが面倒になったり、変えたい時に全部を変えなければいけなくて1つ忘れたら大変なことに。。。this is バグ. そんな時に定義(define)してしまいましょう。
使い方は同じですけど、一般的には値などは定数と呼び。計算式などマクロと呼んでます。※もしかしたら方言かもしれません。あしからず
#define 名前 値など #define 名前(a) いろいろ(a)
プリプロで名前が値に置き換わるだけですので、コンパイルエラーには出てきませんので要注意
#include <stdio.h> #define SHOHIZEI 8 #define KEISAN(a) (int)(a*1.08) int main() { printf("shohizei:%d\n", SHOHIZEI ); printf("keisan :%d\n", KEISAN(100) ); }
$ gcc main.c $ ./a.out shohizei:8 keisan :108
実用:並び替え(qsort)
そろそろプログラム的な事も混ぜてきしょう。っでソート。
良くある実用事例で、沢山有るデータを何らかの基準を設けて並べる(ソート関数)を使ってみましょう。もちろん自分でソート関数も作ることは出来ますが、ここままず、標準である二分岐ソート(qsort)を使ってみます。
void qsort(void *base, size_t nmemb, size_t size,int(*compar)(const void *, const void *));
さて、いきなり関数が第4引数にある例ですが、この関数は自分で基準を指定する所なので、構造体の一部を指定したりして色々な昇順降順を決定することが出来ます。
#include <stdio.h> #include <stdlib.h> // qsortで利用します。 typedef struct _item { int no; char name[10]; } ITEM; int comp(const void *a, const void *b) { // 戻りは、マイナスか0かプラスで、aとbの前後が決まります(0は未確定だけど) // プラス:aが先 // マイナス:bが先 // 0:同じ return( ((ITEM*)a)->no - ((ITEM*)b)->no ); } int comp2(const void *a, const void *b) { return( ((ITEM*)b)->no - ((ITEM*)a)->no ); } int comp3(const void *a, const void *b) { int w = ((ITEM*)a)->no - ((ITEM*)b)->no ; // aとbが同じならnameの文字列比較 return( (w!=0 ? w : strcmp(((ITEM*)a)->name,((ITEM*)b)->name)) ) ; } int main() { ITEM item[10] ={ {1,"N1"},{9,"N9"},{3,"N3-2"},{2,"N2"},{3,"N3-1"},{8,"N8"},{5,"N5"},{4,"N4"},{5,"N5"},{6,"N6"} }; int i; printf("before\n"); for(i=0; i< 10; i++) { printf("before(%d): no(%d) name[%s]\n",i,item[i].no,item[i].name); } qsort(item, 10, sizeof(ITEM), comp ); printf("after-1\n"); for(i=0; i< 10; i++) { printf("before(%d): no(%d) name[%s]\n",i,item[i].no,item[i].name); } qsort(item, 10, sizeof(ITEM), comp2 ); printf("after-2\n"); for(i=0; i< 10; i++) { printf("before(%d): no(%d) name[%s]\n",i,item[i].no,item[i].name); } qsort(item, 10, sizeof(ITEM), comp3 ); printf("after-3\n"); for(i=0; i< 10; i++) { printf("before(%d): no(%d) name[%s]\n",i,item[i].no,item[i].name); } }
■実行結果
before:sort前
after-1:noの昇順
after-2:noの降順
after-3:noの昇順だけど同じだったらnameの比較strcmp
$ gcc main.c $ ./a.out before before(0): no(1) name[N1] before(1): no(9) name[N9] before(2): no(3) name[N3-2] before(3): no(2) name[N2] before(4): no(3) name[N3-1] before(5): no(8) name[N8] before(6): no(5) name[N5] before(7): no(4) name[N4] before(8): no(5) name[N5] before(9): no(6) name[N6] after-1 before(0): no(1) name[N1] before(1): no(2) name[N2] before(2): no(3) name[N3-2] before(3): no(3) name[N3-1] before(4): no(4) name[N4] before(5): no(5) name[N5] before(6): no(5) name[N5] before(7): no(6) name[N6] before(8): no(8) name[N8] before(9): no(9) name[N9] after-2 before(0): no(9) name[N9] before(1): no(8) name[N8] before(2): no(6) name[N6] before(3): no(5) name[N5] before(4): no(5) name[N5] before(5): no(4) name[N4] before(6): no(3) name[N3-2] before(7): no(3) name[N3-1] before(8): no(2) name[N2] before(9): no(1) name[N1] after-3 before(0): no(1) name[N1] before(1): no(2) name[N2] before(2): no(3) name[N3-1] before(3): no(3) name[N3-2] before(4): no(4) name[N4] before(5): no(5) name[N5] before(6): no(5) name[N5] before(7): no(6) name[N6] before(8): no(8) name[N8] before(9): no(9) name[N9]
型作っちゃうぞ
構造体や列挙体などいちいち宣言するのが長いものや、既存の型を、ちょっと分かりやすく名称をしたい場合に利用します。
作る場合の宣言として[typedef]を利用します。ちなみに、本当に新しい型を作れるものでないので要注意。てかC言語は未知の型は作れませんです。既存の型と同じなのでCASTしなくても大丈夫です。
例 integer と言うint型の型を作成する。
typedef int integer; int main() { int a; integer b; a=10; b=a; printf("[%d][%d]\n",a,b); }
$ gcc main.c $ ./a.out [10][10]