関数ポインタのはなし

関数ポインタとは

関数をポインタ変数に入れて使えるよっていうやつです
これです

int func(void)
{
  return 0;
}

int main(int argc, char *argv[])
{
  int (*p)() = func;
  return (*p)();
}

なんでポインタに関数名を入れられるのか分かりませんでした

ですが,その理由はアセンブラにありました

00000000004004cd <func>:
  4004cd:       55                      push   %rbp
  4004ce:       48 89 e5                mov    %rsp,%rbp
  4004d1:       b8 00 00 00 00          mov    $0x0,%eax
  4004d6:       5d                      pop    %rbp
  4004d7:       c3                      retq

00000000004004d8 <main>:
  4004d8:       55                      push   %rbp
  4004d9:       48 89 e5                mov    %rsp,%rbp
  4004dc:       48 83 ec 20             sub    $0x20,%rsp
  4004e0:       89 7d ec                mov    %edi,-0x14(%rbp)
  4004e3:       48 89 75 e0             mov    %rsi,-0x20(%rbp)
  4004e7:       48 c7 45 f8 cd 04 40    movq   $0x4004cd,-0x8(%rbp)
  4004ee:       00
  4004ef:       48 8b 55 f8             mov    -0x8(%rbp),%rdx
  4004f3:       b8 00 00 00 00          mov    $0x0,%eax
  4004f8:       ff d2                   callq  *%rdx
  4004fa:       c9                      leaveq
  4004fb:       c3                      retq
  4004fc:       0f 1f 40 00             nopl   0x0(%rax)

00000000004004cd <func>:とか00000000004004d8 <main>:とそのまんま関数名が書いてあります

これはラベルで,

任意の名前とメモリアドレスを対応付けるためのもの*1

だそうです

上記の例だと,アドレス4004cdをfuncという名前にしています
アドレス4004cdは関数の処理が始まる先頭のアドレスになっていますね

つまり,関数名はメモリアドレスということなので,ポインタに関数名を入れられるのは納得です

何が嬉しいのか

第一級関数のように扱えることです
cの関数は第一級関数ではないです
第一級関数の説明は

marycore.jp

が分かりやすかったです

関数ポインタで高階関数が作れるので,試しにmapを作ってみました

#include <stdio.h>
#include <math.h>

float *map(float *a, float (*f)(float, float))
{
  static float ret[3];
  for (int i = 0; i < 3; i++) {
    ret[i] = (*f)(a[i], 2.0);
  }
  return ret;
}

int main(int argc, char *argv[])
{
  float array[] = {1.0, 2.0, 3.0};
  float *result;


  result = map(array, powf);


  for (int i = 0; i < 3; i++) {
    printf("%f, ", array[i]);
  }
  printf("\n");
  for (int i = 0; i < 3; i++) {
    printf("%f, ", result[i]);
  }
  printf("\n");

  return 0;
}
$ gcc -std=c99 -lm mapper.c -o mapper
$ ./mapper
1.000000, 2.000000, 3.000000,
1.000000, 4.000000, 9.000000,

配列の要素数をmapが持っていなきゃいけないのが気に入らないです

まとめ

  • 関数名はアドレスなのでポインタに入れられる
  • 関数ポインタで高階関数が作れる

*1:Wikipedia アセンブリ言語#ラベル(2018/11/8 最終アクセス)

Cからアセンブラをgccで作る方法,あとついでにclangでも作ってみる

gcc

ソースを作ります

hoge.c

int main(int argc, char **argv) {
  int a = 1;
  return 0;
}

-Sオプションを付ければ生成できます

$ gcc -S hoge.c

hoge.s

        .file   "hoge.c"
        .text
        .globl  main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        movl    %edi, -20(%rbp)
        movq    %rsi, -32(%rbp)
        movl    $1, -4(%rbp)
        movl    $0, %eax
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:
        .size   main, .-main
        .ident  "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-28)"
        .section        .note.GNU-stack,"",@progbits

簡単ですね
アセンブラを読むだけなら -O0 をつけて最適化を無効にしておくのも良いかもです

clang

gccが使えればいいかなと思っていたのでclangは完全に食わず嫌い状態です この際なので触れておいた方がいいでしょう,ということでclangでのやり方です

