掲示板利用宣言

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

 私は

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

掲示板2

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

No.27873

unsigned型はどういう場面で使うのが適切か?
投稿者---h3X(2006/08/05 12:45:56)


毎度お世話になっております.

昨日「型変換」という題目で長々と質問させて頂きました.


ある書籍に,以下のような内容が書かれています.

「不必要にunsigned型を使って,無用の複雑さを持ちこまないこと.
特に,表現する値が負になることはない(例えば「年齢」や「国の負債総額」といったデータだ)という理由だけでunsigned型を使わないこと.
intのような符号付きの型を使えば,複数の型を混在させた時に「格上げ」の規則で煩わされずに済む.
unsigned型を使うのは,ビットフィールドやバイナリのマスクだけに限ることだ.
式にキャストをかけて全オペランドを符号付きか符号なしに統一すれば,コンパイラは式の結果の型を作りださずに済む.」


とあります.

(「バイナリのマスク時に使う」というのはよく分りません.)

参考にお聞きしたいのですが,みなさんはどういった場合に「unsigned型」を使いますか?
またその時,「使わなければだめだったのか(なぜ使わなければだめだったのか)」,
「使った方がよいから使ったのか」等も書いて頂けると参考になります.


よろしくお願い致します.


この投稿にコメントする

削除パスワード

発言に関する情報 題名 投稿番号 投稿者名 投稿日時
<子記事> Re:unsigned型はどういう場面で使うのが適切か? 27874 επιστημη 2006/08/05 14:09:53
<子記事> Re:unsigned型はどういう場面で使うのが適切か? 27875 円零 2006/08/05 14:15:39
<子記事> Re:unsigned型はどういう場面で使うのが適切か? 27878 たかぎ 2006/08/05 15:09:52
<子記事> Re:unsigned型はどういう場面で使うのが適切か? 27883 chu- 2006/08/05 16:12:36


No.27874

Re:unsigned型はどういう場面で使うのが適切か?
投稿者---επιστημη(2006/08/05 14:09:53)


>「不必要にunsigned型を使って,無用の複雑さを持ちこまないこと.
>特に,表現する値が負になることはない(例えば「年齢」や「国の負債総額」といったデータだ)という理由だけでunsigned型を使わないこと.
>intのような符号付きの型を使えば,複数の型を混在させた時に「格上げ」の規則で煩わされずに済む.

少なからず異議あり。
なんで問題になるかっつーと「迂闊に型を混ぜるから」であり、
混ざる/混ぜることがないならなんの問題も不便もない。

なので僕は「零以上(非負)の値を表す」ならまずはunsigned。



この投稿にコメントする

削除パスワード

No.27875

Re:unsigned型はどういう場面で使うのが適切か?
投稿者---円零(2006/08/05 14:15:39)


メモリのダンプをunsigned charの配列に取って
printfで二桁の16進数としてきれいに表示したい場合とか。
(因みにCHAR_BITは8の環境です。)

書式指定"%02x"だと、桁数が少ないときはゼロで埋めてくれるけど
多いときはそのまんまですから、ffffff80とかなると、表示が崩れてしまいます。


この投稿にコメントする

削除パスワード

No.27876

Re:unsigned型はどういう場面で使うのが適切か?
投稿者---h3X(2006/08/05 14:37:53)


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

以前Base64エンコード・デコードのプログラムを作りました.
(一部に自分で作ったライブラリの関数が交じっていますが,話の本題とは関係ないので省略します,
またマジックナンバーが多く読みずらいですがご勘弁下さい)

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

#include <assert.h>



#include "base64.h"

#include "error.h"



static const char b64_table[] = {

    'A', 'B', 'C', 'D', 'E', 'F', 'G',

    'H', 'I', 'J', 'K', 'L', 'M', 'N',

    'O', 'P', 'Q', 'R', 'S', 'T', 'U',

    'V', 'W', 'X', 'Y', 'Z',

    'a', 'b', 'c', 'd', 'e', 'f', 'g',

    'h', 'i', 'j', 'k', 'l', 'm', 'n',

    'o', 'p', 'q', 'r', 's', 't', 'u',

    'v', 'w', 'x', 'y', 'z',

    '0', '1', '2', '3', '4', '5', '6',

    '7', '8', '9',

    '+', '/',

};



static void b64_get_table_index(char *bytes);



static void b64_get_table_index(char *bytes) 

{

    int i;



    for (i = 0; i < 4; i++) {

        if ( ('A' <= bytes[i]) && (bytes[i] <= 'Z'))

            bytes[i] -= 'A';

        else if ( ('a' <= bytes[i]) && (bytes[i] <= 'z')) {

            bytes[i] = bytes[i] - 'a' + 26;

        }

        else if ( ('0' <= bytes[i]) && (bytes[i] <= '9'))

            bytes[i] = bytes[i] -  '0' + 52;

        else if (bytes[i] == '+')

            bytes[i] = 62;

        else if (bytes[i] == '/')

            bytes[i] = 63;

        else /* '=' */

            bytes[i] = 0;

    }

}



