charとuint8_tは違う?

マイコンのプログラムを書いていたが、そろそろ終わりが見えてきた?

来週中にはある程度動くものに仕上げたいところだが……


比較的後回しになっていた機能として製造検査・メンテナンス用のコンソールがある。

コマンド名を識別するにはこんなコードを書くわけですよね。

if(strcmp(cmdname_buf, "FOO")==0){

そしたらcmdname_bufの型がconst char*と互換性がないというワーニングが出る。

形式的な問題であるのは明らかなのだが、なんでそんなこと言われたんだろ。


cmdname_bufは uint8_t型の配列である。これがchar型と互換性がないという指摘である。

ただ、実態としては同じもののはずなんですよね。

どちらもサイズは1バイトなので一致を比較する点では差はない。

char型は符号無し設定になっているわけだから、表せる値も同じである。


このあたりは処理系によるところもあるが、GCCで実験してみた。

_Generic文を使うことで型を識別することができる。

#define TYPESTR(x)  _Generic((x), uint8_t:"uint8_t", int8_t:"int8_t",default: "other")

これを使って unsigned char, signed char, char型を識別すると、

それぞれ uint8_t, int8_t, other に分類された。

なんとchar型は設定によりsigned charかunsigned charと切り替わるわけではなく、

どちらでもない型で扱われているのだという。これには驚いた。


もちろんこれはコンパイラ次第という話ではある。

ただ、文字を表すことを主眼に置いているchar型と、

数値を表すことを主眼に置いている int8_t, uint8_t型は分けて考えている場合があると。

そしてstrcmpは文字列の比較を想定したものなので、

数値の型を持ってくるとちょっと違うとなってしまうようである。


一方が文字列リテラルなので実質問題ないような気もするが、

strcmpは終端が正しくNULL文字になっていないとバッファオーバーランの危険もある。

そういう事情も考えれば専用の比較関数を作った方がいいかもなと思い出してきた。

そうすると今度は文字列リテラルとconst uint8_t*の変換で何か起こるかも知れないけど。

明示的にキャストすればいいんですけどね。


今日でドキュメント作業が片付いてやれやれと思ったのだが、

最初にドラフト版に書いた日付を見ると1ヶ月前で、えらいかかったなと。

他のドキュメントの改訂とかもやってたので時間がかかるのも仕方ないが、

当初の見積もりに比べるとちょうど1ヶ月遅れかなぁと。

これが終わったらその次にはやらないといけないこともあるけど、とりあえずは動くことが優先ですね。