CodingFirst

C言語、Perl、JavaScript、最近はPythonも。出来上がったものより、プログラムを書くことが好き。あと、スイーツ。

スポンサーサイト

  • このエントリーをはてなブックマークに追加
  • web拍手 by FC2
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

C言語だって成長する! 標準がC11(C1X)にアップデート

  • このエントリーをはてなブックマークに追加
  • web拍手 by FC2

C言語の標準がC11(C1X)にアップデートされた。
Unicode、ジェネリックマクロ、マルチスレッド、無名構造体/共用体などなど。

もともとC11については下の記事で知った。
ISO Updates C Standard - Slashdot

早速、規格を見てみようと思ったのだけど、
正式なものはちょっとお高いので、元にしたドラフト版を確認する事にした。
www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf

...と、見てみたら600頁以上あってしんどいので挫折。
代わりに wikipediaにアップデート内容の記事を見てみた。
C1X - Wikipedia, the free encyclopedia

Alignment specification
C言語だとコンパイル先の環境の実メモリに合わせて、
コンパイラのプラグマなどでローカルに指定してたのが、
C言語としてサポートしたという事だろう。

The _Noreturn function specifier(10/28修正)
戻らない関数の宣言。
下に quick_exit()もあるし、よっぽど終了失敗する事が多いのだろう。
N1570にあったサンプルコードを下につける。

_Noreturn void f () {
abort(); // ok
}
_Noreturn void g (int i) { // causes undefined behavior if i <= 0
if (i > 0) abort();
}

Type-generic expressions using the _Generic keyword(10/28修正)
名前からピンとこなかったけど、汎用マクロかな。
Cのマクロで、C++の関数オーバロードのようなことをする。
コードで見たほうが早いか。

#define cbrt(X) _Generic((X), long double: cbrtl, \
                              default: cbrt, \
                              float: cbrtf)(X)

Multithreading support
マルチスレッド関係。Mutex、condition variable、threadとからしい。

Improved Unicode support
unicodeサポート。
char16_t/char32_tにUTF-16/UTF-32を入れる。
文字列は u"abc" のように書く。

Removal of the gets function
gets関数の廃止。
代用には gets_s がいいらしいし、使えなければ fgets で。
よっぽど悪用されやすかったのだろう。

Bounds-checking interfaces (Annex K)
境界チェック。
gets_s のように _s を付けた関数が増えてる。
Visual-C++のManaged-C と思えばいいのかな?

Analyzability features (Annex L).
デバッガ関係かな?

More macros for querying the characteristics of floating point types
浮動少数に関するマクロの追加。
仮数や指数がわかるようになったらしが、C99に無かったっけ?

Anonymous structures and unions
無名構造体、共用体。
ネストする時に便利。

struct T { int 
  tag;
  union {
    float x;
    int n;
  };
};.

Static assertions
コンパイル時にプリプロセッサで assert する。
boostのBOOST_STATIC_ASSERTに相当するっぽい。

An exclusive create-and-open mode ("…x") for fopen
排他的 fopen。
ロックファイルとして使うといいみたい。
POSIXの O_CREAT|O_EXCL 相当らしい。

The quick_exit function as a third way to terminate a program
exitに失敗するようなケースで終了させる方法。
そんなことあるのか。。

Macros for the construction of complex values
複素数関係のマクロ。


プログラミング言語C 第2版 ANSI規格準拠プログラミング言語C 第2版 ANSI規格準拠
(1989/06/15)
B.W. カーニハン、D.M. リッチー 他

商品詳細を見る

Boost C++ LibrariesプログラミングBoost C++ Librariesプログラミング
(2007/07)
稲葉 一浩

商品詳細を見る

実例で学ぶGCCの本格的活用法―高機能コンパイラのオプション・コマンドを一つ一つていねいに解説 (TECHI―Embedded Software)実例で学ぶGCCの本格的活用法―高機能コンパイラのオプション・コマンドを一つ一つていねいに解説 (TECHI―Embedded Software)
(2006/07)
岸 哲夫