-save-temps を付ければ作れますね

$ clang -save-temps hoge.c

実行ファイルを作るついでに一時ファイルは保存しておいてねって意味なので,*.s以外のファイルも出てきます ちなみに最適化無効はgccと同じ -O0 です

hoge.s

        .file   "hoge.i"
        .text
        .globl  main
        .align  16, 0x90
        .type   main,@function
main:                                   # @main
        .cfi_startproc
# BB#0:
        pushq   %rbp
.Ltmp2:
        .cfi_def_cfa_offset 16
.Ltmp3:
        .cfi_offset %rbp, -16
        movq    %rsp, %rbp
.Ltmp4:
        .cfi_def_cfa_register %rbp
        movl    $0, %eax
        movl    $0, -4(%rbp)
        movl    %edi, -8(%rbp)
        movq    %rsi, -16(%rbp)
        movl    $1, -20(%rbp)
        popq    %rbp
        ret
.Ltmp5:
        .size   main, .Ltmp5-main
        .cfi_endproc


        .ident  "clang version 3.4.2 (tags/RELEASE_34/dot2-final)"
        .section        ".note.GNU-stack","",@progbits

やり直しのC言語 現状と目標設定フェーズ

モチベーション

  • 仕事でアプリケーション系の言語(JS/Java/Objective-C)を中心にやったので低レイヤを触ってみたくなった
  • 上っ面の部分だけ理解していたので少し深掘りしようと思った

現在のスキルレベル

  • 10年以上の付き合い
    • 継続して勉強はしていない(最後に触ったのは5年ぐらい前)
    • ギターを持っているけど練習していなくてギター歴が長くなってしまったっていうのと似ている
  • ポインタ,構造体辺りまでは理解している
  • 関数ポインタとかになると厳しい
  • linuxカーネルemacscoreutilsなどのソースを読もうとしたが挫折した
  • autoconf,makefilegcc,ldコマンドのざっくりとしたものは理解しているつもり
  • Deep Lerningに似たニューラルネットワークを作ったことがある
  • 信号処理のプログラムを作ったことがある
  • ロボットを動かしたことがある

ゴール

  • C言語でコマンドを実装できる
  • coreutilsは難なく読める
  • 各ツールが何しているか理解している
  • アセンブラ言語がちょっとわかる

気をつけたい

  • 理由付けが弱いので挫折しないようにのんびりやる

シンセサイザー制作メモ 〜プロトタイプ編〜

基本的なシンセサイザーの中身はこんな感じだと思う

[oscillator] -> [filter] -> [amp/EG]

github.com

ということでコピペして書いた
設定できるパラメータは以下

Oscillator

周期波形を生成

項目名 値の範囲 デフォルト値 説明
Wave “sine”,“square”,“sawtooth”,“triangle” “sine” 波形の種類を指定する
Freq 50.0〜20000.0 440.0 波形の周波数,単位はHz

Filter

フィルタを生成

項目名 値の範囲 デフォルト値 説明
Type “allpass”,“LPF”,“HPF”,“BPF”,“LowShelf”,“HighShelf”,“Peaking”,“Notch” allpass フィルタの種類を指定する
Freq 50.0〜20000.0 50.0 カットオフ周波数,単位はHz
Q 0.0〜50.0 5.0 フィルターのQ

Amplitude

信号の増幅/減衰をさせる EGも設定できる

項目名 値の範囲 デフォルト値 説明
Level 0.0〜1.0 1.0 ゲイン,倍率を数値で指定(dBではない)
Attack 0.0〜1.0 0.0 設定したLevel値になるまでの時間
Decay 0.0〜1.0 1.0 LevelSustainになるまでの時間
Sustain 0.0〜1.0 0.5 Levelの減衰が収束する値
Release 0.0〜1.0 1.0 LevelSustainから0(最小値)になるまでの時間

次にやること

  • 鍵盤みたいなの作ろう(キーボードで音出すのでもいいけど)
  • AnalyserNode波形を可視化したい
    • FFTから着手する
  • ElectronかReact.jsを使おう

参考にさせていただきました

