概要
c
のお勉強c++
ではないC11
をターゲットにするC17
ほどの新環境ではない
- 本格的に開始して3日目の記事です
matlab
で書けるやつをc
に移行したい- オブジェクト指向の設計がちょっとできるようになった
- 試しに
codegen
を使ってみたけど納得できなかった…
- どうしても組み込み開発にオブジェクト指向を使ってみたい…
- 簡単なルーチンのコードならぶっちゃけ引き継ぎで確保している
handle class
がとても便利だよね,ぜひともあれをやりたい
- 結果,オレオレ自己参照構造体にオレオレ関数を設定する方針に至る
- MBP-2016-15(Intel Core i7), macOS Monterey, Xcode CommandLine Tool
clang
- これ
-std=c11
にするとC11
を基準にやってくれるらしい - はじめて
makefile
をつくってみたけどかなりべんりだった
- これ
設計方針
- 初心者なので最も簡単な事例を考える
- 「依存関係を生成して変更に追従して参照できる」
- main関数でハンドルオブジェクト的に使える
A.b();
みたいなのは無理やわ- (実際やろうと思えばいけるっぽいけど設計も用法も無茶苦茶になりそうで流石に却下した)
b(A);
みたいなので実装した
segmentation fault
やbus error
を吐かない- メモリ確保からメモリ解放まで一通り実践する
重要問題
- callocを適切に設置しないと詰む
- (少なくとも上記の環境では)オレオレ自己参照を含むオレオレ構造体のサイズをコンパイラがまともに推定してくれなかったので,自力でオレオレ構造体の容量を計算してアドレス空間を動的に生成した上でオレオレ自己参照に割り当てする必要がある
- これはつまり仕様の変更に対して自動的に修正するコードパターンにしないとサイアクな目(連鎖的なセグフォ)に遭う
- 仕様を記述した1つだけの
struct
をextern
して参照してメモリ確保を連鎖的に実装した(最適かどうかは知らない)
c
は関数型の言語ゆえにmain()
で手元でアドレスを取って引数に入れる手間をかけない限りどうやっても必ず値代入になる
以下,出来上がったもの
#ifndef subPrm #define subPrm typedef struct Elm { int mem; int prv; } Elm; // parameter method extern const Elm Prm; typedef struct prm { //24 struct prm *adr; //8 long double num; //16 } prm; prm defprm(long double num); void setprm(prm theprm, long double num); void delprm(prm theprm); void expprm(prm theprm); long double getprm(prm theprm); #endif
prm
でオレオレ構造体を定義するprm *adr
でオレオレ自己参照を定義する- ヘッダファイルはメンバ変数と関数宣言を置く
- 仕様の大幅な変更がない限りはソースファイルだけで処理しよう
- 関数定義はソースファイルに分ける
- プロトタイプ宣言はヘッダファイルにおくという意味
ifndef
タイプのインクルードガードを冒頭に設置するElm
に仕様(拡張性がある)を持つ構造体を定義してPlm
で公開する
他も似たような感じ
#include <stdlib.h> #include <stdio.h> #include "subPrm.h" const Elm Prm = { .mem = 24 }; prm defprm(long double num){ prm *adr = (prm*)calloc(1,Prm.mem); prm theprm = *adr; theprm.adr = adr; theprm.adr->num = num; return (theprm); }; void setprm(prm theprm, long double num){ theprm.adr->num = num; }; void delprm(prm theprm){ free(theprm.adr); }; void expprm(prm theprm){ printf("%Lf\n",theprm.adr->num); }; long double getprm(prm theprm){ return (theprm.adr->num); };
prm *adr = (prm*)calloc(1,Prm.mem)
で確保してprm theprm = *adr;
に定義free(theprm.adr)
でメモリ解放(これってmatlab
のclear
に相当するのでは?)const Elm Prm = {.mem = 24};
に仕様(メモリサイズ)を設定する- 使いたい関数をあらかじめ定義しておく(命名規則に注意)
- メンバ関数が使えないせいで苦行でしかないんだが…
他も似たような感じ
#ifndef subAxs2d #define subAxs2d #include "subPrm.h" // axis method extern const Elm Axs; typedef struct axs2d { // 56 struct axs2d* adr; //8 prm px; //24 prm py; //24 } axs2d; axs2d defaxs2d(prm px, prm py); void setaxs2d(axs2d xy, prm px, prm py); void delaxs2d(axs2d xy); void expaxs2d(axs2d xy); long double getaxs2dx(axs2d xy); long double getaxs2dy(axs2d xy); #endif
#include <stdlib.h> #include <stdio.h> #include "subPrm.h" #include "subAxs2d.h" const Elm Axs = { .mem = 56 }; axs2d defaxs2d(prm px, prm py){ axs2d *adr = (axs2d*)calloc(1,Axs.mem); axs2d xy = *adr; xy.adr = adr; xy.adr->px = px; xy.adr->py = py; return (xy); }; void setaxs2d(axs2d xy, prm px, prm py){ xy.adr->px = px; xy.adr->py = py; }; void delaxs2d(axs2d xy){ free(xy.adr); }; void expaxs2d(axs2d xy){ printf("(%Lf, %Lf)\n", getprm(xy.adr->px), getprm(xy.adr->py)); }; long double getaxs2dx(axs2d xy){ return (getprm(xy.adr->px)); }; long double getaxs2dy(axs2d xy){ return (getprm(xy.adr->py)); };
getprm()
を連鎖的に仕様する,ポインタ地獄をある程度に回避できる
#ifndef subVec2d #define subVec2d #include "subAxs2d.h" // vector method extern const Elm Vec; typedef struct vec2d { //152 struct vec2d *adr; //8 axs2d xy1; //56 axs2d xy2; //56 long double len; //16 long double ang; //16 } vec2d; vec2d defvec2d(axs2d xy1, axs2d xy2); void setvec2d(vec2d v, axs2d xy1, axs2d xy2); void delvec2d(vec2d v); void expvec2d(vec2d v); void calvec2d(vec2d v); #endif
#include <stdlib.h> #include <stdio.h> #include <math.h> #include "test7subAxs2d.h" #include "test7subVec2d.h" const Elm Vec = { .mem = 152 }; vec2d defvec2d(axs2d xy1, axs2d xy2){ vec2d *adr = (vec2d*)calloc(1,Vec.mem); vec2d v = *adr; v.adr = adr; v.adr->xy1 = xy1; v.adr->xy2 = xy2; return (v); }; void setvec2d(vec2d v, axs2d xy1, axs2d xy2){ v.adr->xy1 = xy1; v.adr->xy2 = xy2; }; void delvec2d(vec2d v){ free(v.adr); } void expvec2d(vec2d v){ long double x1 = getaxs2dx(v.adr->xy1); long double y1 = getaxs2dy(v.adr->xy1); long double x2 = getaxs2dx(v.adr->xy2); long double y2 = getaxs2dy(v.adr->xy2); printf("(%Lf, %Lf) -> (%Lf, %Lf)\n",x1,y1,x2,y2); } void calvec2d(vec2d v){ long double x1 = getaxs2dx(v.adr->xy1); long double y1 = getaxs2dy(v.adr->xy1); long double x2 = getaxs2dx(v.adr->xy2); long double y2 = getaxs2dy(v.adr->xy2); v.adr->len = pow(pow((x2-x1),2)+pow((y2-y1),2),0.5); if (v.adr->len == 0){ v.adr->ang = 0; } else { v.adr->ang = atan2((y2 - y1), (x2 - x1)); }; printf("len : %Lf, ang : %Lf\n",v.adr->len,(v.adr->ang)*180/M_PI); };
getaxs2dx()
とgetaxs2dy()
を連鎖的に仕様する,ポインタ地獄をある程度に回避できる
#include <stdlib.h> #include <stdio.h> #include <math.h> #include "test7subAxs2d.h" #include "test7subVec2d.h" int main(){ double in; printf("x1 : "); scanf("%lf", &in); prm x1 = defprm(in); printf("rep : "); expprm(x1); printf("y1 : "); scanf("%lf", &in); prm y1 = defprm(in); printf("rep : "); expprm(y1); axs2d xy1 = defaxs2d(x1,y1); printf("axs xy1 : "); expaxs2d(xy1); printf("x2 : "); scanf("%lf", &in); prm x2 = defprm(in); printf("rep : "); expprm(x2); printf("y2 : "); scanf("%lf", &in); prm y2 = defprm(in); printf("rep : "); expprm(y2); axs2d xy2 = defaxs2d(x2,y2); printf("axs xy2 : "); expaxs2d(xy2); printf("vec xy1 -> xy2 : "); vec2d v = defvec2d(xy1, xy2); expvec2d(v); calvec2d(v); int amn; printf("fib-n order : "); scanf("%d", &amn); for (int i=0; i<amn; i++){ if (0==(i%2)){ setprm(x2, getprm(x2) + getprm(y2)); }else{ setprm(y2, getprm(x2) + getprm(y2)); } printf("axs xy2 : "); expaxs2d(xy2); } calvec2d(v); return(0); }
機能
- 変更および参照が可能な変数を設定する
prm
オブジェクト - 2つの
prm
オブジェクトで位置座標を表現するaxs2d
オブジェクト - 2つの
axs2d
オブジェクトでベクトルを表現するvec2d
オブジェクトvec2d
オブジェクトに向きと大きさを計算して与えるcalvec2d
関数- 自動更新もちょっと考えたけど地獄が生まれるから無理だと判定
main()
では以下を意図しているぜ- 数字入力の受付
- オブジェクトの生成と代入
- オブジェクトの連鎖的な生成
- オブジェクトに固有の関数の実行
- 下位オブジェクトの再代入と上位オブジェクトからの参照
- ちなみにこれは単純なフィボナッチ数列だったりする
long double
は出来心
コンパイル,実行
NAME = test.exe SRMN = main.c SRSB = subPrm.c subAxs2d.c subVec2d.c OBMN = test.exe OBSB = $(SRSB:%.c=%.o) GCCC = clang GCOP = -std=c11 make : $(SRMN) $(SRSB) $(GCCC) $(GCOP) $(SRMN) $(SRSB) -o $(OBMN)
いちいち全部コンパイルするので効率は悪い,もっと最適化できる
% make % ./test.exe x1 : 0 rep : 0.000000 y1 : 0 rep : 0.000000 axs xy1 : (0.000000, 0.000000) x2 : 1 rep : 1.000000 y2 : 1 rep : 1.000000 axs xy2 : (1.000000, 1.000000) vec xy1 -> xy2 : (0.000000, 0.000000) -> (1.000000, 1.000000) len : 1.414214, ang : 45.000000 fib-n order : 70 axs xy2 : (2.000000, 1.000000) axs xy2 : (2.000000, 3.000000) axs xy2 : (5.000000, 3.000000) axs xy2 : (5.000000, 8.000000) axs xy2 : (13.000000, 8.000000) axs xy2 : (13.000000, 21.000000) axs xy2 : (34.000000, 21.000000) axs xy2 : (34.000000, 55.000000) axs xy2 : (89.000000, 55.000000) axs xy2 : (89.000000, 144.000000) axs xy2 : (233.000000, 144.000000) axs xy2 : (233.000000, 377.000000) axs xy2 : (610.000000, 377.000000) axs xy2 : (610.000000, 987.000000) axs xy2 : (1597.000000, 987.000000) axs xy2 : (1597.000000, 2584.000000) axs xy2 : (4181.000000, 2584.000000) axs xy2 : (4181.000000, 6765.000000) axs xy2 : (10946.000000, 6765.000000) axs xy2 : (10946.000000, 17711.000000) axs xy2 : (28657.000000, 17711.000000) axs xy2 : (28657.000000, 46368.000000) axs xy2 : (75025.000000, 46368.000000) axs xy2 : (75025.000000, 121393.000000) axs xy2 : (196418.000000, 121393.000000) axs xy2 : (196418.000000, 317811.000000) axs xy2 : (514229.000000, 317811.000000) axs xy2 : (514229.000000, 832040.000000) axs xy2 : (1346269.000000, 832040.000000) axs xy2 : (1346269.000000, 2178309.000000) axs xy2 : (3524578.000000, 2178309.000000) axs xy2 : (3524578.000000, 5702887.000000) axs xy2 : (9227465.000000, 5702887.000000) axs xy2 : (9227465.000000, 14930352.000000) axs xy2 : (24157817.000000, 14930352.000000) axs xy2 : (24157817.000000, 39088169.000000) axs xy2 : (63245986.000000, 39088169.000000) axs xy2 : (63245986.000000, 102334155.000000) axs xy2 : (165580141.000000, 102334155.000000) axs xy2 : (165580141.000000, 267914296.000000) axs xy2 : (433494437.000000, 267914296.000000) axs xy2 : (433494437.000000, 701408733.000000) axs xy2 : (1134903170.000000, 701408733.000000) axs xy2 : (1134903170.000000, 1836311903.000000) axs xy2 : (2971215073.000000, 1836311903.000000) axs xy2 : (2971215073.000000, 4807526976.000000) axs xy2 : (7778742049.000000, 4807526976.000000) axs xy2 : (7778742049.000000, 12586269025.000000) axs xy2 : (20365011074.000000, 12586269025.000000) axs xy2 : (20365011074.000000, 32951280099.000000) axs xy2 : (53316291173.000000, 32951280099.000000) axs xy2 : (53316291173.000000, 86267571272.000000) axs xy2 : (139583862445.000000, 86267571272.000000) axs xy2 : (139583862445.000000, 225851433717.000000) axs xy2 : (365435296162.000000, 225851433717.000000) axs xy2 : (365435296162.000000, 591286729879.000000) axs xy2 : (956722026041.000000, 591286729879.000000) axs xy2 : (956722026041.000000, 1548008755920.000000) axs xy2 : (2504730781961.000000, 1548008755920.000000) axs xy2 : (2504730781961.000000, 4052739537881.000000) axs xy2 : (6557470319842.000000, 4052739537881.000000) axs xy2 : (6557470319842.000000, 10610209857723.000000) axs xy2 : (17167680177565.000000, 10610209857723.000000) axs xy2 : (17167680177565.000000, 27777890035288.000000) axs xy2 : (44945570212853.000000, 27777890035288.000000) axs xy2 : (44945570212853.000000, 72723460248141.000000) axs xy2 : (117669030460994.000000, 72723460248141.000000) axs xy2 : (117669030460994.000000, 190392490709135.000000) axs xy2 : (308061521170129.000000, 190392490709135.000000) axs xy2 : (308061521170129.000000, 498454011879264.000000) len : 585967834257297.125000, ang : 58.282526
(1,1)
スタートだとオーダーは70が限界,(0.1,0.1)
スタートだとオーダーは80が限界
それ以上だとcalvec2d()
でinf
が発生してる,math.h
でも2乗はキツいらしいな…
感想
- 行列とか実装して演算も設計すれば自由度が広がりそう
- でもやっぱりアドレス代入(
&Obj
)でいい気がしてきた- 32bitか64bitかでメモリサイズが変わる
- OSとコンパイラでメモリサイズが変わる
const Elm Obj1={.mem=val1};
でval1
がコンパイル時に決定できないとエラーを吐く,例えば他のconst Elm Obj2={.mem=val2};
のval2
は参照できない- アドレスを羅列するだけなら
sizeof(void*)
を参照先の数だけ用意すれば良いし,c
に固有なデータ型のサイズはデフォで使えるはずだし,かえって環境や仕様の変化に強いのは - 結果的にメモリリーク対策がやりやすくなりそうなので次はそのバージョンもやってみる
main()
を書くのがちょっと面倒になるけどしょうがない,c++
使わせて欲しい
参考
http://www9.plala.or.jp/sgwr-t/lib/calloc.html
https://yu-nix.com/archives/c-object-extends/
https://programming-place.net/ppp/contents/c/037.html
https://c.perlzemi.com/blog/20210212090234.html
http://www.c-lang.org/self.html
https://dixq.net/forum/viewtopic.php?t=18103
上記に加えてTwitterの観測結果