コアダンプの数だけ強くなれるよ

見習いエンジニアの備忘log

最低限あると嬉しいvimrc

大抵もっとごちゃごちゃ書いてあるけど、個人的に最低限あると嬉しいvimrcの設定だけをピックアップ。

$cat ~/.vimrc
syntax on

"色設定
colorscheme desert

"前回編集終了位置からの再開
augroup vimrcExtend
au BufReadPost * if line("'\"") > 1 && line("'\"") <= line("$") | exe "normal! g`\""
augroup END

"C/C++ファイルのみ拡張Tab設定
autocmd FileType c,cpp set expandtab

"インデント関連
set cindent
set cinoptions=:0,p0,t0
set cinwords=if,else,while,for,do,switch,case
set backspace=2
set tabstop=4
set shiftwidth=4

"文字コード
set encoding=utf-8
set fileencodings=utf-8,iso-2022-jp,cp932,sjis,euc-jp

"行表示
set nu

"検索時のハイライト
set hlsearch

"ショートカット
nmap <ESC><ESC> : nohlsearch <CR>
nmap <TAB> : set nu <CR>
nmap <TAB><TAB> : set nonu <CR>

ソースコードと完全に同じ文字列を出力する

実用性はゼロですが、お遊び感覚でCとperlでやってみた。

Perlの場合

quine.pl

$_=q{                   q[
     *  *       *  *
   *      *   *      *
  *         *         *
  *                   *
   *   Hello Quine   *
    *               *
      *           *
        *       *
          *   *
            *
]; print"\$_=q{$_};eval\n"};eval

実行結果

$ perl quine.pl
$_=q{                   q[
     *  *       *  *
   *      *   *      *
  *         *         *
  *                   *
   *   Hello Quine   *
    *               *
      *           *
        *       *
          *   *
            *
]; print"\$_=q{$_};eval\n"};eval

Cの場合

quine.c

