アプリ版:「スタンプのみでお礼する」機能のリリースについて

PIC18F14K50のマイコンでPCとUARTで通信するように、ソフトを作成中ですが、なぜか、送信のためにマイコンの変数P[1]-[24]にある1バイトの数値を16進数文字列に変換し始めると、変数に入っている数値が0になってしまいます。
原因のわかる方、0にならない対処法を教えて下さい。
開発環境はMPLABX XC8で行っています。
以下に送信部分のソースを示します。

if(PP){ // PPが1で変換、送信 
Txdata[0] = '\0'; //送信文字列クリア
strcat(Txdata,"Z");// 文字列先頭に"Z"を挿入
if(P[1]<16){strcat(Txdata,"0");}
sprintf(str, "%x",P[1]);//P[1]を16進に変換
strcat(Txdata,str);//Pa\r\nを付け加える
if(P[2]<16){strcat(Txdata,"0");}
sprintf(str, "%x",P[2]);strcat(Txdata,str);
if(P[3]<16){strcat(Txdata,"0");}
sprintf(str, "%x",P[3]);strcat(Txdata,str);

--- 中 略 ----------
if(P[23]<16){strcat(Txdata,"0");}
sprintf(str, "%x",P[23]);strcat(Txdata,str);
if(P[24]<16){strcat(Txdata,"0");}
sprintf(str, "%x",P[24]);strcat(Txdata,str);
strcat(Txdata,"\r\n");
     strOut(Txdata);//送信
Txdata[0]='\0'; PP=0;END=0;}

//**** 文字列送信関数 ****************************
void strOut(char *str){
while(*str){
while (!PIR1bits.TXIF); //送信終了待ち
TXREG = *str++; } } //文字出力しポインタ+1

