xmegaのstrstr関数で文字検索

このQ&Aのポイント
  • ATMEL社のATxmega128A1というCPUでプログラミングを行っています。テンキーパネルでのコマンド操作機能を設置しており、#ボタンが押されるとkey_proc関数が呼ばれます。
  • key_proc関数では、buf引数には#ボタンが押されるまでに押されたボタンのキャラクタデータが収納されるSRAMの番地が格納されます。
  • 質問者は、buf内の特定の条件を満たす場合に、EEPROM内のデータを変更するプログラムを作成したいと考えています。しかし、sprintf関数を使用して文字列を生成しようとすると、プログラムが暴走して正常な動作ができなくなってしまいます。
回答を見る
  • ベストアンサー

xmegaのstrstr関数で文字検索

現在ATMEL社のATxmega128A1というCPUでプログラミングを行っております。 テンキーパネルでのコマンド操作機能というのを設置しているのですが、テンキー操作で、”#”ボタンが押されると u8 key_proc(u8 * buf,int length) この関数が必ず呼ばれます。 bufという引数に”#”ボタンが押されるまでに押されたボタンのキャラクタデータが収納されたSRAMの番地が格納されています。 例えば、端末の設定などを変更したい場合に使用する4文字のキーがEEPROMに格納されていて、それを変更したい場合のコマンドを追加したいと思っています。 そのEEPROMに格納されている4文字キーというのは (u16)gEnv.main_set.key; というEEPROMに”1234”というデータが入っているのですが、 bufというSRAMのメモリに例えば”1234Z4321#” このようなコマンドが来たらもともと”1234”という現在のkeyと合致して、なおかつ次の文字が”Z”という文字が来たら、次の”4321”というデータに(u16)gEnv.main_set.key;のEEPROMの内容を変更するというプログラムが作りたいと思っています。 現在このようなプログラムでこの機能を実現しています。 #if 1 pass16 = (u16)gEnv.main_set.key; sprintf((char *)str,"%04d",pass16); str[4] = 0; memcpy(value,buf,4); if(strstr((char *)value,(char *)str) != 0) { if(buf[4] == 'Z' && buf[9] == '#') { value[0] = buf[5]; value[1] = buf[6]; value[2] = buf[7]; value[3] = buf[8]; gEnv.main_set.key = strtoul(value,NULL,10); env_save(&gEnv); return true; } } #endif このプログラムを作ったことで疑問に思っていることなのですが、 buf[4] == 'Z' であるかどうかをstrstrとは別のコードで判定しているのですが、 sprintf((char *)str,"%04dZ",pass16); このようなコードで"1234Z"という文字列をstrstr検索して判定することはできないのでしょうか? 現在”sprintf((char *)str,"%04dZ",pass16);”としてしまうと、プログラムが暴走したような状態になり、正常な動作ができなくなってしまう現象が出てしまいます。 どうぞ、ご教示よろしくお願い致します。

質問者が選んだベストアンサー

  • ベストアンサー
  • chie65535
  • ベストアンサー率43% (8519/19367)
回答No.4

>sprintf((char *)str,"%04dZ",pass16); >この記述が1行入っただけで暴走してしまうのか大変不思議に思っております。 sprintfの書式指定の"Z"が「長さ修飾子」として機能していると思われます。 sprintf((char *)str,"%04d\x5a",pass16); として下さい。 なお、printf系関数の「書式を書く部分」に sprintf((char *)str,"%04dM1#",pass16); sprintf((char *)str,"%04dZ",pass16); のように「特定の文字、文字列を指定する」のは「書式の修飾子として判断される危険があり、意図した結果にならない場合がある」ので、事故の元です。 こういう「思わぬ事故」を防ぐ為、多少、冗長になってしまうのを承知の上で sprintf((char *)str,"%04d%s",pass16,"M1#"); sprintf((char *)str,"%04d%s",pass16,"Z"); と書くようにしましょう。

techhouse
質問者

お礼

回答頂きありがとうございます。 解決しました。助かります。

その他の回答 (3)

  • wormhole
  • ベストアンサー率28% (1620/5655)
回答No.3

