2021年12月5日日曜日

キャストの挙動が変わった?いつから?

「プログラミング基礎」の授業で,整数の補数表現を理解するために,次のプログラムを動かして確認するという演習を課している.

#include <stdio.h>


int main() {

  unsigned char x = 0;

  do {

    printf("unsigned: %4d "

           "signed: %4d\n", x, (char)x);

  } while (x++ != 255);

  return 0;

}

実際にmacOSで動かしてみると次のような出力になる.

$ gcc file01.c -o file01

$ ./file01 

unsigned:    0 signed:    0

unsigned:    1 signed:    1

unsigned:    2 signed:    2

unsigned:    3 signed:    3

(中略)

unsigned:  125 signed:  125

unsigned:  126 signed:  126

unsigned:  127 signed:  127

unsigned:  128 signed: -128

unsigned:  129 signed: -127

unsigned:  130 signed: -126

(中略)

unsigned:  252 signed:   -4

unsigned:  253 signed:   -3

unsigned:  254 signed:   -2

unsigned:  255 signed:   -1

$

WindowsのとMacのPCをBYODで持ち込む学生を相手に「プログラミング基礎」の授業をおこなっているので,Cのプログラミングは仮想環境のUbuntu Linuxで教えている.これまで,Ubuntuのgccでも上記と同様の動作を示すので,これまでは問題なかった.

ところが,今年やってみると,どうもうまくいかない(図).

どうも(char)で符号付きchar型にキャストしてくれない様子.これまでの動作,あるいは先に示したmacOSでの挙動と同様とするには,(signed char)と明示的に符号付きだと示さないといけないようになったらしい.

#include <stdio.h>


int main() {

  unsigned char x = 0;

  do {

    printf("unsigned: %4d "

           "signed: %4d\n", x, (signed char)x);

  while (x++ != 255);

  return 0;

}

いつ,そんな変更があったんだろう.知らなかった.けっこう重要な仕様変更だと思うけど,困ってるひといないのかなあ?

追記:

もともとCの仕様としては,char を signed として扱うか unsigned として扱うかは決められていない,ということだと教えていただきました.なので,明示的に (signed char) とキャストすべし,という対応が正解だとのこと.勉強になりました.

2 件のコメント:

  1. https://gcc.gnu.org/onlinedocs/gcc-5.3.0/gcc/C-Dialect-Options.html#C-Dialect-Options

    Each kind of machine has a default for what char should be. It is either like unsigned char by default or like signed char by default.

    とあるので、最初からsigned charを期待してはならないというのが正解だと思います。

    返信削除
    返信
    1. コメントありがとうございます.
      そうですね.勉強になりました.

      削除