商品詳細を見る

[C]選択ソートをする

  • このエントリーをはてなブックマークに追加
  • web拍手 by FC2

C言語で選択ソート。

基本だけどたまには書いて筋トレ。

バブルソートの方が良い気がしてたけど、Wikipediaによれば
バブルソートと比較すると、「比較回数」は同じだが「交換回数」が少ないので、選択ソートの方が高速である。
らしい。そ、そんな気もしなくはない。。

書いた選択ソートを gist において、以下に貼りつけた。

実行例。
100個のランダム値をソートした際の比較、交換回数で、 前回のバブルソートなどの結果も合わせてみてみる。

$ for i in *.c ; do gcc $i && ./a.out ; done
         bblsort: cmp=4950.0, swp=2472.6, xor=197DFD2F
        bblsort2: cmp=4768.0, swp=2472.6, xor=197DFD2F
           qsort: cmp= 541.8, swp=??????, xor=197DFD2F
         selsort: cmp=4950.0, swp=  94.8, xor=197DFD2F

確かに交換回数がすごく少ないので、バブルソートより効率良さそうだ。
ただし、ランダムなデータであればだと思う。

アルゴリズム辞典によれば単純選択法とも呼ぶらしいんだけど、
確かに単純で分かりやすいと思う。

ソート時間のばらつきを無くし、バグのはいり込む余地のない
選択ソートを採用しました...という事例がありそう。



C言語による最新アルゴリズム事典 (ソフトウェアテクノロジー)C言語による最新アルゴリズム事典 (ソフトウェアテクノロジー)
(1991/03)
奥村 晴彦

商品詳細を見る

[C]バブルソートをする

  • このエントリーをはてなブックマークに追加
  • web拍手 by FC2

C言語でバブルソート。
超基本だけど、たまには書いてみないと。筋トレのようなものかな。

バブルソートを書いて gistにあげた。
ランダムな100個の数値をバブルソートし、比較回数を出力した。

以下にgistと実行結果

$ gcc bblsort-eg.c && ./a.out
calls: 4950.000, xor: 197DFD2F

qsortを使った例。

$ gcc qsort-eg.c && ./a.out
calls: 541.826, xor: 197DFD2F

プログラム自体は難しくないので結果に注目してみる。
バブルソートが 4950 回、
クイックソートが 541 回とでた。
クイックソートの平均は n log n = 460 なので誤差があるけど、10倍の開き。

ちょっと改良した bblsort_eg2.c を作ってみたけど大差なし。

$ gcc bblsort-eg2.c && ./a.out
calls: 4767.998, xor: 197DFD2F

と、散々な結果がでてしまうけれど、
バブルソートを否定してるわけではない。

バブルソートは、
プログラムが単純、
ソートにメモリが不要、
ある程度ソートされてれば遅くもない。

大抵、ソートするデータって規則性あるし、
ソートを途中で中断しやすいのが地味に便利だったりもする。
なので、バブルソートはよくお世話になる。


C言語による最新アルゴリズム事典 (ソフトウェアテクノロジー)C言語による最新アルゴリズム事典 (ソフトウェアテクノロジー)
(1991/03)
奥村 晴彦

商品詳細を見る

GCCでC言語の分岐カバレッジを試す。思ったのとちょっと違った。

  • このエントリーをはてなブックマークに追加
  • web拍手 by FC2

gcov(gcc)のカバレッジにある "Branchs executed" が
どう計測されるのかを試した。

まず、"Branches executed"について。
分岐網羅の事で、条件文の各条件をどれだけ実行したかで、C1のこと。
分岐網羅の解釈に揺れがあるようで、「条件をただ実行」とか「条件の分岐方向それぞれを実行」などがあるが、
gcovは「条件をただ実行」の意で計測していた。

C言語でソースを作成し、以下のように実行した。