Web Audio API 解説 - 01.前説 | g200kg Music & Software

プログラマーの力量を見極める質問に答えてみた

matome.naver.jp

プログラミング一般のところだけ自分なりに答えをまとめてみる

「等値」と「等価」の違いを説明してください

言葉の意味だと
等値は値の等しいこと
英語ではequality

等価は価値や価格が等しいこと
英語ではequivalence

数学的な意味だと
等値は等式のことになるので A = B
等価はA ⇔ B,すなわち,AならばBとBならばAが同時に成り立つことをいう

ここまで説明すれば,プログラム的な意味は

A == Bが等値
!(A < B) && !(B < A)が等価
等価は++EE += 1が同じとか,!E0 == Eが同じであることを表すときにも使う

正確に等しいときは等値
正確には等しくないが等しいとみなすときは等価

「値渡し」と「参照渡し」の違いはなんですか?オブジェクト指向システムや手続き型システムにおいて,これらにはどのような違いが存在するのかを説明してください

変数の中身を変えるか変えないかの違い
値渡しは変数の中身を変えない
参照渡しは変数の中身を変える

C言語で書くと

#include <stdio.h>

int call_by_value(int a) {
    ++a;
}

int call_by_reference(int* a) {
    ++*a;
}

int main(void) {
    int a = 1;
    
    call_by_value(a);
    printf("a = %d¥n", a);
    call_by_reference(&a);
    printf("a = %d¥n", a);

    return 0;
}
a = 1
a = 2

値渡しは変数の中身を渡す
参照渡しは変数が格納されているメモリのアドレスを渡す

オブジェクト指向システムや手続き型システムにおいて,これらにはどのような違いが存在するのかを説明してください

これはざっくりと言うと
 オブジェクト指向 → 値渡し
 手続き型 → 値渡し,参照渡しどっちもできる
という感じかと

ポリモーフィズム」とは何か説明してください

日本語で多態性とか多様性とか
オブジェクト指向における「継承」の概念をなす機能の一つ
例えば,与えられた引数を文字列にして返す処理を考えると
手続き型の場合だと引数の型によって処理をそれぞれ別々に書かなければいけないし,使う場合も引数の型を意識しないといけない
オブジェクト指向だと汎用的な処理を書き,型ごとに処理を書くことで,使う場合に引数の型を意識しなくて良くなる

「悲観的ロック」と「楽観的ロック」を比較し,違いを明確に述べてください

楽観的ロックは
Aさんが更新画面を開く
Aさんが更新中にBさんが更新画面を開く

Bさんの更新作業が完了する
Aさんの更新作業が完了する,そのときBさんが更新したことを知らせるメッセージを表示する

悲観的ロックはAさんが更新画面を開くとその間誰も更新作業ができない

楽観的ロックはredmineでよく見る(というよりブラウザが絡むとトランザクションを維持するのが難しいので主にこちらが使われる)

シンセサイザー制作メモ ~方針編~

モチベーション

  • Electron,React.js,fluxに慣れておこうと思った
  • WebAudioAPI を使ってみたかった
  • フロントエンドを勉強しようと思った

作るもの

  • シンセサイザー
    • オシレータ,フィルタ,アンプ機能を持つ
    • 音を出さなくても波形が確認できるように周波数特性の表示をさせる
    • ミキサやキーボード,エフェクタなどは上記ができてから着手する

内部設計方針

外部設計方針

読んだら忘れない読書術 と 覚えない記憶術 樺沢紫苑 著 を読んで

www.amazon.co.jp

www.amazon.co.jp

 

この読書感想を書くきっかけになった2冊.

読んでもすぐに忘れてしまうし,覚えていないことが多かった.

2冊を読んでインプットばかりではダメでアウトプットする必要があるなと気づいた.

読んだら忘れない読書術にただ漫然と読むのではなく「目的」を持って読む,と書かれているように読み方も工夫してみようと思った.

「目的」を持って読むために,まず目次を見て全体像を把握して,この章ではこれを学ぼうとはっきり決めて読むのがいいと思う.

 

覚えない記憶術にも全体を見てから細部にという工程だと学習効率がよいということなので,いろんな場面で試してみようと思う.