1: 2007-02-11 (日) 16:59:40 |
2: 2007-02-11 (日) 19:14:00 |
| | | |
| そもそも、「型」ってなに?? | | そもそも、「型」ってなに?? |
| + | |
| + | 普通のコンピュータの素子は1bit(ビット)。0と1しか記憶できない。で、それを8個集めた単位が1byte(バイト)。(つまり8bit=1byteになる) |
| + | |
| + | 言語の変数上の素子はたいていこの1byteになる。(素子=最低単位) |
| + | |
| + | よって変数の最低単位は1byteになる。 |
| + | |
| + | でもこれじゃ不便なので、複数のバイトで1個という単位の変数が生まれた。 |
| + | |
| + | C言語では大体こんな感じ。 |
| + | |
| + | |int型|整数|4バイト|どうも通常は動作している環境って単位みたい。| |
| + | |float型|小数|4バイト|文字通り少数を扱いたい場合使うらしい| |
| + | |char型| 文字(一文字単位)|1バイト|「A」だの「i」だの| |
| + | |double型|少数|8バイト|より桁数が多い少数?| |
| + | |ポインタ型|4バイト|アドレス変数|アドレスを記憶する| |
| + | |
| + | ・・・こうして見ると、intやfloat、ポインタはみな同じバイト数である。なのでおそらく、実際にメモリ内に記憶されている状態ではまったく同じ状態だと思われる。 |
| + | |
| + | int a; |
| + | float b; |
| + | char *c; |
| + | |
| + | a=0x00FF00FF; |
| + | b=0x00FF00FF; |
| + | c=0x00FF00FF; |
| + | |
| + | /*この状態で各変数の実際の場所を覗いてみるときっとまったく同じだと思われる*/ |
| + | |
| + | しかし、''C言語上では''おのおのは型(タイプ)が違う変数という解釈がされるのだろう。 |
| + | |
| + | **豆知識〜 [#t245203f] |
| + | 枝豆ってなぁ・・・大豆やねんでぇ? |
| + | |
| + | -たとえばchar型。1バイトである。二進数でいえば8桁。扱える範囲は 00000000〜11111111までだ。これを16進数に直すと0x00〜0xFF。10進数だと0〜256までの値を使えるということになる。 |
| + | |
| + | ヨミちゃんアカンわ・・・・ |
| + | |
| + | -しかしこれは間違い。C言語の通常の考え方では、頭の(一番左)のビットは''符号用の印''として扱うようだ。頭の1ビットが0なら正数。1なら負の数ということ。っていうことは、char型が数字の値として扱えるのは残りの7ビットだけということになるわけ。7ビット(0000000〜1111111)だと、0〜128まで。で、符号があるから、char型が扱える数値の範囲は -128〜128 ということになる。ポインタを除く普通の変数はみなこの法則になる。 |
| + | |
| + | それは豆知識やのーて豆の知識や〜て言わなアカンやろ |
| + | |
| + | -しかしそれだと不便だという場合もあるよね?。50+40+80なんて計算をしたい場合、char型では正の数は最大で128。たかが170なんて値を計算したのにint型(4バイト)を使わなくてはいけなくなる。これじゃメモリの無駄だよね。なので、''unsigned''というオプションがある。ただの char a; だと、変数aは-128〜128という範囲になるけど、unsigned char a; って宣言すると、最初の1ビットを符号とするのをやめてくれる。なので変数aは、 0〜256 を扱える変数になるわけ。 |
| + | |
| + | *違う型同士の演算 [#db169d62] |
| + | COLOR(red){ここは厳密には間違っているが、概念の理解のため大雑把に端折って考える} |
| + | |
| + | 当たり前だが現実の世界の計算でも、型やタイプがバラバラなもの同士の演算はできないし、困る。 |
| + | |
| + | X = sing60(ピタゴラス+マジンガーZ) |
| + | |
| + | とか。 |
| + | |
| + | 同じようにC言語の変数でも、違う型同士だと演算や代入はできない。intとfloatがたまたま同じ4バイトだからと言っても、とにかく型が違うんだからできない。ましてやchar型+int型なんて、バイト数も違うんだからなおさらである。 |
| + | |
| + | int a,aa,aaa; |
| + | unsegned int b; |
| + | char c; |
| + | float d; |
| + | double e; |
| + | |
| + | aaa = aa + a; /*同じint同士。足し算も代入もOK。*/ |
| + | |
| + | a = b - aa; /*おいおい! bとaaは型が違うだろ同じ4バイトでも!*/ |
| + | |
| + | aaa = e; /*うわ!aaaとeじゃ型が違うしそもそもバイト数も・・・*/ |
| + | |
| + | aa = e + d -b; /*もうなにがなんだか・・・*/ |
| + | |
| + | ''そこで!!!'' |
| + | |
| + | 型キャスト(型の変換)が必要になってくるというわけ。 |
| + | |
| + | |
| + | *キャストの実際 [#r83bdef1] |
| + | **単純なもの [#h2426baf] |
| + | こんな式を妄想してみます。 |
| + | |
| + | char a; |
| + | int b; |
| + | int c: |
| + | |
| + | a = 18; |
| + | b = 38632; |
| + | |
| + | c = a + b; |
| + | |
| + | さて、これは問題です。まず、 a+b の時点で、char型+int型です。型が合いません。 |
| + | |
| + | これを暗算すると、38650になります。計算の結果はint型の変数cに収まります。代入はまあ、問題ないでしょう。 |
| + | |
| + | じゃあどうしたらいいかというと、こんなふうにしてchar型のaをint型に変換します。 |
| + | |
| + | char a; |
| + | int b,ab; |
| + | int c: |
| + | |
| + | a = 18; |
| + | b = 38632; |
| + | |
| + | ab = (int)a; /*こうすると、char型aの値(18)はそのままに、int型に変換してくれる*/ |
| + | |
| + | c = ab + b; /*当然int同士の足し算。そして結果をint型に代入なので、問題なし!*/ |
| + | |
| + | さて、上の式を見ると、変数abは一時的に型キャストした変数aを収めているだけですね。こんなことでいちいち変数を使うのはもったいないし行数も増える。ちょっといや〜ん。 |
| + | |
| + | そこでこんなふうに書くことができます。 |
| + | |
| + | |
| + | char a; |
| + | int b; |
| + | int c: |
| + | |
| + | a = 18; |
| + | b = 38632; |
| + | c = (int)a + b; |
| + | |
| + | なるほど!。簡単で単純ですね! |
| + | |
| + | ・・・ちなみに、この型キャストはあくまでも一時的に式の内部で変換をしただけです。なので、変数aそのものがint型に変化したわけではないことに注意。 |
| + | |
| + | |
| + | |
| + | *ポインタ型のキャスト [#aa70e23e] |
| + | これがややこしいですね。整理して単純化して考えてみます。 |
| + | |
| + | たとえばですが、メモリの0x0010番地〜0x0014番地になんらかの方法で22〜26までの値を代入してあるとします。(どうやるかはちょっとここでは考えない) |
| + | |
| + | とりあえず今はこういう状態だと仮定する。 |
| + | : : |
| + | ├──────┤ |
| + | 0x0010番地 │ 22 (0x16)│ |
| + | ├──────┤ |
| + | 0x0011番地 │ 23 (0x17)│ |
| + | ├──────┤ |
| + | 0x0012番地 │ 24 (0x18)│ |
| + | ├──────┤ |
| + | 0x0013番地 │ 25 (0x19)│ |
| + | ├──────┤ |
| + | 0x0014番地 │ 26 (0x1A)│ |
| + | ├──────┤ |
| + | : : |
| + | |
| + | ここでたとえば、「0x0010番地〜0x0014番地に入っている値を表示しなさい!」という指令を受けたとします。どういうプログラムになるでしょう? |
| + | |
| + | char *p; |
| + | char a; |
| + | int i; |
| + | |
| + | p = 0x0010; |
| + | |
| + | for(i = 0; i = 4; i++) |
| + | { |
| + | p = p + i; |
| + | printf("データは %d 16進数だと %x だよーん!\n",*p); |
| + | } |
| + | |
| + | これで、0x0010番地〜0x0014番地の内容が次々と表示されるはずです。 |
| + | |
| + | データは 22 16進数だと 0x16 だよーん! |
| + | データは 23 16進数だと 0x17 だよーん! |
| + | データは 24 16進数だと 0x18 だよーん! |
| + | データは 25 16進数だと 0x19 だよーん! |
| + | データは 26 16進数だと 0x1A だよーん! |
| + | |
| + | ・・・しかし、これでは問題が出てしまいます。そう、 ''p = p + i;''の部分です。 |
| + | |
| + | もうお解りですね。これは、 p(ポインタ型) + i(int型) の足し算になります。実際の足し算の値にはなんの問題もないのですが、異なる型同士の演算ということなので問題になるのです。 |
| + | |
| + | じゃ、どうするか?。簡単です。 この足し算の時、一時的に変数iをポインタ型にキャスト(型変換)してやればいいのです。 |
| + | |
| + | この変数pはポインタ型。さらに言えば、「char *」型の変数ですね。なので、'' (char *)i''としてやれば、変数pと同じ型になります。よって、 |
| + | |
| + | char *p; |
| + | char a; |
| + | int i; |
| + | |
| + | p = 0x0010; |
| + | |
| + | for(i = 0; i = 4; i++) |
| + | { |
| + | p = p + (char *)i; /*これで変数iは一時的にchar *型のポインタ型になる*/ |
| + | printf("データは %d 16進数だと %x だよーん!\n",*p); |
| + | } |
| + | |
| + | こうすることで、型違いの演算という問題は出なくなります。 |
| + | |
| + | COLOR(red){念のためもう一度言いますが、こういう処理を実際にプログラミングする場合、こんなコードは書きません。単純化するためにこんなことかいてますので。} |