質問者からの補足コメント

  • どう思う?

    P変数は unsigned char P[30] =0; で宣言しています。
    因みにPCから送信される0-255の値を代入して、P[]の値をEEPROMの各アドレスの1byteに書き混んでいますが、正常に書き込まれています。

    文字変数を扱うのは難しいですね ><

    「if(P[24]==0){LED=1;}で調べました」は言葉足らずですいません。この判定分をP[]が使用される場所(PCからデータを受信してP[]に代入した後、P[]をEEPROMに書き込んだ後、P[]を文字変換して送信する箇所)にいれて、LEDが点灯する箇所==P[]が0に変化する箇所と考えてサーチしました。
    結果として、今回アップしたP[]を変換、送信のソース部分で変化しています。
    また、変化はif(PP){if(P[24]==0){LED=1; でLEDが点灯しています。

    No.5の回答に寄せられた補足コメントです。 補足日時:2018/01/14 09:25
  • うーん・・・

    因みにP[1] P[2]より、最後のP[24]のほうが0に変化する頻度が高いです。また、入っている数字が
    255が0になりやすいです。

      補足日時:2018/01/14 10:57

A 回答 (7件)

%x は「unsigned int 型の引数を 16進で出力する」書式指定だから, 対応する引数はせめて unsigned char であってほしい. つまり P を


char P[なんとか];
のように定義しているなら「char が符号なしである」ことを確認しなきゃいけない (plain char が符号付きか符号なしかは処理系定義).

unsigned char 型の引数に対応するなら書式は %02xhhx とするのが今の ISO C としては正しいのだが, 処理系が対応してるかなぁ.

あぁ, %02x だと
・最低 2桁
・不足分は 0 付加
なので, 「絶対に 2桁になる」という保証はないっす.
    • good
    • 0
この回答へのお礼

回答ありがとうございます。記入していませんでしたが、P[]は0-255に制限しておりますがそれでも2桁以上になりますか?また、それにより、0に変化しますか?P[]に値が代入されるときに、設定がおかしくて0になるのであれば、わかりますが今回0に変化するのはP[]からほかの変数に代入するときに発生しています。P[],str,Txdataの変数の設定がまずいとして、P[]からほかの変数に代入した場合でも、P[]は変化するのでしょうか?

お礼日時:2018/01/14 10:51

あと…


>if(P[1]<16){strcat(Txdata,"0");}
>sprintf(str, "%x",P[1]);//P[1]を16進に変換
>strcat(Txdata,str);//Pa\r\nを付け加える
としなくても、
sprintf(str, "%02x",P[1]);
でP[1]が5だったら"05"になるかと思われますよ。
ライブラリの実装にもよりますけど。
# そういう指定もできないヘタレ実装ならunsigned charから16進数文字列2桁に変換する処理を自前で作るべきかと。
    • good
    • 0
この回答へのお礼

了解です。ところで今回の私の質問はそこを改善すれば、解決するということですか?
行ってみましたが、かわりませんでした。なぜですか?

お礼日時:2018/01/14 10:45

>strとかTxdata等にサイズが小さい場合、P[]が0になってしまう可能性がありますかね?



バッファオーバーフロー/バッファオーバーランとなれば破壊する可能性はあるでしょう。

>ご指摘の記述でも送信バイト数はふえないのでしょうか?

意図しないサイズで出力される。
というのを防げます。
コンパイラに依存しますがint型が16bitだった場合、かつ明示しない場合にsigned intとして扱われる。という状況だったときに
P[1]に255が入っている場合に
sprintf(str, "%x",P[1]);
でstrに"ffff"が入るかも知れません。
P[1]の11111111bが符号拡張で1111111111111111bに。
16進数文字列への変換が指定されているのでffffに。
# 当然文字列なので、char str[4]には収まりませんのでめでたくバッファオーバーラン成立となります。

>変化するのはP[ ]のマイコン内の変数で

ちなみにP[]はどのような?
P[0]が出てこないのは何か理由あるんでしょうか?

>送信側にデータを吐き出す変数が0になっています。
で、「if(P[24]==0){LED=1;}で調べました」なんですか???
この回答への補足あり
    • good
    • 0

変数 P の定義は char型でしょうか?


サイン型の変数を sprintf の引数を指定し、その値がマイナスの数値である場合 "%x" での変換結果の桁数が 8桁になります。 (char型=1byteの変数であっても)
よって、char str[4] (4byte)、char Txdata[60] (60byte)でも十分なサイズではありません。
少なくとも str は 8+1=9byte 必要で、Txdata は (1+8) x 24+1=217byte 必要です。
("%x"を"%02x"などとすれば、必ず2桁分しか生成されないと言う方もいますが、これは間違いです)
    • good
    • 0
この回答へのお礼

回答ありがとうございます。
P変数は unsigned char P[30] =0; で宣言しています。
因みにP[]もstrも0-255の1byteで制限をかけて、使用するようにしているつもりです。PC側から送信するデータへも制限をかけています。このデータは制御に使用するので、EEPROMに書き込んで使用するため、1変数1アドレスに書き込めるように扱っているつもりです。

お礼日時:2018/01/14 09:10

もう少し全容がわかるソースコードが必要ですが、そのような現象の一般的な問題はバッファーオーバーフローが原因であることが多いです。


変数 Txdataとstrはどのように定義していますか? これらの変数のサイズが小さいことによる問題の可能性を疑います。
    • good
    • 0
この回答へのお礼

回答ありがとうございます。今回の問題は送信側なのですが、バッファーオーバーフローが発生するのでしょうか?
また、char str[4]; 4byte  char Txdata[60]; 60byte 取っています。 strは2byte,Txdataは50byte程度と思いますが?
そして、変化するのはP[ ]のマイコン内の変数で、ソース内を検索しても、書き換える部分が存在しないタイミングで変わっているので理解できません????書き換わるタイミングをif(P[24]==0){LED=1;}で調べましたが、アップしているソース部分でした。xc8のバグ?

お礼日時:2018/01/13 20:31

ああ、あと…


>sprintf(str, "%x",P[1]);
%xではなく%2x等のように何桁で出力するのかは明示した方がいいかもしれません。
# signed int型に拡張されて思わぬ桁数で出力されているかもしれませんし。
    • good
    • 0
この回答へのお礼

ご指摘ありがとうございます。送信バイト数をできるだけ、減らしたかったので、1バイト数値を16進数文字列の2バイトにしてと思って記述していましたが、ご指摘の記述でも送信バイト数はふえないのでしょうか?

お礼日時:2018/01/13 20:36

>sprintf(str, "%x",P[1]);//P[1]を16進に変換


>strcat(Txdata,str);//Pa\r\nを付け加える

とりあえず、2つめのコメントに書かれている挙動にはならないかと思われますがその辺りはどうなんでしょう?
あと、strとやらには十分なサイズの領域確保されてます?
# char str[2];だと不足しますけど。

>変数に入っている数値が0になってしまいます。

というのは「どの変数」のことですかね?
    • good
    • 0
この回答へのお礼

回答ありがとうございます。char str[4]; 4byte  char Txdata[60]; 60byte 取っています。
今回問題となっている変数はP[24]で入力される変数ではなく、送信側にデータを吐き出す変数が0になっています。
strとかTxdata等にサイズが小さい場合、P[]が0になってしまう可能性がありますかね?

お礼日時:2018/01/13 20:45

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!