SVX日記

2004|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|

2016-10-04(Tue) sdccのcharがunsignedになっててドハマり物語

  ラズベリーパイとPIC間でI2C通信を成立させたくて、PIC用にI2Cスレーブのスタックを、純ソフトウェア実装で書いていたのだが、スレーブとしてとりあえずの受信に成功したところで、ライブラリ化するため、コードを分割しようとしたところ、これがどうにも動かなくなってしまった。

  どう考えてもロジックに誤りが見つからないので、苦し紛れに、ライブラリ化の際、ついでにchar型に変更した部分をunsigned charに戻したところ、動くようになった……って、ナニそれ!?

  生成されたアセンブラコードに降りていくと、確かに符号付きとして数値比較しているらしきコードが見当たらない。すわコンパイラのバグか!?と、思って「sdcc signed char」でググってみたところ、charはunsignedとして扱われる旨の情報がある。ウッソ!? いつからッ!?

News
  June 12th, 2016: Small Device C Compiler 3.6.0 released.
    SDCC 3.6.0 Feature List:
      char type is now unsiged by default (old behaviour can be restored using --fsigned-char)

  ……って、いまさっきからなのかよッ!!

$ cat test.c 
#include <stdio.h>
 
void main() {
	char           x = -1;
	signed char   sx = -1;
	unsigned char ux = -1;
 
	printf("  %lu %x\n", sizeof(x),  (unsigned char)x);
	printf("s %lu %x\n", sizeof(sx), (unsigned char)sx);
	printf("u %lu %x\n", sizeof(sx), (unsigned char)ux);
 
	printf("  %x\n",  x > 0);
	printf("s %x\n", sx > 0);
	printf("u %x\n", ux > 0);
}
 
$ ./a.out 
  1 ff
s 1 ff
u 1 ff
  0
s 0
u 1

  当然ながら、gccならcharはsignedだ。

  「0, 1, stop condition」の3値を返す関数だったので、オシャレに負数を返すように変更したことが裏目に出た。しかし、char型がデフォルトでunsignedなんてCの仕様として許されないんじゃねーのか? と、K&Rの第2版をパラパラ……「このcharが符合付きか符合なしかは機種によるが……」なんですか、そーでしたか……私が悪うござんした。

  ちなみに、最新版のマニュアルでは以下のようなコンパイルオプションの説明がある。

--fsigned-char
By default char is unsigned.
To set the signess for characters to signed, use the option --fsigned-char.
If this option is set and no signedness keyword (unsigned/signed) is given, a char will be unsigned.
All other types are unaffected.

  以前は、ちょうど逆のコンパイルオプションがあったようだ。

--funsigned-char
The default signedness for every type is signed.
In some embedded environments the default signedness of char is unsigned.
To set the signess for characters to unsigned, use the option --funsigned-char.
If this option is set and no signedness keyword (unsigned/signed) is given, a char will be signed.
All other types are unaffected.

  単にcharと書かず、signed char, unsigned charと明示的に宣言しておけば間違いはないようだ。

  しっかし、この変更、ホントに必要だったんかなぁ。これまで書いたコードが動かなくなりそうでイヤやわ。とりあえず、自分が忘れないように、ここにしっかりとメモしておく。ほんっと、まったくもぅ、まったくもぅ、まったくもぅだよ、まったくもぅ。