DT-900/910/930 アプリケーション作成上の注意点 Rev 1.00 カシオ計算機株式会社 国内営業統轄部 IT推進部部
はじめに この解説書は、アプリケーションの組み方が原因となるトラブルについて、過去の事例と共にその内 容と対策を述べたものです。 プログラムを作成する上で、この解説書を十分ご活用下さるよう、お願いいたします。 1
注意点一覧 1.プログラミング上の注意点 No 内 容 発生する現象 ページ .
1.プログラミング上の注意点 1-1.配列変数のオーバーフロー 【内容】 指定したサイズ以上の文字を配列変数に代入してしまう、もしくは配列に代入するつもりが、 エリア外に代入を行ってしまう。 これによって、配列外のエリアを破壊してしまい、以降の処理で暴走することがある。 【例】 ①UB abc[10]; strcpy(abc, "1234567890"); (上記の場合、文字数10とNULL文字の合計11バイトを要するため、配列は11バ イト以上必要) ②bar_str[len-4]=’\0’; (上記の指定で、len の値が4未満であったり、len-4 が宣言した配列の大きさを超えてい た場合、配列外のエリアを破壊) ③memcpy(abc, "1234567890", len-4); (上記同様、len の値が4未満であったり、コピーデータが配列のエリアを超えていた場 合、配列外のエリアを破壊) 【対策】 ・sizeof 演算子を利用する。 sizeof(配列変数名)で、配列の大きさが得られるので、その値を使用して エリア外への書き込みを行わないようにする。 ・変数の値を事前にチェックする。 添え
1-2.アドレス未設定のポインタの使用 【内容】 ポインタ変数にアドレスを与えないまま、処理に使用した。 これによって、不定のアドレスにアクセスしてしまい、以降の処理で暴走することがある。 【例】 UB *abc; strcpy(abc, "1234567890"); (アドレスを設定しない内に処理に使用するため、どこにアクセスするか分からない) 【対策】 対応策の一つとして、変数宣言時に初期化してしまう。 【対策例】 UB c[100], *abc = c; 4
1-3.変数の型の不一致 【内容】 引数として与える変数が、正しい型で取られていない。 この場合、関数に正常な値が渡されなかったり、逆に正常な値を取得できないことがあり、 以降の処理が正しく動作しない。 【例】 ①関数内で値が格納される変数の型が異なる int rcd, barlen; OBR_gets(obrbuf, &rcd, &barlen); (関数仕様上、rcd、barlen はそれぞれ UW、UB だが、両方とも int で宣言している。 rcd の場合、int も UW も4バイトなので、結果的には同じになるが、barlen の場合は、 UB が1バイトなので、参照すると全く的外れな値が入っている) ②関数に値を渡す変数の型が異なる int busy_ch,nonbusy_ch; c_open(COM0,param,buf,buf_l,&tim_out,&del_cod,busy_ch,nonbusy_ch); (関数仕様上、busy_ch、nonbusy_ch はそれぞれ B だが、両方とも int で宣言している。 本来1バイトの引数を渡すべきところに4バイトの変数を与えた場合、自
1-4.永久ループの使用 【内容】 ある特定の条件が発生しない限り、ループを抜けないようなロジックを組むと、ループから 抜け出せず、プログラムがストップした様に見えることがある。 【例】 key_fnc_mode(FNC_MODE_SET,FNC_2,&flg_id,&ptn); while(1){ flg_sts(&dumy,&ptn,FL_FK_INT_ID); if (ptn & FL_FK_INT_FNC1) break; } (F1が押されるまでループするが、F1を通知モードにしていないと永久に抜けない) 【対策】 対策として、「永久ループ」の形を取らない組み方や、必ず成立しうる脱出条 件を設定する。 【対策例】 key_fnc_mode(FNC_MODE_SET, FNC_2, &flg_id, &ptn); s_timeget(&tim_dat); tim_dat.
1-5.使用不可のCライブラリ関数の使用 【内容】 動作保証をしていないSH-Cの標準C関数を使用する。 動作の確認をしていないため、どのような現象が発生するかは不明。 【例】 fp=fopen(“test.
1-6.メモリブロック領域の確保/解放の不一致 【内容】 malloc 等の関数で確保した領域の未解放や、確保していないエリアの解放により、メモリエ リアの整合性が取れなくなる。 【例】 ①同一変数での複数回のエリア確保 unsigned char *area; while(1){ area = malloc(AREA_SIZE); ・ } (解放せずに、再度エリア確保) ②確保していないエリアの解放 unsigned char *area1, *area2; area1 = malloc(AREA_SIZE); ・ free(area1); free(area2); (確保していないエリア area2 の解放を行う) 【対策】 決定的な対策はないが、malloc と free は、極力同一関数内で実行するように し、プログラム作成時にセットで組み込むようにする。 8
1-7.スタックオーバーフロー 【内容】 ローカル変数を大量に確保したり、引数を大量に用いて、アプリケーションで使用可能なス タック領域(2KB)を超えてしまう。 【例】 usr_func(UB *str,struct K_DEF *k_def,int len) { unsigned char s_buf[1024],r_buf[1024]; ・ } (上記の場合、ローカル変数で2048バイト、引数で12バイトのため、2KBを超えて いる) 【対策】 ・変数を static 型やグローバルで確保し、スタックを使わないようにする。 ・関数の引数を極力少なくする(構造体にまとめてしまう、自身の戻り値を用 する、等) ☆なお、コンパイル時にリストファイル(-lオプションにて作成される)を 作成する事で、各関数が使用するスタックサイズが確認できます。 (ソフトウェア解説書の「4.1 スタック領域について」参照) 【対策例】 unsigned char s_buf[1024],r_buf[1024]; usr_func(UB *str,struct K_DEF *k_def,int len) { ・ } 9
1-8.レジュームOFF時のファイルオープン&電源OFF 【内容】 レジュームOFFの状態でファイルをオープンし、クローズしていない状態で電源を切ると、 次に電源を入れた際にINIT ERRORが発生する。 (下記の画面) INIT ERROR R4:00000003 R5:00000000 R6:00000003 【例】 dat_pwr_str.res_md = RESUME_OFF; /* resume off */ dat_system(SYSD_FNC_WRITE,SYSD_PWR,&dat_pwr_str); /* mode set */ ・ fp = fopen(“test.
1-9.通信処理中の電源OFF/ON 【内容】 通信処理を行っている最中に電源をOFF/ONすると、受信バッファにゴミが乗ることが ある。 【例】 c_din(COM0, buf); この関数でデータ受信待ちの最中に電源が切られ、再び電源が入れられると受信バッファ中 にゴミが乗ることがある。 【対策】 ・受信データのチェックを必ず行う。 手組で通信プログラムを作成する場合、プロトコルによりデータのチェック を行うようにする。 【対策例】 1データのパリティビット(偶数or奇数)を入れ、1パケットのデータの後ろに1~2バ イトのチェック用データを付加する。 11
1-10.Xon/Xoffによるビジー制御の注意 【内容】 通信のビジー制御でXon/Xoffによる制御を行う場合、受信バッファのサイズは67 バイト以上で用意する。 【例】 UB r_buf[64]; param=B_19200|PARI_EVN|CHAR_8|STOP_1|RTS_ON|ER_ON|SI_OFF|XON_XOFF; c_open(COM0, param, r_buf, buf_len, &time_out, &del_cod, DC3, DC1); 上記の場合、バッファが64バイトしかないため、Xon/Xoffは正常に機能しません。 【対策】 ・バッファは必ず67バイト以上取る。 12
1-11.キー入力バッファの初期化 【内容】 キー入力関数(key_read、key_string、key_num)に使用する入力用バッファを正常に初期 化していないと、暴走することがある。 【例】 KEY_INPS UB key_inps; abc[21]; … key_string(&key_inps, abc); (キー入力関数は、バッファに格納されている値を表示するが、ここにゴミが入っていた場 合、暴走することがある) 【対策】 NULLクリアを行うか、妥当な値を入れて置く。 【対策例】 UB abc[21]; … memset(abc, ‘\0’, sizeof(abc)); key_string(&key_inps, abc); 13
2.特定関数の使用上の注意点 2-1.flg_sts 【内容】 通知モードで設定したフラグの状態を確認する関数ですが、永久ループ内では、この関数のみ で脱出条件を設定することは避け、別の脱出条件(キー入力等)を入れるようにして下さい。 DT-700では、タイミングによって、内部的にフラグがマスクされるケースがあります。 そのため、フラグの状態が変わらず、同関数のみを脱出条件にした永久ループから抜けなくな ります。 ただし、待ちの入る関数(key_read、wai_flg、c_din 等)を呼ぶことで、マスク状態は解除さ れますので、永久ループに入る直前(もしくはループ内)で、これらの関数を呼んでいただく ことで永久ループから脱出できなくなる危険性は無くなります。 【プログラム例】 ・悪い例 t_id=s_settimer(FL_TM2_INT_ID, FL_TM2_INT_ITU0, 3); while(1){ flg_sts(&dumy, &ptn, FL_TM2_INT_ID); if (ptn & FL_TM2_INT_ITU0) break; } ・良い例 t_id=s_settimer(FL_TM
2-2.OBR_gets 【内容】 読み込んだバーコードリーダをバーコードバッファから取り出す関数です。 この関数は、正常に終了すると与えられた3つの引数(文字列、読み取りコードの種類、読み 取り文字数)にデータを設定して返しますが、この関数の実行と同時に電源キーが押されると、 この関数はコードの種類に「データ無し」、読み取り文字数に0を入れて返し、文字列のバッ ファはNULLクリアしています。 これは、電源OFF処理時の状態で読み取ったデータが不定なためです。 電池の電圧が低下した状態でバーコード読み込みを行った際に、電源が切れることがあります が、この場合にも上記の内容は適用されます。 この関数の実行後、読み取ったコードの種別や文字数のチェックをせずに処理に用いると、不 具合が発生することがありますので注意して下さい。 【プログラム例】 ・悪い例 OBR_gets(buf, &rcd, &obrlen); buf[obrlen-1] = ‘¥0’; ・良い例 OBR_gets(buf, &rcd, &obrlen); if (obrlen>0) { buf[obrlen-1] = ‘¥0’; ・ ・
最終ページです 16