int B64_encode(FILE *fp) 

{

    char bytes[3];

    int index1, index2, index3, index4;

    size_t read_bytes;



    if (fp == NULL) {

        return -1;

    }



    while ( (read_bytes = fread(bytes, 1, sizeof(bytes), fp)) != 0) {

        switch (read_bytes) {

            case 3:

                index1  = (  bytes[0] & 0xFC) >> 2;

                index2  = ( (bytes[0] & 0x03) << 4) | ( (bytes[1] & 0xF0) >> 4);

                index3  = ( (bytes[1] & 0x0F) << 2) | ( (bytes[2] & 0xC0) >> 6);

                index4  = (  bytes[2] & 0x3F);

                printf("%c%c%c%c", b64_table[index1], b64_table[index2], b64_table[index3], b64_table[index4]);

                break;

            case 2:

                index1  = (  bytes[0] & 0xFC) >> 2;

                index2  = ( (bytes[0] & 0x03) << 4) | ( (bytes[1] & 0xF0) >> 4);

                index3  = ( (bytes[1] & 0x0F) << 2);

                printf("%c%c%c=", b64_table[index1], b64_table[index2], b64_table[index3]);

                break;

            case 1:

                index1  = (  bytes[0] & 0xFC) >> 2;

                index2  = ( (bytes[0] & 0x03) << 4) | ( (bytes[1] & 0xF0) >> 4);

                printf("%c%c==", b64_table[index1], b64_table[index2]);

                break;

            default:

                ERR_DBG_PRINT_EXIT("Bugs");

                break;

        }

        fflush(stdout);

    }

    return 0;

}



int B64_decode(FILE *fp)

{

    char bytes[4];

    char decbytes[3];

    

    if (fp == NULL) {

        return -1;

    }

    

    while (fread(bytes, 1, sizeof(bytes), fp) != 0) {

        

        if ( (bytes[2] != '=') && (bytes[3] != '=')) {

            b64_get_table_index(bytes);

            

            decbytes[0] = (  bytes[0]         << 2) | ( (bytes[1] & 0x30) >> 4);

            decbytes[1] = ( (bytes[1] & 0x0F) << 4) | ( (bytes[2] & 0x3C) >> 2);

            decbytes[2] = ( (bytes[2] & 0x03) << 6) | (  bytes[3]             );

            printf("%c%c%c", decbytes[0], decbytes[1], decbytes[2]);

        }

        else if ( (bytes[2] != '=') && (bytes[3] == '=')) {

            b64_get_table_index(bytes);

            

            decbytes[0] = (  bytes[0]         << 2) | ( (bytes[1] & 0x30) >> 4);

            decbytes[1] = ( (bytes[1] & 0x0F) << 4) | ( (bytes[2] & 0x3C) >> 2);

            printf("%c%c", decbytes[0], decbytes[1]);

        }

        else {

            b64_get_table_index(bytes);



            decbytes[0] = (  bytes[0]         << 2) | ( (bytes[1] & 0x30) >> 4);

            printf("%c", decbytes[0]);

        }

        fflush(stdout);

    }

    return 0;

}


(char型がsigned char の環境とします)
char型の配列「char bytes[3]」にファイルから読み取ったものを格納し,
その後,これに対してビット演算や,シフト演算をしているのですが,
この場合特に「char bytes[3]」をunsigned型で宣言する必要はないと思っているのですが,
どうなんでしょうか?

アドバイスの程よろしくお願い致します.


この投稿にコメントする

削除パスワード

No.27879

Re:unsigned型はどういう場面で使うのが適切か?
投稿者---たかぎ(2006/08/05 15:28:48)
http://takagi.in/


>この場合特に「char bytes[3]」をunsigned型で宣言する必要はないと思っているのですが,
>どうなんでしょうか?

規格厳密合致プログラムを目指すのであれば、unsigned charにすべきです。
また、その場合には'A'のような文字定数ではなく、0x41とすべきですね。

処理系を特定し、その処理系で問題がないのであれば、今のままでもかまわないと思います。



この投稿にコメントする

削除パスワード

No.27881

Re:unsigned型はどういう場面で使うのが適切か?
投稿者---h3X(2006/08/05 15:42:51)


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


>処理系を特定し、その処理系で問題がないのであれば、今のままでもかまわないと思います。

自分の想定している処理系は(全部はとても把握していませんが)

1.char型はsigned型で,8bitである
2.int型は32bitである


>規格厳密合致プログラムを目指すのであれば、unsigned charにすべきです。

どのような処理系の場合に,今の私が書いたコードは思うように動かなくなるのでしょうか?


今後プログラムを書く上で非常に参考になると思います.
よろしくお願い致します.


この投稿にコメントする

削除パスワード

No.27886

Re:unsigned型はどういう場面で使うのが適切か?
投稿者---たかぎ(2006/08/05 16:58:58)
http://takagi.in/


>どのような処理系の場合に,今の私が書いたコードは思うように動かなくなるのでしょうか?

全部挙げると大変なので、かいつまんで説明すると、

bytes[0] & 0xFC