$ gcc -g --coverage gcov-test-branch.c && ./a.out && gcov -bc gcov-test-branch.c && cat gcov-test-branch.c.gcov
P
P
P
P
P
File 'gcov-test-branch.c'
Lines executed:90.48% of 42
Branches executed:100.00% of 10
Taken at least once:60.00% of 10
Calls executed:81.25% of 16
gcov-test-branch.c: 'gcov-test-branch.c.gcov' を作成しています

作成された gcovファイルを見る。冒頭と、main関数付近。

        -:    0:Source:gcov-test-branch.c
        -:    0:Graph:gcov-test-branch.gcno
        -:    0:Data:gcov-test-branch.gcda
        -:    0:Runs:1
        -:    0:Programs:1
        -:    1:#include <stdio.h>
        -:    2:

function main called 1 returned 100% blocks executed 100%
        1:   51:int main(int c,char*v[]){
        1:   52:  branch1_ok(0);
call    0 returned 1
        1:   53:  branch1_ok(1);
call    0 returned 1
        1:   54:  branch2_not(0);
call    0 returned 1
        1:   55:  branch3_not(0);
call    0 returned 1
        1:   56:  branch4_ok(0);
call    0 returned 1
        1:   57:  branch5_ok(0);
call    0 returned 1
        1:   58:  branch6_ok(0);
call    0 returned 1
        1:   59:  branch7_ok(0);
call    0 returned 1
        1:   60:  branch8_ok(0);
call    0 returned 1
        1:   61:  return 0;
        -:   62:}

branch1_ok()から見ていく

function branch1_ok called 2 returned 100% blocks executed 100%
        2:    3:void branch1_ok(int i){
        2:    4:  if(i==0||i>=0){
branch  0 taken 1 (fallthrough)
branch  1 taken 1
branch  2 taken 1 (fallthrough)
branch  3 taken 0
        2:    5:    puts("P");
call    0 returned 2
        -:    6:  }
        2:    7:}

branch の後ろに "never executed" がなく、パス(全網羅)と判定されている。
条件(分岐)は i==0 と、i>=0 で、main関数から i=0, i=1 でコールしてるので、
この判定は true にしかならない。
したがって、条件がtrue/falseによらず実行されていればパス(全網羅)扱いになると言える

function branch2_not called 1 returned 100% blocks executed 100%
        1:    8:void branch2_not(int i){
        -:    9:  if(0){
        -:   10:    puts("P");
        -:   11:  }
        1:   12:}

if(0)は分岐とすらみなさない。

function branch3_not called 1 returned 100% blocks executed 100%
        1:   13:void branch3_not(int i){
        -:   14:  int j;
        1:   15:  j=(i==0)?0:1;
        1:   16:}

3項演算子も同様に、分岐とみなさない。

function branch4_ok called 1 returned 100% blocks executed 67%
        1:   17:void branch4_ok(int i){
        1:   18:  switch(i){
branch  0 taken 1
branch  1 taken 0
        -:   19:    case 0:
        1:   20:      puts("P");
call    0 returned 1
        1:   21:      break;
        -:   22:    default:
    #####:   23:      puts("N");
call    0 never executed
    #####:   24:      break;
        -:   25:  }
        1:   26:}

switchは分岐とみなす。
ただし、if文同様にtrue/falseは関係しない。

function branch5_ok called 1 returned 100% blocks executed 60%
        1:   27:void branch5_ok(int i){
        -:   28:  int j;
        1:   29:  for(j=0;j<i;j++){
branch  0 taken 0
branch  1 taken 1 (fallthrough)
    #####:   30:    puts("N");
call    0 never executed
        -:   31:  }
        1:   32:}

forも同様。分岐にはなる

function branch6_ok called 1 returned 100% blocks executed 75%
        1:   33:void branch6_ok(int i){
        2:   34:  while(i>0){
branch  0 taken 0
branch  1 taken 1 (fallthrough)
    #####:   35:    puts("N");
call    0 never executed
        -:   36:  }
        1:   37:}
function branch7_ok called 1 returned 100% blocks executed 100%