#define quine(x) main(){return puts(#x"\nquine("#x")");}
quine(#define quine(x) main(){return puts(#x"\nquine("#x")");})


実行結果

$ gcc -o quine quine.c
$ ./quine
#define quine(x) main(){return puts(#x"\nquine("#x")");}
quine(#define quine(x) main(){return puts(#x"\nquine("#x")");})

C言語で簡易関数トレース

シンプルなprintf()デバッグコンパイルスイッチでON/OFFできるように実装する。

trace.c

#include <stdio.h>

#ifdef DEBUG_ENABLE
#define ENTRANCE(fmt, ...) \
    printf("%s:%u:===> IN  %s(" fmt ")\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__);
#else
#define ENTRANCE(fmt, ...)
#endif /* DEBUG_ENABLE */

#ifdef DEBUG_ENABLE
#define EXIT(fmt, ...) \
    printf("%s:%u:<=== OUT %s(" fmt ")\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__);
#else
#define  EXIT(fmt, ...)
#endif /* DEBUG_ENABLE */

#ifdef DEBUG_ENABLE
#define LOG_DEBUG(fmt, ...) \
    printf("%s:%u:%s():  " fmt "\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__);
#else
#define LOG_DEBUG(fmt, ...)
#endif /* DEBUG_ENABLE */

int function(int a)
{
    int ret = 0;
    ENTRANCE("a=%d", a);

    EXIT("ret=%d", ret);
    return ret;
}

int main(void)
{
    ENTRANCE();

    LOG_DEBUG("hello world");

    int a = 1;
    int b = 2;
    LOG_DEBUG("a + b = %d", a+b);

    function(a);

    EXIT();
    return 0;
}



実行例

$ gcc -DDEBUG_ENABLE trace.c
$
$ ./a.out
trace.c:35:===> IN  main()
trace.c:37:main():  hello world
trace.c:41:main():  a + b = 3
trace.c:27:===> IN  function(a=1)
trace.c:29:<=== OUT function(ret=0)
trace.c:45:<=== OUT main()
$




あくまで簡易的なのでソースコードの規模が大きくなるほど見にくくなる予感。

trace.c:35:===> IN  main()
trace.c:27:  ===> IN  function(a=1)
trace.c:29:  <=== OUT function(ret=0)
trace.c:45:<=== OUT main()

こんな感じで、関数のIN/OUTでスタックが深くなるにしたがって表示が右にシフトしていくような機能が欲しい。
後は、タイムスタンプとsyslogのようにログレベルを指定してログの出力量を調整できる機能を追加したい。

コードの処理速度計測にgprofを試してみた

GNUのプロファイラ gprofでコードの処理速度計測を試してみた。
関数単位、LINE(行)単位での実行速度の計測ができる模様。

今回は関数単位でお試し。

使い方は"-pg"のオプションを付与してコンパイルしたバイナリを実行後にgprofコマンドをかませばOK。

gproftest.c

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

#define LOOP_BASE_COUNT (100*1000)

void function1(void)
{
    char buf1[1024] = {0};
    char buf2[1024] = {0};

    /* メモリ操作で負荷をかける */
    for(int i = 0; i < LOOP_BASE_COUNT; i++) {
        memset(buf1, 0xff, sizeof(buf1));
        memcpy(buf2, buf1, sizeof(buf2));
    }
}

void function2(void)
{
    char buf1[1024] = {0};
    char buf2[1024] = {0};

    for(int i = 0; i < LOOP_BASE_COUNT*10; i++) {
        memset(buf1, 0xff, sizeof(buf1));
        memcpy(buf2, buf1, sizeof(buf2));
    }
}

void function3(void)
{
    char buf1[1024] = {0};
    char buf2[1024] = {0};

    for(int i = 0; i < LOOP_BASE_COUNT*1000; i++) {
        memset(buf1, 0xff, sizeof(buf1));
        memcpy(buf2, buf1, sizeof(buf2));
    }
}

int main(void)
{
    function1();
    function2();
    function3();

    return 0;
}


Makefile

$ cat Makefile
gproftest: gproftest.c
        gcc -O2 -pg -std=gnu99 -o gproftest gproftest.c


実行例

$ make
gcc -O2 -pg -std=gnu99 -o gproftest gproftest.c
$
$ time ./gproftest

real    0m10.355s
user    0m10.341s
sys     0m0.004s
$
$
$ gprof gproftest gmon.out
Flat profile:

Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total
 time   seconds   seconds    calls   s/call   s/call  name
 99.88     10.31    10.31        1    10.31    10.31  function3
  0.98     10.41     0.10        1     0.10     0.10  function2
  0.00     10.41     0.00        1     0.00     0.00  function1
...(後略)... 


[その他]
どうやらコンパイル時に -O3 で最適化すると上手くいかないぽい。

linuxでkbhit()

LinuxWindowsのkbhit()相当を実装する。

kbhit.h

#ifndef _KBHIT_H_
#define _KBHIT_H_

#include <stdbool.h>
#include <termios.h>
#include <unistd.h>

extern void KB_open(void);
extern void KB_close(void);
extern bool kbhit(void);
extern char linux_getch(void);


#endif /* _KBHIT_H_ */


kbhit.c

#include "kbhit.h"

static struct termios Old_set;
static struct termios New_set;
static int ReadChar = -1;

void KB_open()
{
    tcgetattr(0,&Old_set);
    New_set = Old_set;
    New_set.c_lflag &= ~ICANON;
    New_set.c_lflag &= ~ECHO;
    New_set.c_lflag &= ~ISIG;
    New_set.c_cc[VMIN] = 0;
    New_set.c_cc[VTIME] = 0;
    tcsetattr(0,TCSANOW,&Old_set);
}

void KB_close()
{
    tcsetattr(0,TCSANOW, &Old_set);
}

bool kbhit()
{
    char ch;
    int nread;

    if(ReadChar !=-1) {
        return true;
    }

    New_set.c_cc[VMIN]=0;
    tcsetattr(0,TCSANOW,&New_set);
    nread=read(0,&ch,1);
    New_set.c_cc[VMIN]=1;
    tcsetattr(0,TCSANOW,&New_set);

    if(nread == 1) {
        ReadChar = ch;
        return true;
    }

    return false;
}

char linux_getch()
{
    char ch;

    if(ReadChar != -1) {
       ch = ReadChar;
       ReadChar = -1;
       return (ch);
    }

    read(0,&ch,1);
    return(ch);
}

main.c

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

int main(void)
{
    KB_open();

    while(1) {
        if (kbhit()) {

            char ch = linux_getch();

            printf("kbhit: %c\n", ch);

            if ('q' == ch) {
                break;
            }
        }

        usleep(10000);
    }

    KB_close();
}


Makefile

kbloop: kbhit.o main.o
        gcc -o kbloop kbhit.o main.o

kbhit.o: kbhit.h kbhit.c
        gcc -c kbhit.c

main.o: main.c
        gcc -c main.c

clean:
        rm -f *.o


実行例

$ make
gcc -c kbhit.c
gcc -c main.c
gcc -o kbloop kbhit.o main.o
$
$ ./kbloop
kbhit: a
kbhit: b
kbhit: c
kbhit: d
kbhit: e
kbhit: f
kbhit: g
kbhit: q
$
$

HTML,JavaScriptで格子を描画

HTML5Canvas機能でオセロの盤面ぽい格子を描く。

canvas.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
</head>

<body>
  <!-- キャンバスの設定 -->
  <canvas id="canv" width="500" height="500"></canvas>
  <script>
    var ctx = document.getElementById("canv").getContext('2d');
    
    // 描画内容を指定する
    // 背景を500×500の緑で描画
    ctx.fillStyle = "rgb(0,128,0)";
    ctx.fillRect(0, 0, 500, 500);
    
    // 格子の設定
    ctx.strokeStyle = "white"
    ctx.lineWidth = 2;
    ctx.beginPath();
    
    // 縦線
    for (var v = 50; v < 500; v+=50) {
      ctx.moveTo(v, 0);
      ctx.lineTo(v, 500);
    }

    // 横線
    for (var h = 50; h < 500; h+=50) {
      ctx.moveTo(0, h);
      ctx.lineTo(500, h);
    }
    
    // 描画内容を実行する
    ctx.stroke();
  </script>

</body>

</html>


実行例
f:id:segmentation-fault:20170208142347p:plain

C言語でbuffer overrun

お呼びでない関数を実行する。

overrun.c

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

void goast(void)
{
    printf("Boo!\n");
}

void dummy(void)
{
    char a[1] = {0};

    a[9] = &goast;
}

int main(void)
{
    dummy();
    return 0;
}


実行例

$ gcc overrun.c
overrun.c: In function 'dummy':
overrun.c:13: warning: assignment makes integer from pointer without a cast
$ ./a.out
Boo!
Segmentation fault