リトルエンディアンとビッグエンディアンの違いをC#とJavaで実験
仕事でバイナリデータの読み書きをする際に自作ツールとメーカーツールで結果が異なったため、その原因調査の一貫として勉強したことの備忘。
バイトオーダ
多バイトのデータをどういう順番でメモリに格納するか。
プロセッサによって異なる。
Intelはリトルエンディアン。
JVMはビッグエンディアン。
ネットワークにおいても多バイトデータをどう通信するか、バイトオーダを決める必要がある。
TCP/IPはビッグエンディアン。
リトルエンディアン
下位ビットを低位のアドレスに格納する。順序が逆になる。
バイト列: 0x0123456789ABCDEF
メモリ配置: [EF, CD, AB, 89, 67, 45, 34, 01]
キャストする場合、下位のnバイトを読めばいいので効率がいい。
上のバイト列(8バイト)を4バイトにキャストする場合、メモリの0番地〜3番地を読めばよい。
ビッグエンディアン
上位ビットを低位のアドレスに格納する。
バイト列: 0x0123456789ABCDEF
メモリ配置: [01, 23, 45, 67, 89, AB, CD, EF]
キャストする場合上位のnバイトを読むためにオフセットが必要になる。
実験
C#
int data = 0x0123ABCD; byte[] byteArray = BitConverter.GetBytes(data); foreach(byte b in byteArray) Console.Write($"{b:X2} ");
出力結果 CD AB 23 01
リトルエンディアンのため元のデータと逆順で出力される。
Java
int data = 0x0123ABCD; byte[] byteArray = ByteBuffer.allocate(4).putInt(data).array(); for (byte b : byteArray) { System.out.printf("%02X ", b);
出力結果 01 23 AB CD
ビッグエンディアンのため元のデータと同じ順序で出力される。
参考
C/C++で作られたDLLをC#から利用する際の引数の渡し方色々
C言語で書かれたDLLをC#から扱う際に引数の受け渡しが分からなくて色々調べたので、その備忘。
intを引数にとる関数
//C++ //intを受け取って表示し、1加えて返す int __stdcall MyFuncA(int a) { printf("C++ : int a = %d\n", a); return a + 1; }
//C# [DllImport("MyDll.dll")] private static extern int MyFuncA(int a); int ans = MyFuncA(1); Console.WriteLine("C# : ans = {0]",ans);
出力結果 C++ : int a = 1 C# : ans = 2
int*を引数にとる関数
※C#からDLLに値を渡すことはできるが、DLL側での操作がC#の変数に反映できてない。
私の用途ではコレで困らなかったが、C#側にDLL側の操作を反映するにはこれではダメ。要調査。
//C++ //intのポインタを受け取って値を変更。前後の値を出力する。 void __stdcall MyFuncG(int* a) { printf("C++ : a = %d\n", *a); int x = 100; a = &x; printf("C++ : a = %d\n", *a); }
//C#側 [DllImport("MyDll.dll")] private static extern void MyFuncG(ref int a); //refで参照渡し int a = 77; Console.WriteLine($"C# : a = {a} -- before"); MyFuncG(ref a); Console.WriteLine($"C# : a = {a} -- after");
出力結果 C# : a = 77 -- before C++ : a = 77 //値を渡すことはできている C++ : a = 100 C# : a = 77 -- after //DLL側での操作が反映できていない
cahr*を引数にとる関数(入力のみ)
//C++ void __stdcall MyFuncB(char* str) { printf("C++ : %s\n", str); }
//C# [DllImport("MyDll.dll")] private static extern void MyFuncB(string str); MyFuncB("call MyFuncB from C#");
出力結果 C++ : call MyFuncB from C#
char*を引数にとる関数(文字列を操作する)
//C++ void __stdcall MyFuncC(char* str) { printf("C++ : before : %s\n", str); sprintf_s(str, 256, "Set By MyFuncC"); printf("C++ : after : %s\n",str);
//C## [DllImport("MyDll.dll")] //文字列を操作する関数に対してStringBuilderを渡す private static extern void MyFuncC(StringBuilder str); StringBuilder sb = new StringBuilder("Before Str",256); MyFuncC(sb); Console.WriteLine("C# : " + sb);
出力結果 C++ : before : Before Str C++ : after : Set By MyFuncC C# : Set By MyFuncC //DLL内での変更が反映されている
構造体を引数にとる関数
//C++側 typedef struct _SampleStruct { int index; char name[128]; int data[50]; }SampleStruct void __stdcall MyFuncD(SampleStruct st) { printf("C++ : index = %d\n", st.index); printf("C++ : name = %s\n", st.name); printf("C++ : data = {%d, %d, %d}\n", st.data[0], st.data[1], st.data[2]); }
//C# [DllImport("MyDll.dll")] private static extern void MyFuncD(SampleStruct st); //構造体定義 //構造体の型、大きさ、順序を揃える。 [StructLayout(LayoutKind.Sequential)] private struct SampleStruct { public int index; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] //固定長文字列配列 public string name; //固定長配列 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 50)] //固定長配列 public int[] data; } //構造体の初期化 var st = new SampleStruct() { index = 4, name = "構造体サンプル", data = new int[50], }; st.data[0] = 10; st.data[1] = 20; st.data[2] = 30; MyFuncD(st);
出力結果 C++ : index = 4 C++ : name = 構造体サンプル C++ : data = {10, 20, 30}
構造体のポインタを引数にとる関数
//C++ //構造体は上と同じものを利用 void __stdcall MyFuncE(SampleStruct* pst) { (*pst).index = 55; sprintf_s((*pst).name, "構造体ポインタサンプル"); (*pst).data[0] = 51; (*pst).data[1] = 52; (*pst).data[2] = 53; }
//C# //構造体は上と同じものを利用 [DllImport("MyDll.dll")] //SampleStruct*に対してIntPtrを渡す private static extern void MyFuncE(IntPtr pst); var pst = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SampleStruct))); try { MyFuncE(pst); var st_rtn = (SampleStruct)Marshal.PtrToStructure(pst, typeof(SampleStruct)); Console.WriteLine($"C# : index = {st_rtn.index}"); Console.WriteLine($"C# : name = {st_rtn.name}"); Console.WriteLine($"C# : data = [{st_rtn.data[0]},{st_rtn.data[1]},{st_rtn.data[2]}]"); } catch (Exception e) { System.Diagnostics.Debug.WriteLine(e); } finally { //必ずメモリ解放 Marshal.FreeHGlobal(pst); }
出力結果 C# : index = 55 //DLL側の操作が反映されている C# : name = 構造体ポインタサンプル C# : data = [51,52,53]
構造体のポインタのポインタを引数にとる関数
//C++ //ポインタのポインタを引数にとるが処理内容は上と同じ void __stdcall MyFuncI(SampleStruct** ppst) { (**ppst).index = 70; sprintf_s((**ppst).name, "構造体ポインタのポインタのサンプル"); (**ppst).data[0] = 11; (**ppst).data[1] = 22; (**ppst).data[2] = 33; }
//C# //構造体は上と同じものを利用 [DllImport("MyDll.dll")] //構造体SampleStructのポインタのポインタを渡す unsafe private static extern void MyFuncI(ref IntPtr ppst); //ref IntPtrを渡していること以外は構造体のポインタを渡した場合と同様 var pst = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SampleStruct))); try { MyFuncI(ref pst); // ref IntPtrとして構造体を指すポインタの参照を渡す var st_rtn = (SampleStruct)Marshal.PtrToStructure(pst, typeof(SampleStruct)); Console.WriteLine($"C# : index = {st_rtn.index}"); Console.WriteLine($"C# : name = {st_rtn.name}"); Console.WriteLine($"C# : data = [{st_rtn.data[0]},{st_rtn.data[1]},{st_rtn.data[2]}]"); } catch (Exception e) { System.Diagnostics.Debug.WriteLine(e); } finally { //必ずメモリ解放 Marshal.FreeHGlobal(pst); }
出力結果 C# : index = 70 //DLL側の操作が反映されている C# : name = 構造体ポインタのポインタのサンプル C# : data = [11,22,33]
おわり
C、C++,C#いずれも全然習熟していない状態なので、もっとスマートな実装方法があるかもしれない。マサカリお待ちしてます。
ただポインタのポインタを受け渡す例などは調べても全然出てこなかったので、ニッチな困りごとの一助になれば幸いです。
参考
STS(Spring Tool Suits4)にLombak導入
環境
Eclipse : Spring Tool Suits 4(Version: 4.3.1.RELEASE)
Lombok : v1.18.8
(2019/07/28時点)
導入手順
だけのはずが、私の環境ではIDE候補が表示されなかった。 「Specify Location」を選んで手動でIDEを指定するようにとあるが、SpringToolSuits4.appやそれっぽいフォルダを選択できないので、どうしていいか分からず。
結論として、IDEの設定ファイル(ini)を指定する必要があった。
Applications > SpringToolSuits4.app > Contents > Eclipse > SpringToolSuits4.ini
Eclipse(STS)の補完設定(mac)
久しぶりにEclipseを触ったら補完周りが使いづらくて仕方ないので、最低限の設定方法の備忘。
環境
OS : macOS Mojava
Eclipse : SpringToolSuits4
(2019/07/28時点)
やること
- 補完が常に効くようにする
- Enter以外で補完が実行されないようにする
Javaに関してのみ設定するが、HTMLなど他のエディタでも同様。
設定方法
メニューバーより
SpringToolSuits > 環境設定 > Java > Editor > ContentAssist
赤線部分を下記の通り変更する
補完が常に出るようにする
Eclipseの初期設定では.
を入力した場合のみ補完候補が表示される。
また補完候補の表示まで遅延がある。
(Eclipseのバージョンによって初期設定が多少異なるらしい?)
補完候補が開くまでの遅延時間をなくす
Auto Activation > Auto activation delay(ms)
の値を0に設定する。
これで補完が効く状況では補完候補表示がすぐ表示される。
何を入力でも補完候補が開くようにする
Auto Activation > Auto activation triggers for Java
デフォルトでは.
が設定されているので、これを以下のように変更する。
._abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
これで.
以外のキー入力でも補完候補が表示される。
Enter以外で補完が実行されないようにする
補完候補が表示されている場合、デフォルトではEnter以外のキーでも補完が実行される。
困る例として、
String hoge = "hogehoge";
と入力したい場合、hoge
を入力した時点でhogeString
などの候補が表示され、次のスペース入力で補完が実行されてしまう。
上の通り入力するにはhoge
esc
スペース
・・・のように入力する必要があり面倒くさい。
Insertion > Disable insertion triggers except 'Enter
のチェックボックをオンにする。
これでEnterキー以外では補完が実行されなくなる。
書籍「教える技術」
- 作者: 石田淳
- 出版社/メーカー: かんき出版
- 発売日: 2011/06/21
- メディア: 単行本(ソフトカバー)
- 購入: 4人 クリック: 33回
- この商品を含むブログ (19件) を見る
数年前に後輩への指導方法に悩んで買った本。
ふと返し読みしたので、簡単に振り返り。
感想
性格や考え方ではなく「行動」にフォーカスした内容。
具体的にどういう行動が出来てないのか分解して分析し、数件ずつ指摘する。
できたら褒めて、行動を強化。いつでもできるよう習慣付ける。
「行動に着目しろ」という本だけあって、この本を読んだ教える側にも具体的な行動を提示してくれる。 後輩の指導、先輩の指導の受け方、自分の行動の修正など、実際に使える手法や考え方が多い。
後輩や同僚の指導、指摘をする機会は多いが、やり方を間違えると相手を辞めさせたり精神的にダウンさせかねない。 自分や周囲の指導に不安を感じたらぜひ読んでみて欲しい本。
ざっくり要約
概要
「できない」状態とは相手が自分の期待通りの行動をしない、という状態。
自分が期待する行動、理想的な行動を「できてる」人を観察して細かく分解する。
どの行動が「できてない」か分析し、直して欲しい行動を数件指摘する。
「やらなくていいこと」を伝えることも有効。正すべき行動のみに注力させ、評価する。
そして、次の行動で指摘した部分ができたならちゃんと褒める。
行動の修正と評価を繰り返し、できない>できる>いつでもできる という成長を実現する。
ABCモデル
- A:Antecedent=先行条件
- B:Behavior=行動
- C:Consequence=結果
状況に対して行動を起こし、ハッピーな結果を得る
⇛ 行動が強化される(身につく)
「行動」の粒度・指標
MORSの法則
- M:Measured=計測できる
- O:Observable=観察できる
- R:Reliable=信頼できる
- S:Specific=明確化されている
信頼されるべし
教える相手から教える側への信頼や安心感がないと、そもそも「褒める」が機能しない。
(嫌いな相手、信頼できない相手に褒め言葉を言われても嬉しくない)
そうなると正しい行動を強化できず、教えること自体が成り立たない。
直接的な教える技術ではないが、日頃からコミュニケーションをとる、ネガティブな態度を見せないなど、 何でも話せる・挑戦させてくれる、という安心感を相手に与えられるように振る舞う必要がある。
書籍「みんなが自分で考え始める「15分ミーティング」のすごい効果」
みんなが自分で考えはじめる 「15分ミーティング」のすごい効果
- 作者: 矢本治
- 出版社/メーカー: 日本実業出版社
- 発売日: 2018/02/22
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
感想
「Yahooの1on1」などのミーティング関連本と言っていることは概ね同じ。
ただ違うのは「できる人・職場を磨くための本」というより「出来ない職場で苦労してきた本」という感じ。
ミーティングやコーチングの技術を、筆者流にできるだけ簡単で明確な方法にして実際のミーティングに落とし込んだ、という印象。
理論本ではなく実践アイデアのひとつ。そのため職場に導入したり、人に教えるためのハードルが低く感じられる。
ターゲット層の合わせてか、文量も少なく1〜2時間で読み切れるコンパクトさも、困ったちゃん職場的に◎
ざっくり要約
- ミーティングの時間は15分を目安にする
- 提案、整理、計画立てを各5分程度
- 提案は全員が付箋に決めた数のアイデアを書いて発表
- 簡潔に意見をまとめる訓練。
- 「私も同じ」を防ぐ。同じでも表に出てくる。
- 声の小さい人から順に発表。
- 強い意見に委縮しないため
- 付箋に書いた事以外は話さない。
- 発表されてない意見が採用されると、無駄な会議と思われる。
- 結論を誘導しない
- 誘導は雰囲気で伝わる。無駄な議論だと思われる。
- 誘導するくらいなら最初から意見をプレゼンする。
- 他人の意見を裁かない
- その場で否定、批判、意見しない。意見の多様性を喜ぶ。
- 代案なき否定はNG
- 繊細な議題でないなら、代案なき否定はNG。
否定するのは簡単。改善案考えるのは難しく、訓練が必要。
案を出し実際に取り組むことに価値がある。PDCAでいうとDoまで実現させる
- 繊細な議題でないなら、代案なき否定はNG。
- 過去に向かって意見しない
- 「なぜこうなったのか」 ⇒ 「どうしたら改善するか」
- 賛同なきチャレンジも必要。
- みんながやりたくないことこそ、必要なことかもしれない。
- 採用された意見に全員で取り組む。
- 選手は全員ベストを尽くす。「監督の指示が悪い」という選手はいない。
- タスクごとに責任者を立てる
- 実施担当者ではない。他者に依頼するなどすればいいが、最終的な責任を負う。
- 最初の一歩をその場(アイデア採用時点)で実施する
- ラフスケッチ作る、次の会議日程決める
- 期限でなく実行日を決める。「いつやるか?」
書籍「リーダブルコード」
前々から読もうと思っていた本が会社に届いたので読んでみた。
テスト駆動開発と同じく、手元に置いておきたい本なので結局購入しそう・・・
リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)
- 作者: Dustin Boswell,Trevor Foucher,須藤功平,角征典
- 出版社/メーカー: オライリージャパン
- 発売日: 2012/06/23
- メディア: 単行本(ソフトカバー)
- 購入: 68人 クリック: 1,802回
- この商品を含むブログ (140件) を見る
感想
全体を通して良いコード(≒可読性・保守性が高いコード)の書き方についての本。
普段から「良いコード」を意識している人には目新しい内容はないかも。
(例えば命名ルール、ネストを浅くするには、など)
ただ日頃あまり明文化しない勘・コツの類がコンパクトに文章化されているので、知識の整理や振り返り、人に教える教材としてとても良い。
特にチームで開発する場合、「経験に基づく勘・コツ」は三者三様だし文章化できていない暗黙知の場合が多い。あるべき姿や言葉遣いを統一する拠り所として、全員で一読すると捗りそう。
図や例を多様して200ページという軽さも、サラッと読んで勧められるので良ポイント。
学ぶためにも復習のためにも、ぜひ一度読んで欲しい良書。
良いコード・良い設計のための本ということで、テスト駆動開発も合わせてオススメ。
書籍の概要
項目
コンセプト
- 他人が理解しやすいように書く
- 他人とは6ヶ月後の自分かもしれない
- 名前や順番に意味を持たせる
- 小さいことはいいこと(スコープ、関数の大きさ、記述量)
初めて知った手法・考え方
以下、本書で初めて知った方法や意識したことがなかった方法を抜粋。
命名規則
名前に単位や属性を付加する
- より明確な単語を選ぶ。
例曖昧なGetHoge
ではなく具体的な動作を示すDownloadHoge
,FetchHoge
とする。
単語の選択に困ったら類語辞典で近い意味の英単語を探す。
Qiitaやブログに頻出英単語まとめ記事がたくさんあるので、参照する。 size_mb
,delay_secs
のように単位をつける。plaintext_password
のように平文であることを明示する。- 書式の事例紹介:クラスのメンバ変数の末尾に
_
をつける(例:size_
,name_
)
「良いコード」の最も基礎的な部分だけど、要するに「知ってるか知らないか」なのでレベルを上げるには時間と根気と良いコードを読む機会が必要そう。
コメントの書き方
- 名前付き引数風コメント
名前付き引数が使えない言語で引数の意味を明示する方法の案。
Connect(timeout = 10, use_encryption = False) # 名前付き引数
Connect(/*timeout =*/10, /*use_encryption =*/false) //名前付き引数風コメント
- 引数に関するコメントの書き方例
// Connect(timeout, use_encryption) // [secs] [boolean] con = Connect(10, false)
- 自分の考えを記録する
例:// ◯◯という理由で〜〜というロジックを採用した
- 定数にコメントをつける
なぜその固定値を選んだか根拠を記す。 - 挙動を厳密かつ完結に記す
△://ファイルの行数を数える
・・・空ファイルは?hello\n\r cruel\n world\r
は何行?
◯://ファイルに含まれる'\n'を数える
- 入力のコーナーケースの実例を記す
上記のようなコメントを規約としてある程度強制すると、命名や設計を明確・簡潔に作るよう促せそう。
簡潔・明快な日本語を書く能力=国語力なので、命名と同じく結局は教養的なものが求められるので
一朝一夕では鍛えられない。根気よくコードレビューしながら育てるしかない?
制御フロー・ロジックの単純化
- ネストを浅くする
- ループや関数を早く抜ける(if, elseを浅くする)
- スコープは極力小さくする
- 三項演算子、Do Whileなどパッと見で分かりづらい処理は必要性を考える
変数やコメントの章と比べると技法的な話は少なめで、一般的なお作法という感じ。
読み手に対してなにかを解釈したり覚えたりという負荷をかけないこと。
リファクタリング
- 関数を小さく分割して汎用化する
- 複数の機能を個別機能に分割する
- 上位の機能を支える下位の機能を抽出して分割する
こちらも一般的なお話。
小さく分割されていて、かつ処理フローがシンプルだと読みやすい上にテストしやすい。
その他
- 意図が分かるテストを書く
ソースコードと同様に意味のある名前をつけ、意図が分かる値をテストする。 - 標準ライブラリを知る
標準ライブラリを知っておけば余計なコードやテスト、ドキュメントを書かずに済む.
まとめ
上記以外のことや上記の詳細も書かれているが、基本的には下2つが基本軸。
- いい名前を付けること
- 短いコードを書くこと
これらを実現するためのアイデア・ベストプラクティス集という感じ。
本書をすんなり理解できるチームなら本書を参考に開発チーム全体で「良いコード」の認識を共有すると捗りそう。
あるいはこれから学ぶという人も、最初のチュートリアルや簡単なモノづくりが一段落したら早い内に読んで、その後本書の内容を意識しながらコーディングすると良いコードを書く「良い習慣」が身につくと思う。
最初に書いたとおり、サラッと読めるのに学びと再発見の多い良い本でした。