whileも同様。分岐にはなる

        1:   38:void branch7_ok(int i){
        -:   39:  do{
        1:   40:    puts("P");
call    0 returned 1
        1:   41:    break;
        -:   42:  }while(i==0);
        1:   43:}

途中で break した場合、if(0)と同様に whileの行は分岐とみなされない

        -:   44:#define b8(i) if(i==0||i==1){\
        -:   45:  puts("P");\
        -:   46:  }
function branch8_ok called 1 returned 100% blocks executed 100%
        1:   47:void branch8_ok(int i){
        1:   48:  b8(0);
call    0 returned 1
        1:   49:}
        -:   50:

マクロにすると分岐とみなさなくなる。これは意外。

gcovのブランチカバレッジは単純に条件を実行したかだった。
意味はわかりやすかったけど、コードカバレッジとしては使いにくいと感じる。



実例で学ぶGCCの本格的活用法―高機能コンパイラのオプション・コマンドを一つ一つていねいに解説 (TECHI―Embedded Software)実例で学ぶGCCの本格的活用法―高機能コンパイラのオプション・コマンドを一つ一つていねいに解説 (TECHI―Embedded Software)
(2006/07)
岸 哲夫

商品詳細を見る


テスト駆動開発入門テスト駆動開発入門
(2003/09)
ケント ベック

商品詳細を見る

もともとのソースコードは続きに

続きを読む »


GCCでC言語のラインカバレッジ。ちょっと怖いぞ。

  • このエントリーをはてなブックマークに追加
  • web拍手 by FC2

gcc(gcov)のラインカバレッジを試した(C言語)。

そもそも、ラインカバレッジに関しては、
「カバレッジとしてはちょっと弱い」程度の知識しかなかった。
だから、どう弱いのか実感したかった。
ただし、やって見た結果は、たしかにラインのカバレッジだ!...ではあったけどね...

まず、おさらい。カバレッジの測り方。
作成したソース gcov-test-line.c をコンパイル、実行して、gcovで集計する。

$ gcc -g --coverage gcov-test-line.c && ./a.out && gcov gcov-test-line.c
P
P
P
P
P
File 'gcov-test-line.c'
Lines executed:93.75% of 64
gcov-test-line.c: 'gcov-test-line.c.gcov' を作成しています

できた gcov-test-line.c.gcov を見る。

        -:    0:Source:gcov-test-line.c
        -:    0:Graph:gcov-test-line.gcno
        -:    0:Data:gcov-test-line.gcda
        -:    0:Runs:1
        -:    0:Programs:1
        -:    1:#include <stdio.h>
        -:    2:

        1:   74:int main(int c,char*v[]){
        1:   75:  line1_ok(0);
        1:   76:  line2_ok(0);
        1:   77:  line3_ng(0);
        1:   78:  line4_ok(0);
        1:   79:  line5_ok(0);
        1:   80:  line6_ok(0);
        1:   81:  line7_ng(0);
        1:   82:  line8_ok(0);
        1:   83:  line9_ng(0);
        1:   84:  line10_ok(0);
        1:   85:  line11_ok(0);
        1:   86:  line12_ok(0);
        1:   87:  line13_ok(0);
        1:   88:  return 0;
        -:   89:}

冒頭とmain関数付近。特になし。

        1:    7:void line1_ok(int i){
        1:    8:  if(i==0||i==1){
        1:    9:    puts("P");
        -:   10:  }
        1:   11:}

複数の条件があり、実行されない条件(i==1)がある場合。
これはラインカバレッジとしては、OKと判定される。
やはり、文字通り、行をちょっとでも実行したらOKのようだ。

        1:   12:void line2_ok(int i){
        1:   13:  if(i==2){puts("N");}
        1:   14:}
        1:   15:void line3_ng(int i){
        1:   16:  if(i==2){
    #####:   17:    puts("N");
        -:   18:  }
        1:   19:}