>memcpy(value,buf,4); >if(strstr((char *)value,(char *)str) != 0) strの検索対象であるvalueは検索直前にbufから4バイトコピーしていますが、 valueはNUL文字分含めた最低5バイト分ありますか? またvalue[4]はNUL文字ですか?("Z”を含めないで検索する場合の話です) あと、strstrではなくstrcmpでいいような。 さらにはvalueにコピーして検索せずにmemcmpでいいような pass16 = (u16)gEnv.main_set.key; sprintf((char *)str,"%04dZ",pass16); // str[4] = 0; 元々これは不要 if(memcmp(buf, str, 5) == 0 && buf[9] == '#') { memcpy(value, &buf[5], 4); value[4] = '\0'; gEnv.main_set.key = strtoul(value,NULL,10); env_save(&gEnv); return true; }

techhouse
質問者

お礼

回答頂きありがとうございます。 サンプルコード頂きありがとうございます。助かります。 #if 1 pass16 = (u16)gEnv.main_set.key; sprintf((char *)str,"%04dZ",pass16); if(memcmp(buf,str,5)==0 && buf[9]=='#') { memcpy(value,&buf[5],4); value[4] = '\0'; gEnv.main_set.key= strtoul(value,NULL,10); env_save(&gEnv); return true; } #endif このコードに差し替えてコンパイルして実行してみたのですが、 前回と同様の暴走したような動作をしてしまいます。 結局、次のソースコードに戻して動作させています。 なお、memcpy(value,buf,4);でブレイクをかけて value[4]の値を調べたところ"0x82"となっていました。 #if 1 pass16 = (u16)gEnv.main_set.key; sprintf((char *)str,"%04d",pass16); //sprintf((char *)str,"%04dZ",pass16); //str[5] = 0; memcpy(value,buf,4); if(strstr((char *)value,(char *)str) != 0) { if(buf[4] == 'Z' && buf[9] == '#') { value[0] = buf[5]; value[1] = buf[6]; value[2] = buf[7]; value[3] = buf[8]; gEnv.main_set.key= strtoul(value,NULL,10); env_save(&gEnv); return true; } } #endif

  • chie65535
  • ベストアンサー率43% (8519/19367)
回答No.2

>プログラムが暴走したような状態になり char str[5]; とかって定義になっていれば「4文字+EOS」の分しかメモリが無いので、5文字以上の文字列をstrの場所に書き込むと暴走します。

techhouse
質問者

お礼

回答頂きありがとうございます。 もう一度、今回の関数を見直してみました。 strの配列が少なくて今回の現象が起きているようではないようです。 この"key_proc"関数内には sprintf((char *)str,"%04dM1#",pass16); if(strstr((char *)buf,(char *)str) != 0) このようにして判定をしている箇所もあるのですが、これでは暴走するような現象は起きていないのに、 sprintf((char *)str,"%04dZ",pass16); この記述が1行入っただけで暴走してしまうのか大変不思議に思っております。 u8 key_proc(u8 * buf,int length) { ----------(中略)---------------------------------------- u8 str[11]; ----------(中略)---------------------------------------- pass16 = (u16)gEnv.main_set.service_code; sprintf((char *)str,"%04dM1#",pass16); str[7] =0; if(strstr((char *)buf,(char *)str) != 0) { devicePairingNumber = OPER_PAIRING_REMO_DRIVER; oper_insert(OPER_PAIRING_REMO_TESTOPER,1); return true; } ----------(中略)---------------------------------------- }

  • wormhole
  • ベストアンサー率28% (1620/5655)
回答No.1

strの型はどうなってるんですか?

techhouse
質問者

お礼

回答頂きありがとうございます。 strの型ですが、 u8 str[11]; このように宣言しています。

関連するQ&A

  • sprintf

    文字列に1文字を結合したいんだけど、 例えば  char str[] = "xy";  char c = 'z'; があって、2つを結合する場合、 #include <iostream.h> main(){  char str[] = "xy";  char c = 'z';  char buf[2];  buf[0] = c;  buf[1] = 0;  strcat(str, buf);  cout << str; } でもいいけど、もっと簡単にする方法があったら教えてください。  char str[] = "xy";  char c = 'z';  sprintf(str, "%s%c\0", str, c);  cout << str; イメージ的にこうだけど、失敗しました。

  • sprintfを用いたフォーマット文字列攻撃

    sprintfを用いたフォーマット文字列攻撃に関する質問です。 main文の中で --------------------- int main(void){ char str[50]; sprintf(str,"%s"); puts(str); } --------------------- を実行すると(null)で帰ってきます。 しかし関数呼び出しを行うと結果が変わってきます。 --------------------- void f(){ char str[50]; sprintf(str,"%s"); puts(str); } int main(void){ f(); } --------------------- これを実行すると" ・L "のような文字化けしたものに変わりました。 sprintf(str,"%s%s");と記述することによって" ・L (null) "と、本来呼び出される箇所のものが格納されています。 なぜ関数呼び出しにすると変なところを参照してしまうのか(第3引数があると勘違いしてしまうのか)教えてください。 また、これによりどのような悪影響があるのか教えてください。 自分はプログラマが意図しない箇所を参照するため攻撃者のプログラムアドレスを格納してしまう恐れがあると考えています。

  • strstrを利用しない文字列検索について

    キーボードから入力した文字列 str に対し, This is~の文字列中に str が出現するかどうかを判定して表示するプログラムを作成したいのですが、うまくコンパイルができません。 条件ですが、、、 ・forループを利用し、0文字目、1文字目と順に検索していく ・strstr関数およびstring.hを利用しない です。このプログラムをいじって教えてほしいです。 ややこしくて答えづらい質問かと思いますが、有識者の知恵を拝借したいです。よろしくお願いします。 #include <stdio.h> int main(void) { char str[256]; char s[] = "This is a pen. That is an apple."; int i,j,k,checker; printf("This is a pen. That is an apple.\n\n"); gets(str); for(j=0;j<256;j++) { for(i=j,k=0;str[k]!='\0';k++,i++) { if(str[k]==s[i]) { checker=1; } else { checker=0; } } } if(checker==0) { printf("NG!その文字列は含まれていません。\n"); } else { printf("OK!その文字列は含まれています。\n"); } }

  • C言語 strstrの実装

    課題で strstr()と同じ働きをするmy_strstr()を実装しなさいという課題がでています。 strstr()は char *strstr(const char *s1, const char *s2) という形で、 文字列s2が文字列s1の中に含まれていれば、文字列1の文字列2が含まれている先頭のアドレス(ポインタ)を返す関数です。(すいません、説明が下手で) 例えば、 strstr(Japanese, pan)は Japanese にpan が 含まれるのでpan 以降の文字列を返します→panese *********************** 質問ですが、 strstrの実装は完成して、正常に動くのですが 警告がでてきます。 str1= Japanese str2 = pan とすると、 私のプログラムでは、次のようなプロセスで動きます。 一文字ずつ調べる ↓ str1のJと str2の pを比較        J != p → str1++; //str1の次の文字へ str1のaと str2の pを比較        a != p, str1++; //str1の次の文字へ str1のpと str2の pを比較        p == p, rec = str1 // str1のアドレスを記録         str1++; str2++; //次の文字へ str1のaと str2の aを比較        a == a, str1++; str2++; //次の文字へ str1のnと str2の nを比較        n == n, str1++; str2++; //次の文字へ str1のeと str2 nullを比較    *str2 = null //search 終了。 return rec; とこんな感じです。 このプロセスの3行目の rec = str1; つまり、関数の戻り値のために記録したstr1のところで、 「問題のあるポインタ変換」 という警告がでます。 これは s1がconstだからでしょうか? どのようにしたらこの警告が消えるでしょうか? どなたかよろしければアドバイスをいただけませんか。 お願いします。

  • strtoul関数について

    現在Atmelマイコンでテンキーパネルのクリックで得られた数値データを使って、パスワードなどの数値と照合して処理を行うようなプログラムを作成しています。 u8 key_buf[21]; パスワードを1回ごとにクリックしていくとこのkey_buf配列のkey_buf[0]から順に0x30('0')~0x39('9')の文字列の番号が代入されます。 照合したいパスワードデータは u32 pass  というデータ変数でpass = 0x00001234    このようなデータが入っています。 このデータを比較して同一かどうかを調べるために次のようなプログラムを作成しました。 test_id_data = strtoul(key_buf,NULL,10); if(key_index != 0 && test_id_data == pass) {       PASS同一と判定 } しかし、このプログラムではkey_bufの配列がなんであろうとif文条件では全部同一と判定されてしまいます。 試しに、 test_id_data = strtoul((char *)key_buf,NULL,10); if(key_index != 0 && test_id_data == pass) {       PASS同一と判定 } このようにすると、key[0]=0x31,key[1]=0x32,key[2]=0x33,key[3]=0x34 これとpass=0x00001234 は同一と判定して、それ以外だとfalseと分岐されました。 なぜ、(char *)と入れないと意図した動作をしたのかがよくわかりません。 どうぞ、ご教示頂きますよう、お願い致します。

  • sprintf関数の使用法について

    sprintf関数の使用法がまずいようで、実行すると Segmentation faultエラーが発生します。 コードは以下のとおりです。 main(){ char buf[100]; int h,m,s; h=12; m=30; s=47; sprintf(buf,"%s:%s:%s",h,m,s); } bufに時分秒をコロン区切りで格納したいの ですが、どうすれば良いのか教えて下さい。

  • C言語:小文字を大文字に変換する関数を作成

    C言語超初心者です。学校の課題で次のような問題が出されました。 ・問題・ 次に挙げる縛りに沿い、以下の関数とメイン関数を作り、処理結果を画面に作成しなさい。 char *tst(char *str) ・strの中の文字列も小文字を大文字に変換し、変換した文字列が格納されているchar *に返す。 ・引数strの中の文字列は受け取った状態で手を加えない。 ・関数内でmalloc関数を使用し、領域を確保して大文字に変換した文字列を格納しreturnでポインタを返す。 ・malloc関数を使用する。 ---------------------------------------------------------------------------------- 大文字に変換するには while(*str != '\0') { if(*str >= 'a' && *str <= 'z') { *str -= 'a'- 'A'; } ++str; } というのは分かったのですがここから何をすするか全く分かりません。初心者なのでなるべく分かりやすく教えてもらえると有難いです。 お願いします。

  • mainから渡した文字列を関数内で書き換え

    非常に基礎的な質問で申し訳ないのですが mainから渡した文字列を関数内で書き換えることができません。 int型の整数やchar型一文字はできるのですが。。。 例えば以下のようなソースでmainのABCをDEFに書き換えたいとき どのようにすればいいのでしょうか。 (関数の戻り値で変更という方法以外で) 以下のソースでは値は書き換わりませんでした。 void func(char *str2) { str2 = "DEF"; } int main() { char str1[20] = "ABC" printf("%s", str1); //ABC func(str1); printf("%s", str1); //DEFになるようにしたい }

  • プログラミング(関数reverseを作る)

    プログラミングの勉強会で文字列を反転させる関数(例:ABCDE→EDCBA)を作れという課題が出たのですが、以下のように書いたきり進みません。あと、何を書けばいいのでしょうか?教えてください。 #include <stdio.h> void reverse(char *); void reverse2(char *); void main(){ char buf[10]; printf("str :"); fgets(buf, 10, stdin); reverse(buf); printf("rev :%s", buf); return; } //文字列を反転させる関数 void reverse(char *s){ return; }

  • int型変数をchar型変数に格納する方法

    int型数値をchar型変数に格納したいのですが、 sprintf()とatoi()を用いてやりましたが、 うまくいきません。 考えたコードを下記します。 int get_y(){ int y=2005; return y; } int main(){ char year; char str[5]; //intをcharに変換して格納している sprintf(str,"%d",get_y()); //数値として代入 year=atoi(str); //yearには2005が格納されていない! cout<<year<<endl; char ans; //このchar型変数に数値を代入する。 ans=year; cout<<ans<<endl; return 0; } お手数をお掛けしますが、 よろしくお願い致します。

専門家に質問してみよう