Address of stack memory associated with local variable ‘hoge’ returned ワーニング

これはエラーではなくて警告でよく出ると思うのだが、この現象の解説がやはりというべきかここにあった。

char** ReadLineImpl::my_completion () {
    char* matches[1];
    matches[0] = "add";

    return matches;
}

要するに「単に配列などを定義しただけでは、一時的にメモリが確保されているだけなので、場合によっては正しいポインタを返さないかもしれないよ」という仕様由来の背景がある。

ああ、ややこしい。

解決策で最もわかりやすいのは変数を static で宣言することだろう。

なお、一般的な static の修飾子の説明はここあたり参照。

 

cannot refer to declaration with an array type inside block エラー

C のブロック内(^ に続く関数のあれ)では、配列への参照はできないらしい。

わかりやすい例がここにあった。

void block_arr()
 {
   int res = 0;
   int i[4] = { 3, 4, 4, 1 };
   int* j = i;
  
   int (^test_block )(int) = ^(int num)
     {
       return num + i[1];      // This is an error: "error: cannot refer to declaration with an array type inside block"
 //      return num + j[1];    // This would work
     };
  
     res = test_block(7);
 }

なんでこんな制限があるのか最初よくわからなかったのだが、そこの人いわく、「ブロック内で配列を使ってしまうと配列全体をコピーする必要があるため、非効率だ。ポインタは OK 。」ということらしい。

 

strlen と sizeof にまつわる不具合

以前に Mac 環境で C を使う場合、バイナリバイト列などのデータは NSData に任せた方がいいということは述べた。

いうまでもなく、C のデータの取り扱いの煩雑さは負担と考えているからだ。

そうはいっても、 既に(Objective ではない素の)C で各種データの定義が完了しているような場合、そういうわけにもいかない。例えば C で書かれた汎用ライブラリの機能に大きく依存しているようなプロジェクトではどうしてもどうなってしまう。

ここら辺、どの部分を NSData (というか cocoa の各種フレームワーク)に割り振り、どの部分を C で押し通すのかは、センスの出るところだと思う。

ところで、C 言語に関しては、巷の C 談義のようなものだけではうまくいかないと感じている。

ポインタの話は C を語る上で重要だが、ポインタだけ知っていても実用的なコードが書けるとは思えない。

ところで、この前、さるプログラムに不具合が出て、原因を調べていて気がついたのだが、以下のコードを実行した場合、結果はどうなると思います?

uint8_t AP1[] = {
        0x00, 0x20, 0x00, 0x88, 0x04
    };
printf("strlen(AP1): %d\n", strlen(AP1));
printf("sizeof(AP1): %d\n", sizeof(AP1));

この部分だけを取り出すとわかりやすいと思うが、結果は

strlen(AP1): 0
sizeof(AP1): 5

となる。

「終端文字 0x00 が先頭にあるのだから、文字列の長さを求める strlen では 0。使われているメモリのバイト数を求める sizeof では 5。当たり前じゃないか」と言われるかもしれないが、実際の(不具合のあった)コードはこんなにわかりやすくはない。

具体的には AP1 の末尾にバイト列を追加してバイト列を生成するためのメモリ確保の段階で

malloc(strlen(AP1) + strlen(hoge))

とやっていたんだな。

実は、これらコードを含むプログラムは USB を制御するコマンドで AP1[5] もそのコマンドの一つだった。

実際にデバイスを制御するわけだから、コマンドといえどもバイナリデータであってもおかしくはないのだが、そこら辺の事情をわからず「コマンド」と言われれば文字列を連想するのが普通ではなかろうか。

で、文字列と思いこんでこのような実装になってしまったと。

この手の不具合が発見しにくいのは、0 を含まない「コマンド」の場合は正しく動作してしまうからだ。

なお、バイト列とバイト列を結合させたい場合には

   uint8_t cmd[sizeof(AP1)+sizeof(hoge)];
    memcpy(cmd, AP1, sizeof(AP1));
    memcpy(cmd+sizeof(AP1), hoge, sizeof(hoge));

出力させる時は

for (int i = 0; i < sizeof(cmd); i++) {
            printf("%02x", cmd[i]);
    }

と書くとわかりやすいでしょうか。

 

JavaScript の非同期処理のクセが強いんじゃば

われわれのグループ(ネタのみ提供者含む)は、どういうわけか JavaScript (つか node 案件)に関してガチ仕事レベルで取り組んだものは一人もいない。

いや、簡単なウェブ案件はあるんすよ。

だが、chrome 拡張に手を出すにあたってそれじゃいかんだろうとようやくある程度本格的にプログラミングし始めた。

node 系の隆盛などは知っているし、「ブラウザ以外でも動作するようになって・・・」みたいな歴史的な知識もある。

でも、実際にプログラム組まないとその言語固有のクセなんてわからない。

 

けっこう見落とされがちなのは「JavaScript は非同期処理」というものだろう。

例えば、


処理1

処理2

処理3


というようなプログラム構造があった時、処理2が重いと先に処理3を実行することがある。
だから、処理2を処理3より前に実行したいときは promise を使いましょう、みたいな教科書にも出てくるよくある話に繋がるわけだが、知識として知っていても実務レベルで身についているかというとそんなことはなく、バグを作るときは作る(笑)。

この前イタい思いをしたのは次のようなケース。


コールバック関数1{

promise(

コールバック関数2

処理A

).then{

処理B

}

}


こう書くと「コールバック関数2が完了した後に処理Aが走ると思い込んで不具合起こしたんでしょ」と言われそうだが、実際、その通り。

もちろん落ち着いて考えれば、コールバック関数2の処理が重い場合、JavaScript はしれっと処理Aを先に実行してしまうってのはわかる。

大体においてコールバック関数を使うのは思い処理をやらせたいときだから、バグが出るのは当然なのだが、初学者のワイは「promise 使ってるじゃん!コールバック関数2と処理Aが何でこの順序でできないんだよ」と静かに怒りを覚えていた。

ちなみに何でこの不具合に気がついたかといえば、コールバック関数2で値を代入した変数が、処理Aの時点で何度実行しても undefined になっていたから。

慣れてくれば、undifined が出てきた時点で、「ああ、これは実行順序に起因するバグだ」と気が付くのだろうが、習いたての段階ではそうそう気が付かないのだ。

色々と学びの多いプログラミングでやんした。

 

(続く)