if文の条件不成立の場合。
13行目のように 1行でまとめてしまえば OK。
複数行にすると実行されない扱いになる。

        1:   20:void line4_ok(int i){
        -:   21:  if(0){
        -:   22:    puts("N");
        -:   23:  }
        1:   24:}

意外にも if(0)の場合はラインカバレッジは OK。
特に最適化をかけたつもりはないので、明らかに実行されないパスは
OKという事なのだろう。

        1:   25:void line5_ok(int i){
        -:   26:  int j;
        1:   27:  j=(i==0)?0:1;
        1:   28:}

3項演算子による代入。
(i==0)のフォルスパスが実行されないが、ラインカバレッジ的には OK。
だんだんノリがわかってきた。

        1:   29:void line6_ok(int i){
        1:   30:  switch(i){
        1:   31:    case 0:puts("P");break; default:puts("N");break;
        -:   32:  }
        1:   33:}
        1:   34:void line7_ng(int i){
        1:   35:  switch(i){
        -:   36:    case 0:
        1:   37:      puts("P");
        1:   38:      break;
        -:   39:    default:
    #####:   40:      puts("N");
    #####:   41:      break;
        -:   42:  }
        1:   43:}

switch - caseで実行されないパスがある場合。
1行にまとめてしまえば OK。

        1:   44:void line8_ok(int i){
        -:   45:  int j;
        1:   46:  for(j=0;j<i;j++){puts("N");}
        1:   47:}
        1:   48:void line9_ng(int i){
        -:   49:  int j;
        1:   50:  for(j=0;j<i;j++){
    #####:   51:    puts("N");
        -:   52:  }
        1:   53:}
        1:   54:void line10_ok(int i){
        1:   55:  while(i>0){puts("N");}
        1:   56:}

forもwhilieも同じ。1行ならOK

        1:   57:void line11_ok(int i){
        1:   58:  while(i>=0){
        1:   59:    puts("P");
        1:   60:    break;
        -:   61:    puts("N");
        -:   62:  };
        1:   63:}
        1:   64:void line12_ok(int i){
        -:   65:  do{
        1:   66:    puts("P");
        1:   67:    break;
        -:   68:    puts("N");
        -:   69:  }while(++i>=0);
        1:   70:}

whileやdo-whileの途中で強制breakした場合。
break以降の処理は通過しないが、ラインカバレッジは OK。

        -:    3:#define m1(i) if(i==3){\
        -:    4:  puts("N");\
        -:    5:  }
        -:    6:

        1:   71:void line13_ok(int i){
        1:   72:  m1(i);
        1:   73:}

複数行なマクロの場合。
連結してるから、ラインカバレッジは OK なのだろう。

以上から、
gcc(gcov)のラインカバレッジは確かにラインのカバレッジだった。
とにかく行をまとちゃえば OK と考えていいだろう。

実際、テストにたっぷり時間をかけられなかったり、
仕様変更が頻繁におきたりするときには
これくらいの粒度で丁度よいかなぁと思う。

だけど、これで慣れてしまうのは怖い。
なまじデータが計測できてるだけにこれ以上を目指さなくなるし、
カバレッジを上げる事が目的になって、かえってソースコードの品質が落ちてしまうような
そんな気がしてならない。


実例で学ぶGCCの本格的活用法―高機能コンパイラのオプション・コマンドを一つ一つていねいに解説 (TECHI―Embedded Software)実例で学ぶGCCの本格的活用法―高機能コンパイラのオプション・コマンドを一つ一つていねいに解説 (TECHI―Embedded Software)
(2006/07)
岸 哲夫

商品詳細を見る

ソフトウェアテスト入門 押さえておきたい<<要点・重点>>ソフトウェアテスト入門 押さえておきたい<<要点・重点>>
(2008/04/11)
ソフトウェア・テストPRESS編集部

商品詳細を見る

続きを読む »


«  | HOME |  »

Search

Recent Entries

Foot Print



Categories

Monthly

Recent Comments

Recent Trackbacks

Profile

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。