掲示板利用宣言

 次のフォームをすべてチェックしてからご利用ください。

 私は

 題名と投稿者名は具体的に書きます。
 課題の丸投げはしません。
 ソースの添付は「HTML変換ツール」で字下げします。
 返信の引用は最小限にします。
 環境(OSとコンパイラ)や症状は具体的に詳しく書きます。
 返信の付いた投稿は削除しません。
 マルチポスト(多重投稿)はしません。

掲示板2

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧

No.27822

h3X
投稿者---型変換(2006/08/04 02:00:33)


環境はLinux, gccです.

型変換について質問させて頂きます.

「コード1」を実行すると,「1」と表示されます.

[コード1]
#include <stdio.h>

int main(void)
{
    if (-1 < (unsigned char)1) {
        printf("1\n");
    }
    else {
        printf("2\n");
    }

    return 0;
}


「コード2」を実行すると,「2」と表示されます.

[コード2]
#include <stdio.h>

int main(void)
{
    if (-1 < (unsigned int)1) {
        printf("1\n");
    }
    else {
        printf("2\n");
    }

    return 0;
}

「コード1」も「コード2」も結果は「1」と表示されて当然だと思っていました.
しかし結果は違っていました.
どういう事が起こっているのか理解できません.

またもうひとつ理解出来てないことがあるのですが,
定数「0x12」は「0x00000012」であり,16進数定数は常に正だと思っているのですが,
間違った認識でしょうか? 例えば,

(例1)
unsigned char a[10];

if (a[0] & 0x12)


のような場合型変換は起こっているのでしょうか?

(例2)
char a[10];

if (a[0] & 0x12)


のような場合は,a[0]がまず何かに型変換されてから0x12とANDが取られるのでしょうか?
またa[0]が型変換されるのなら,何の型に変換されるのでしょうか?

頭の中が混乱しています.
ご教授のほどよろしくお願い致します.


この投稿にコメントする

削除パスワード

発言に関する情報 題名 投稿番号 投稿者名 投稿日時
<子記事> 訂正:型変換 27823 h3X 2006/08/04 02:02:14
<子記事> Re:h3X 27824 ruby 2006/08/04 09:59:08


No.27823

訂正:型変換
投稿者---h3X(2006/08/04 02:02:14)


すみません.

投稿者名と題名を逆にして投稿してしまいました.


この投稿にコメントする

削除パスワード

No.27824

Re:h3X
投稿者---ruby(2006/08/04 09:59:08)


こんな風に考えました。誤っている箇所やあいまいな表現をしている箇所が
あるかもしれませんので、どなたかからご指摘があるでしょう。

