6: 2007-03-22 (木) 19:20:56 |
現: 2024-01-06 (土) 22:37:40 ゲスト[htonJexNzZc] |
| | | |
| | | |
- | 1バイトは2進数で00000000〜11111111。10進数に直すと0〜255までとなる。逆に言えば、たった255しか表現できない困った単位ともいえる。 | + | 1バイトは2進数で00000000〜11111111。10進数に直すと0〜255までとなる。逆に言えば、たった256種類しか表現できない困った単位ともいえる。 |
| | | |
| でもこれじゃ不便なので、複数のバイトで1個という単位の変数が生まれた。 | | でもこれじゃ不便なので、複数のバイトで1個という単位の変数が生まれた。 |
| C言語では大体こんな感じ。 | | C言語では大体こんな感じ。 |
| | | |
- | |int型|整数|4バイト|どうも通常は動作している環境って単位みたい。つまり16bitCPUなら16bit、32bitCPUなら32bitとか(Cコンパイラに依存?)| | + | |
- | |float型|小数|4バイト|文字通り少数を扱いたい場合使うらしい| | + | |型名|バイト数|用途|範囲(10進数)|範囲(16進数)|備考|h |
- | |char型| 文字(一文字単位)|1バイト|「A」だの「i」だの| | + | |char型|1バイト|文字(一文字単位)|-127〜127|0x00〜0xFF|「A」だの「i」だの「#」だの| |
- | |double型|少数|8バイト|より桁数が多い少数?| | + | |short型|2バイト|すこし大きな整数|-32768〜32767|0x0000〜0xFFFF|| |
- | |ポインタ型|4バイト|アドレス変数|アドレスを記憶する| | + | |long型|4バイト|かなり大きな整数|-214783648〜214783647|0x00000000〜0xFFFFFFFF|| |
| + | |int型|4バイト|かなり大きな整数|-214783648〜214783647|0x00000000〜0xFFFFFFFF|どうも通常は動作している環境って単位みたい。つまり16bitCPUなら16bit、32bitCPUなら32bitとか(Cコンパイラに依存?)| |
| + | |float型|4バイト|少数を扱える|小数を扱いたい場合使うらしい||| |
| + | |double型|8バイト|より桁数が多い小数|||| |
| + | |ポインタ型|4バイト|アドレス変数|アドレスを記憶する||| |
| | | |
| ・・・こうして見ると、intやfloat、ポインタはみな同じバイト数である。なのでおそらく、実際にメモリ内に記憶されている状態ではまったく同じ状態だと思われる。 | | ・・・こうして見ると、intやfloat、ポインタはみな同じバイト数である。なのでおそらく、実際にメモリ内に記憶されている状態ではまったく同じ状態だと思われる。 |
| float b; | | float b; |
| char *c; | | char *c; |
| + | |
| a=0x00FF00FF; | | a=0x00FF00FF; |
| b=0x00FF00FF; | | b=0x00FF00FF; |
| c=0x00FF00FF; | | c=0x00FF00FF; |
| + | |
| /*この状態で各変数の実際の場所を覗いてみるときっとまったく同じだと思われる*/ | | /*この状態で各変数の実際の場所を覗いてみるときっとまったく同じだと思われる*/ |
| | | |
| float d; | | float d; |
| double e; | | double e; |
| + | |
| aaa = aa + a; /*同じint同士。足し算も代入もOK。*/ | | aaa = aa + a; /*同じint同士。足し算も代入もOK。*/ |
| + | |
| a = b - aa; /*おいおい! bとaaは型が違うだろ同じ4バイトでも!*/ | | a = b - aa; /*おいおい! bとaaは型が違うだろ同じ4バイトでも!*/ |
| + | |
| aaa = e; /*うわ!aaaとeじゃ型が違うしそもそもバイト数も・・・*/ | | aaa = e; /*うわ!aaaとeじゃ型が違うしそもそもバイト数も・・・*/ |
| + | |
| aa = e + d -b; /*もうなにがなんだか・・・*/ | | aa = e + d -b; /*もうなにがなんだか・・・*/ |
| | | |
| int b; | | int b; |
| int c: | | int c: |
| + | |
| a = 18; | | a = 18; |
| b = 38632; | | b = 38632; |
| + | |
| c = a + b; | | c = a + b; |
| | | |
| int b,ab; | | int b,ab; |
| int c: | | int c: |
| + | |
| a = 18; | | a = 18; |
| b = 38632; | | b = 38632; |
| + | |
| ab = (int)a; /*こうすると、char型aの値(18)はそのままに、int型に変換してくれる*/ | | ab = (int)a; /*こうすると、char型aの値(18)はそのままに、int型に変換してくれる*/ |
| + | |
| c = ab + b; /*当然int同士の足し算。そして結果をint型に代入なので、問題なし!*/ | | c = ab + b; /*当然int同士の足し算。そして結果をint型に代入なので、問題なし!*/ |
| | | |
| int b; | | int b; |
| int c: | | int c: |
| + | |
| a = 18; | | a = 18; |
| b = 38632; | | b = 38632; |
| char a; | | char a; |
| int i; | | int i; |
- | | + | |
| + | |
| for(i = 0x0010; i = 0x0014; i = i+1) | | for(i = 0x0010; i = 0x0014; i = i+1) |
| { | | { |
| p = i; | | p = i; |
| printf("データは %d 16進数だと %x だよーん!\n",*p); | | printf("データは %d 16進数だと %x だよーん!\n",*p); |
| + | |
| } | | } |
| | | |
| char a; | | char a; |
| int i; | | int i; |
- | | + | |
| + | |
| for(i = 0x0010; i = 0x0014; i = i+1) | | for(i = 0x0010; i = 0x0014; i = i+1) |
| { | | { |
| p = (char *)i; /* ←ここに注目!*/ | | p = (char *)i; /* ←ここに注目!*/ |
| printf("データは %d 16進数だと %x だよーん!\n",*p); | | printf("データは %d 16進数だと %x だよーん!\n",*p); |
| + | |
| } | | } |
| | | |
| char z; | | char z; |
| char c; | | char c; |
| + | |
| z = 0x44; | | z = 0x44; |
| c = 0x22; | | c = 0x22; |
| + | |
| c = c + z; | | c = c + z; |
| + | |
| printf("data is %x\n",c); | | printf("data is %x\n",c); |
| | | |
| | | |
| int i; | | int i; |
| + | |
| char z; | | char z; |
| char c; | | char c; |
| + | |
| z = 0x44; | | z = 0x44; |
| c = 0x22; | | c = 0x22; |
| + | |
| i = c + z; | | i = c + z; |
| + | |
| printf("data is %x\n",i); | | printf("data is %x\n",i); |
| | | |
| -COLOR(blue){もちろん、 i = (int)c + (int)z; と書いても当然エラーにはなりません。むしろこれが正しいのです。''本来の概念''で言えば。} | | -COLOR(blue){もちろん、 i = (int)c + (int)z; と書いても当然エラーにはなりません。むしろこれが正しいのです。''本来の概念''で言えば。} |
| | | |
- | もちろん「許容」にもちゃんと法則があります。これには順番があります。以下を順番に適用していくのです。 | + | もちろん「許容」にもちゃんと法則があります。 |
- | -最後は左辺の型に自動調整される | + | ~ |
| + | 許容の法則は一般にちょっとややこしい説明がされていることが多いのですが、こんな風に分けて考えればそうむずかしくありません。 |
| | | |
| | | |
- | です。 | + | まず、左辺と右辺に分けて考えるといいです。 |
| + | -まず右辺内 |
| + | ++混在する一番大きな型に合わせて自動調整される |
| + | ++ただし、int以下のものは全てintに強制変換される |
| + | ++以上の法則で、「とりあえず右辺内」の値が出来上がる |
| + | -次に左辺 |
| + | ++右辺の値や型がなんだろうと左辺の型に強制変換される |
| + | ++もしはみ出た場合は切り捨てられる |
| | | |
- | こんな式を妄想してみます。 | + | 基本的にはこれを上から順番に行い、最後に左辺の値が決まると考えてもらってOKです。 |
| + | |
| + | 実際の場合を思考実験してみましょう。たとえばこんな式の場合。 |
| | | |
| char c; | | char c; |
| int i; | | int i; |
- | short s; | + | |
- | double d; | + | c = 0xf3; |
| + | i = 0x12; |
| + | |
| + | c = c + i; |
| + | |
| + | printf("data is 0x%X\n",c); |
| + | |
| + | これは暗算できますね?。答えは0x105になるはずです。しかしこれを実際にコンパイルして実行すると、答えは0x5になります。はて??? |
| + | |
| + | c = c + i; /* ← ここがどう処理されているのか? */ |
| + | |
| + | ++まず右辺。 |
| + | ++おぉ? iはintだけど、cはcharじゃないか!。型が違うぞ! |
| + | ++仕方がない。自動的に許容しよう。 |
| + | ++右辺で一番大きいのは・・・int型(変数i)だな! |
| + | ++よし、全ての変数を無理やりint型として考えよう! |
| + | ++結果、この式は、c = 0x000000f3(int型。4バイト) + 0x00000012 (int型。4バイト)に変換だ! |
| + | ++よし!計算できたぞ!。結果はこうだ! c = 0x00000105(int型。4バイト) だ!。 |
| + | ++これで右辺は終了だ!。今度は左辺への代入処理だ! |
| + | ++左辺への代入の場合、右辺が何だろうと左辺の型に強制変換だ! |
| + | ++むむむ! なんていうことだ! 左辺はchar型。右辺はint型。左辺の型のほうが小さいぞ! |
| + | ++・・・しかし、「右辺が何だろうと左辺の型に強制変換」は決まっている!。これは崩せない! |
| + | ++仕方がない。はみ出す部分は残念だが''切捨て''てしまおう! |
| + | ++右辺をこう切り捨てる。 c = 0x05 (右辺の頭の3バイトが切り捨てられた) |
| + | ++よし! 左辺 c に代入できるぞ。代入! |
| + | |
| + | こういう内部処理が行われて、結果、暗算の結果である0x105は0x05に切り捨てられ、cに代入が完成し、計算が終わるのです。 |
| + | |
| + | |
| + | この、自動型調整はややこしいといわれますが、とにかくポイントは、上記の8番の部分。''「式の処理は、一旦右辺の中で全部解決して、それが終わったら改めて左辺への代入に移る」''というところでしょうか? |
| + | |
| + | -COLOR(blue){少数の場合はどうなるの???} |
| + | -COLOR(red){・・・いや申し訳ない。少数を扱う場合も勉強してみたのですが、どうも整理しきれませんでした。またここはオイラが''OSを作る''ために勉強して整理しているサイトなので、このページとしては少数型はこれ以上はツッこまないことにしようかと・・・} |
| + | |
| + | |
| + | *現実の型キャスト(ポインタ) [#j3516d0b] |
| + | これは「現実」とは言っても、ほぼ概念通りと考えてもらってかまわないと思います。 |
| + | |
| + | アドレス変数(ポインタ)は特別にして唯一無比の型なので、とにかくこれに代入しようとした場合はほぼ''かならず型キャストして代入する''で問題ないでしょう。 |
| + | |
| + | -COLOR(blue){少ない桁数の変数から型キャストした場合どうなるのか?} |
| + | |
| + | こういうのは実験しちゃうのが早いですよ? |
| + | |
| + | int main(void){ |
| + | |
| + | char *p; |
| + | char c; |
| + | |
| + | c = 0x33; |
| + | p = c; |
| + | |
| + | printf("data is 0x%X\n",p); |
| + | |
| + | } |
| + | |
| + | さて、これをコンパイルすると・・・ |
| + | ~おっと!いけない!型キャストを忘れていました。 |
| + | |
| + | int main(void){ |
| + | |
| + | char *p; |
| + | char c; |
| + | |
| + | c = 0x33; |
| + | p = (char *)c; |
| + | |
| + | printf("data is 0x%X\n",p); |
| + | |
| + | } |
| + | |
| + | ・・・実は、これもエラーが出ます。「キャストはいいけどサイズが合わんぞ。サイズくらい合わせろよ!」ってとこでしょうか? |
| + | |
| + | しかし警告エラーは無視してコンパイルし、これを実行すると、答えは0x33と表示されます。おそらくこういう場合、足りないケタ数は0で埋められてしまうようです。 |
| | | |
- | c = 0x11; | + | では、char型の変数の値をアドレス変数に当て込むにはどういう手法が正しいのでしょうか? |
- | i = 0x22; | + | |
- | s = 0x33; | + | |
- | d = 0x44; | + | |
| | | |
- | i = c + i + s + d; | + | 正しい呼び名はわかりませんが、おそらくこういう「二段キャスト」でよいはずです。 |
| | | |
- | printf("data is 0x%x\n",i); | + | p = (char *)(int)c; |
| | | |
- | さあ、この計算は型が混在もいいところですね。でもエラーは出ません。Cコンパイラが「許容」しているためです。 | + | COLOR(blue){上記。理解のため無理にchar型を使っていますが、やっぱり理想は、せめてint型にしてから、あるいはアドレス変数への代入が予想されるような値は最初からint型で定義して使うというのが本流なのでしょう。きっと。} |