の部分はbytes[0]が負の値の場合、int型に変換される際にどんなビットパターンになるかが処理系定義です。

他には、

bytes[0] << 2

の部分ですが、bytes[0]が負の場合の動作が未定義になります。
負にならないようにしているようですが、'a'のような文字定数の値が処理系定義なので、完全に回避し切れません。

また、

decbytes[1] = ( (bytes[1] & 0x0F) << 4) | ( (bytes[2] & 0x3C) >> 2);

では、decbyte[1]に(符号付き8ビットの)char型の表現範囲を超える値を代入しようとしています(処理系定義の値になるか、処理系定義のシグナルが生成される)。

そして、Base64はアスキーを使う必要がありますが、'A'のような文字定数を使ってしまうと、処理系定義である文字定数の値に依存してしまいます。

あと、'A'〜'Z'の連続性が保証されない問題もあります。(アスキーを前提とするなら、これについては問題ないでしょう。要は、上の問題と根は同じ)




この投稿にコメントする

削除パスワード

No.27887

Re:unsigned型はどういう場面で使うのが適切か?
投稿者---h3X(2006/08/05 17:11:34)


>bytes[0] << 2
>
>の部分ですが、bytes[0]が負の場合の動作が未定義になります。

bytes[0]が負の場合で,シフト演算子が右シフト演算子「>>」の場合は,
シフト演算が「算術シフト」か「論理シフト」かどちらで行われるかは処理系依存だということは存じてますが,
左シフト演算の場合は,単純に左から「0」がシフトインするだけではないのでしょうか?


この投稿にコメントする

削除パスワード

No.27888

Re:unsigned型はどういう場面で使うのが適切か?
投稿者---たかぎ(2006/08/05 17:46:48)
http://takagi.in/


>左シフト演算の場合は,単純に左から「0」がシフトインするだけではないのでしょうか?

JIS X3010:2003 6.5.7 ビット単位のシフト演算子から引用すると、

E1 << E2の結果は, E1をE2ビット分左にシフトした値とする。空いたビットには0を詰める。E1が符号無し整数型を持つ場合, 結果の値は, E1×2E2の, 結果の型で表現可能な最大値より1大きい値を法とする剰余とする。E1が符号付き整数型と非負の値を持ち, E1×2E2が結果の型で表現可能である場合, それが結果の値となる。それ以外の場合, その動作は未定義とする。
ちょっと長いですが、ここで問題になるのは後半の部分です。
最後の「それ以外の場合」というのは、具体的には次のような場合です。

1. 値が負の場合
2. シフトの結果が表現できない場合

int型が32ビットだとすると、-1 << 2 とか 1 << 31 とかは動作が未定義になるわけです。
unsigned charやunsigned shortを使っていても、汎整数拡張すればint型になる場合には、値が表現できずに動作が未定義になることがあるので要注意ですね。



この投稿にコメントする

削除パスワード

No.27889

Re:解決しました
投稿者---h3X(2006/08/05 19:13:18)


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

色々と複雑ですね.

C言語では「処理系定義」,「未規定」,「未定義」,「制約」,「規格厳密合致」,「規格合致」等,色々定義されており,

どのマシンでも同一の結果を得られるプログラムを書くというのは,すごく難しそうですね.

お答え下さった皆様も含めありがとうございました.


この投稿にコメントする

削除パスワード

No.27878

Re:unsigned型はどういう場面で使うのが適切か?
投稿者---たかぎ(2006/08/05 15:09:52)
http://takagi.in/


I/Oなど、ハードウェアの制御に使うのは大抵unsignedですね。
これはバイナリのマスクに近い使い方もあれば、そうでない場合もあります。

他には、個数やサイズを表す変数は、(符号無し整数型である)size_t型と比較する機会が多いので、unsignedの方がよいかもしれません。



この投稿にコメントする

削除パスワード

No.27883

Re:unsigned型はどういう場面で使うのが適切か?
投稿者---chu-(2006/08/05 16:12:36)


私の使い分け方を参考までに。

[数値用変数]
・基本的にsigned
・負の値が必要なくてもsigned
・signedでは値の範囲が足りない、かつ、それより大きいsigned型を使うことが出来ないときはunsigned

数値用変数ではゼロ付近の値を扱うことが多いと思います。
そういう数値用変数にunsignedを使うということは、
例えると、崖のそばでキャッチボールをする感じでとても危ういです。
signedを使えばアンダーフローをあまり意識しなくて済みます。

[フラグ用変数]
・必ずunsigned

符号ビットを意識しなくて済むから。



この投稿にコメントする

削除パスワード

No.27885

Re:unsigned型はどういう場面で使うのが適切か?
投稿者---chu-(2006/08/05 16:21:49)


補足します。

ここで言っている[フラグ用変数]とは、

bool flag_a;
bool flag_b;
bool flag_c;

という変数ではなく、

unsigned long flag;
#define FLAG_A 0x00000001
#define FLAG_B 0x00000002
#define FLAG_C 0x00000004

という変数です。



この投稿にコメントする

削除パスワード

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