>[コード1]
>
>     if (-1 < (unsigned char)1) {

両辺の型が、左辺:int, 右辺:unsigned charで異なっているため、
intにそろえられます。その結果、
    if ((int) -1 < (int) 1) {

という比較を行なうことになり、条件が真となるため、「1」と出力します。

>[コード2]
>
>     if (-1 < (unsigned int)1) {

両辺の型が、左辺:(signed) int, 右辺:unsigned intで異なっているため、
unsigned intにそろえられます。
このとき、左辺は32ビットすべてがONである4294967295となります。
その結果、
    if (4294967295 < 1) {

という比較を行なうことになり、条件が偽となるため、「2」と出力します。



この投稿にコメントする

削除パスワード

No.27825

Re:型変換
投稿者---h3X(2006/08/04 10:15:10)


ご返信ありがとうございます.

>>[コード1]
>>
>両辺の型が、左辺:int, 右辺:unsigned charで異なっているため、
>intにそろえられます。

とありますが,比較も演算の内の1つなので算術型変換が行われてから,
比較するということでしょうか?

では,次の場合はどう考えたらよろしいのでしょうか?
結果は「1」と表示されました.

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    if ( (signed char)-1 < (unsigned char)1) {
        printf("1\n");
    }
    else {
        printf("2\n");
    }
}

自分なりに考えると,まず「汎整数拡張」により比較演算の前に双方intに型変換される.
その結果,左辺:(int)-1,右辺:(int)1となり,結果「1」と表示された.

また,ビット演算時も算術型変換が行われてから演算が行われるのでしょうか?

よろしくお願いします.


この投稿にコメントする

削除パスワード

No.27826

Re:型変換
投稿者---nop(2006/08/04 10:27:13)


>比較も演算の内の1つなので算術型変換が行われてから,
>比較するということでしょうか?

その通りです。
代入もビット演算もシフトも比較も、
Cでは全て同じ「式」として評価されます。


この投稿にコメントする

削除パスワード

No.27827

Re:型変換
投稿者---ruby(2006/08/04 10:30:56)


>自分なりに考えると,まず「汎整数拡張」により比較演算の前に双方intに型変換される.
>その結果,左辺:(int)-1,右辺:(int)1となり,結果「1」と表示された.

お考えのとおりではないかと思います。


この投稿にコメントする

削除パスワード

No.27829

Re:型変換
投稿者---h3X(2006/08/04 10:41:07)


ご返信ありがとうございます.

だいぶ考えが整理できてきました.

しかし,まだいくつか分からないことがあります.

1.16進数定数は常に,その値の大きさによらず「unsigned」なのでしょうか?

2.16進定数と「signed」の型を持つ変数とで演算を行った場合,型変換はどうなるのか?

3.16進定数と「unsigned」の型を持つ変数とで演算を行った場合,型変換はどうなるのか?

2,3の疑問に関しては1の疑問の結果によりけりだと思うのですがどうなのでしょうか?

よろしくお願いします


この投稿にコメントする

削除パスワード

No.27830

Re:型変換
投稿者---たかぎ(2006/08/04 10:51:36)
http://takagi.in/


>1.16進数定数は常に,その値の大きさによらず「unsigned」なのでしょうか?

int型の表現範囲であればint型、unsigned int型の表現範囲であればunsigned int型、long型の表現範囲であればlong型、そうでなければunsigned long型です。
C99では、さらにlong long型→unsigned long long型になります。

>2.16進定数と「signed」の型を持つ変数とで演算を行った場合,型変換はどうなるのか?
>
>3.16進定数と「unsigned」の型を持つ変数とで演算を行った場合,型変換はどうなるのか?

16進定数かどうかは関係ありません。
オペランドの型で決まります。



この投稿にコメントする

削除パスワード

No.27833

Re:型変換 (To:たかぎさん)
投稿者---h3X(2006/08/04 11:17:41)


ご返信ありがとうございます.

>int型の表現範囲であればint型、unsigned int型の表現範囲であればunsigned int型、long型の表現範囲であればlong型、そうでなければunsigned long型です。

ということは,

char c = 0x80;

if (c & 0x12)

というコードの場合,「0x12」はint型,cは「汎整数拡張」によりまずint型に変換されますが「4294967168(0xffffff80)」になります.
”「汎整数拡張」は符号を含めてその値を変えない”というのはこういうことですか?
しかしこの値は,signed int型では扱えないので,
cの型は「汎整数拡張」により「unsigned int」に,値は「4294967168」になる.
次に「&」による両辺の算術変換が行われる.
左辺のcの型は「unsigned int」,右辺の型は「int」なので型が合わせられ,両辺「unsigned int」となる.

以上の結果より「0xffffff80 & 0x00000012」の演算が行われる.

0x80と0x12の論理積を計算したいと思っていたはずが,
まったく違う計算をしてしまうことになるのですね?

なのでこの場合だと,思ったように計算させるには,cを
「unsigned char c = 0x80」とすれば,「汎整数拡張」をcに施しても,(int)0x80のままということであっているのでしょうか?

間違っている箇所があればご指摘下さい.

よろしくお願いします.




この投稿にコメントする

削除パスワード

No.27834

Re:型変換 (To:たかぎさん)
投稿者---ruby(2006/08/04 11:26:26)


>というコードの場合,「0x12」はint型,cは「汎整数拡張」によりまずint型に変換されますが「4294967168(0xffffff80)」になります.

int型に変換するんだったら、0x00000080(十進で128)じゃないんですか?


この投稿にコメントする

削除パスワード

No.27835

Re:型変換 (To:たかぎさん)
投稿者---ruby(2006/08/04 11:30:57)


>int型に変換するんだったら、0x00000080(十進で128)じゃないんですか?

oops, とんでもない思い違いだったかも… orz
お許しください。


この投稿にコメントする

削除パスワード

No.27841

Re:型変換 (To: rubyさん)
投稿者---h3X(2006/08/04 12:21:44)


>>int型に変換するんだったら、0x00000080(十進で128)じゃないんですか?
>
>oops, とんでもない思い違いだったかも… orz
>お許しください。

ご返信内容が理解し難いのですが,No.27824にてご返信下さった内容は正しいということでよろしいのでしょうか?


この投稿にコメントする

削除パスワード

No.27842

Re:型変換 (To: rubyさん)
投稿者---ruby(2006/08/04 12:45:43)


>ご返信内容が理解し難いのですが,

むずかしかったですか?
No.27833(by h3Xさん)
>というコードの場合,「0x12」はint型,cは「汎整数拡張」によりまずint型に変換されますが「4294967168(0xffffff80)」になります.

に対して、私が
>int型に変換するんだったら、0x00000080(十進で128)じゃないんですか?

というコメントをつけた点が、私の思い違いだったかも、
と言っているだけです。

>No.27824にてご返信下さった内容は正しいということでよろしいのでしょうか?

No.27835(by 私)では、27824について何も言及しておりませんが…。
まあ、間違いがあればどなたかからご指摘があると思います。


この投稿にコメントする

削除パスワード

No.27836

Re:型変換 (To:たかぎさん)
投稿者---h3X(2006/08/04 11:36:31)


返信ありがとうございます.

>>というコードの場合,「0x12」はint型,cは「汎整数拡張」によりまずint型に変換されますが「4294967168(0xffffff80)」になります.
>
>int型に変換するんだったら、0x00000080(十進で128)じゃないんですか?

「signed char型」の扱える値の範囲は「-128〜127」です.
「0x80」は10進数で「128」なので,cの値は「-128」です.

次にこれを「汎整数拡張」によりint型に変換するのですが,変換したものは(符号を含めてその値を変えることはないので)「0xffffff80」になり,
この値はint型では扱えないため,cの型は「汎整数拡張」により,
結局「unsigned int」になり値は「0xffffff80」になると思うのですが違うのでしょうか?


この投稿にコメントする

削除パスワード

No.27837

Re:型変換 (To:たかぎさん)
投稿者---ruby(2006/08/04 11:39:49)


No.27835のとおりです。申し訳ありません。


この投稿にコメントする

削除パスワード

No.27839

Re:型変換 (To:たかぎさん)
投稿者---nop(2006/08/04 11:45:38)


>「0x80」は10進数で「128」なので,cの値は「-128」です.

0x80を unsigned char に変換した時、
どの様な値になるかは環境に依存します。

# 1 補数表現の環境と 2 の補数表現の環境
# などによって変わってくるでしょう。


この投稿にコメントする

削除パスワード

No.27840

Re:型変換 (To:たかぎさん)
投稿者---たかぎ(2006/08/04 11:52:20)
http://takagi.in/


>>「0x80」は10進数で「128」なので,cの値は「-128」です.
>
>0x80を unsigned char に変換した時、
>どの様な値になるかは環境に依存します。

そんなことはありません。
signed char型またはchar型の場合の間違いでは?



この投稿にコメントする

削除パスワード

No.27845

Re:型変換 (To:たかぎさん)
投稿者---nop(2006/08/04 15:02:46)


>signed char型またはchar型の場合の間違いでは?

その通りです…。
# どこから unsigned が出てきたんだ、オレ…orz


この投稿にコメントする

削除パスワード

No.27838

Re:型変換 (To:たかぎさん)
投稿者---たかぎ(2006/08/04 11:41:17)
http://takagi.in/


>char c = 0x80;
>略
>if (c & 0x12)
>
>というコードの場合,「0x12」はint型,cは「汎整数拡張」によりまずint型に変換されますが「4294967168(0xffffff80)」になります.
>”「汎整数拡張」は符号を含めてその値を変えない”というのはこういうことですか?

まず、signedもunsignedも付かない単なるchar型が符号付きであるか符号無しであるかは処理系定義です。
cの値が「0xffffff80」になったということは、char型は符号付きの8ビット整数型だということですが、この値は-128であって4294967168ではないと思います。もう一度ご確認ください。

>しかしこの値は,signed int型では扱えないので,
>cの型は「汎整数拡張」により「unsigned int」に,値は「4294967168」になる.

signed int型の表現範囲であるかどうかで型が変わるのは定数(リテラル)の場合で、一度オブジェクトに格納した後は値によって型が変わることはありません。

>次に「&」による両辺の算術変換が行われる.
>左辺のcの型は「unsigned int」,右辺の型は「int」なので型が合わせられ,両辺「unsigned int」となる.

そうはなりません。
両方ともint型なので、式の評価結果もint型になります。

>以上の結果より「0xffffff80 & 0x00000012」の演算が行われる.
>
>0x80と0x12の論理積を計算したいと思っていたはずが,
>まったく違う計算をしてしまうことになるのですね?

型はともかく、そういうことですね。

>なのでこの場合だと,思ったように計算させるには,cを
>「unsigned char c = 0x80」とすれば,「汎整数拡張」をcに施しても,(int)0x80のままということであっているのでしょうか?

まあ、そういうことです。



この投稿にコメントする

削除パスワード

No.27849

Re:これまでに分かった事のまとめ
投稿者---h3X(2006/08/04 15:58:17)


ひとまずこれまでにみなさんのご教授から,分かった内容をまとめてみます.

・値の表現範囲であるかどうかで型が変わるのは定数(リテラル)の場合で,
 一度オブジェクトに格納した後は値によって型が変わることはない

・「汎整数拡張」とは文字型や整数型などの汎整数型の場合,型がintより小さな場合
 (char, signed char, unsigned char, short, unsigned short)は,演算の最初に
  int か,表現できなければunsigned int に変換される.
  このとき,符号を含めてその値を変えることはないというもである.

・「算術型変換」は
(1)「+, -, ×,/, %」等において起こる
(2)「<,>, <=, >=,==」等において起こる
(3)「&, |, ^, ~,」等において起こる
   注意 :シフト演算の場合は例外的に一般的な算術変換が
       適用されず,以下のルールに従う
   ルール:整数拡張を各オペランドに適用する.
       結果の型は,左オペランドを拡張した後の型とする.


・16進数定数の値は,int型の表現範囲であればint型,unsigned int型の表現範囲であればunsigned int型,
  long型の表現範囲であればlong型,そうでなければunsigned long型となる.


現在,家に置いてある資料を見れない所にいるもので,また今晩その資料により確認後,疑問点があればまた質問させて頂きます.

また,上記の内容で間違っている箇所がありましたら,ご指摘頂けると助かります.


この投稿にコメントする

削除パスワード

No.27851

Re:これまでに分かった事のまとめ
投稿者---たかぎ(2006/08/04 16:07:32)
http://takagi.in/


>・「汎整数拡張」とは文字型や整数型などの汎整数型の場合,型がintより小さな場合
> (char, signed char, unsigned char, short, unsigned short)は,演算の最初に
>  int か,表現できなければunsigned int に変換される.
>  このとき,符号を含めてその値を変えることはないというもである.

念のため指摘しておくと、ここでいう「表現できなければ」というのは、例えばshortもintも16ビットの場合、unsigned shortの表現範囲はintではカバーできないのでunsigned intになるということです。決して実際の値が表現できるかどうかではありません。

>(3)「&, |, ^, ~,」等において起こる

~ は単項演算子ですので、通常の算術型変換は行われません。汎整数拡張(C99では整数拡張)だけです。
これは、単項の + や - も同様です。



この投稿にコメントする

削除パスワード

No.27828

Re:型変換
投稿者---たかぎ(2006/08/04 10:36:32)
http://takagi.in/


>また,ビット演算時も算術型変換が行われてから演算が行われるのでしょうか?

ビット演算でも、両方のオペランドに通常の算術型変換が行われます。

シフト演算の場合は少し違っていて、両方のオペランドを汎整数拡張します。似ているようでちょっと違うんですね。
例えば、

1 << 16L

という式を書いた場合、もし通常の算術型変換が行われるのであれば、左辺(1)もlong型になるのですが、汎整数拡張なので左辺はint型のままです。
その結果、int型が16ビットの処理系では動作が未定義になります。




この投稿にコメントする

削除パスワード

No.27831

Re:型変換 (To:たかぎさん)
投稿者---h3X(2006/08/04 10:53:42)


>例えば、
>
>1 << 16L
>
>という式を書いた場合、もし通常の算術型変換が行われるのであれば、左辺(1)もlong型になるのですが、汎整数拡張なので左辺はint型のままです。
>その結果、int型が16ビットの処理系では動作が未定義になります。

今までの内容からすると,まず「<<」演算子を適用する前に両辺に「汎整数拡張」を施します.(今回の例の場合は何も起きません)

左辺:(int)1,右辺:(long)16

次に「<<」演算を適用するため両辺の型を揃えます.なので,

左辺:(long)1, 右辺:(long)16

となると思います.
まず両辺「汎整数拡張」が行われ,次に両辺「算術型変換」が行われ,「<<」演算が行われるのではないのでしょうか?

よろしくお願いします.


この投稿にコメントする

削除パスワード

No.27832

Re:型変換 (To:たかぎさん)
投稿者---たかぎ(2006/08/04 10:59:11)
http://takagi.in/


>まず両辺「汎整数拡張」が行われ,次に両辺「算術型変換」が行われ,「<<」演算が行われるのではないのでしょうか?

そうはならないのです。
JIS X3010:2003 6.5.7 ビット単位のシフト演算子から引用すると、
整数拡張を書くオペランドに適用する。結果の型は, 左オペランドを拡張した後の型とする。
となっています。
つまり、シフト演算は例外的に通常の算術型変換が行われないのです。



この投稿にコメントする

削除パスワード

No.27843

Re:型変換
投稿者---ruby(2006/08/04 13:19:34)


コード2について、

>両辺の型が、左辺:(signed) int, 右辺:unsigned intで異なっているため、
>unsigned intにそろえられます。

unsigned int型にそろえられる理由が誤っていたようです。
左右両辺の型が異なっているため、ではなく、
一方の辺がunsigned int型であるため、というのが
正しいようです。

いろいろとお手間を取らせてしまいまして、申し訳ありません。>h3Xさん


この投稿にコメントする

削除パスワード

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