関数ポインタのはなし
関数ポインタとは
関数をポインタ変数に入れて使えるよっていうやつです
これです
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の関数は第一級関数ではないです
第一級関数の説明は
が分かりやすかったです
関数ポインタで高階関数が作れるので,試しに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が持っていなきゃいけないのが気に入らないです
まとめ
- 関数名はアドレスなのでポインタに入れられる
- 関数ポインタで高階関数が作れる
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カーネル,emacs,coreutilsなどのソースを読もうとしたが挫折した
- autoconf,makefile,gcc,ldコマンドのざっくりとしたものは理解しているつもり
- Deep Lerningに似たニューラルネットワークを作ったことがある
- 信号処理のプログラムを作ったことがある
- ロボットを動かしたことがある
ゴール
気をつけたい
- 理由付けが弱いので挫折しないようにのんびりやる
シンセサイザー制作メモ 〜プロトタイプ編〜
基本的なシンセサイザーの中身はこんな感じだと思う
[oscillator] -> [filter] -> [amp/EG]
ということでコピペして書いた
設定できるパラメータは以下
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 | Level がSustain になるまでの時間 |
Sustain | 0.0〜1.0 | 0.5 | Level の減衰が収束する値 |
Release | 0.0〜1.0 | 1.0 | Level がSustain から0(最小値)になるまでの時間 |
次にやること
- 鍵盤みたいなの作ろう(キーボードで音出すのでもいいけど)
AnalyserNode
波形を可視化したい- FFTから着手する
- ElectronかReact.jsを使おう
- まずは↓のリポジトリに移植しておこう
github.com
- まずは↓のリポジトリに移植しておこう
参考にさせていただきました
プログラマーの力量を見極める質問に答えてみた
プログラミング一般のところだけ自分なりに答えをまとめてみる
「等値」と「等価」の違いを説明してください
言葉の意味だと
等値は値の等しいこと
英語ではequality
等価は価値や価格が等しいこと
英語ではequivalence
数学的な意味だと
等値は等式のことになるので A = B
等価はA ⇔ B
,すなわち,AならばBとBならばAが同時に成り立つことをいう
ここまで説明すれば,プログラム的な意味は
A == B
が等値
!(A < B) && !(B < A)
が等価
等価は++E
とE += 1
が同じとか,!E
と0 == 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 を使ってみたかった
- フロントエンドを勉強しようと思った
作るもの
- シンセサイザー
- オシレータ,フィルタ,アンプ機能を持つ
- 音を出さなくても波形が確認できるように周波数特性の表示をさせる
- ミキサやキーボード,エフェクタなどは上記ができてから着手する
内部設計方針
- Node.js v6系(v6.2.2)
- npm v3系(3.9.5)
- Electron v1系(1.2.5)
- flux で作る
- WebAudioAPIを利用
- コーディング規約
外部設計方針
読んだら忘れない読書術 と 覚えない記憶術 樺沢紫苑 著 を読んで
この読書感想を書くきっかけになった2冊.
読んでもすぐに忘れてしまうし,覚えていないことが多かった.
2冊を読んでインプットばかりではダメでアウトプットする必要があるなと気づいた.
読んだら忘れない読書術にただ漫然と読むのではなく「目的」を持って読む,と書かれているように読み方も工夫してみようと思った.
「目的」を持って読むために,まず目次を見て全体像を把握して,この章ではこれを学ぼうとはっきり決めて読むのがいいと思う.
覚えない記憶術にも全体を見てから細部にという工程だと学習効率がよいということなので,いろんな場面で試してみようと思う.