掲示板ランキング  システムユーティリティ(デスクトップアクセサリ)


掲示板利用宣言

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

 私は

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

掲示板1

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

No.7139

移植性の高い型のキャスト
投稿者---kolona(2007/02/20 06:13:00)



現在、UCS4を扱う関数を作成しています。UCS4はISO 10646 に似た、一文字を4Byteで表すUNICODEです。
UCS4の場合、データを処理するときはchar配列で扱えるのですが、
文字の比較の際にcharのままだと、一文字につき1Byteずつ、4回の比較が必要になります。
これは嫌なので、比較の際には他のデータ型にキャストしようと思ったのですが、ここで疑問があります。
4Byteならint型が良さそうですが、int型は処理系にとって最も効率的に処理できるサイズと定義されてますので4Byteとは限りません。
long型もint型以上のサイズとしか定義されていません。そうなると確実にサイズが4Byteの型が確保できません。
現在の環境ではint型でキャストすればうまくいく事を確認済みです。
C言語で4Byteが保証されるキャストの方法は無いのでしょうか。

使用環境は WinXP,borland C++ compiler 5.5.1
です。

以下、int型で4Byte一気に比較するソースです(対象は1Byte文字ですが。)。
このint*型のキャストを移植性の高いものにしたいという事です。


#include <stdio.h>
#include <string.h>

int main(void){

char s1[100],s2[100];

strcpy(s1,"abcde");
strcpy(s2,"abcee");

if(((int*)s1)[0]==((int*)s2)[0]){
    printf("同じ\n");
}
else{
    printf("違う\n");
}

return 1;
}



この投稿にコメントする

削除パスワード

発言に関する情報 題名 投稿番号 投稿者名 投稿日時
<子記事> Re:移植性の高い型のキャスト 7140 RiSK 2007/02/20 08:09:59
<子記事> Re:移植性の高い型のキャスト 7142 YuO 2007/02/20 10:13:42
<子記事> 移植性の高い型のキャスト:結論 7145 kolona 2007/02/20 15:26:56


No.7140

Re:移植性の高い型のキャスト
投稿者---RiSK(2007/02/20 08:09:59)


memcmp で比較してはどうでしょうか?

# CHAR_BIT の心配はいらないのでしょうか?


この投稿にコメントする

削除パスワード

No.7144

Re:移植性の高い型のキャスト
投稿者---kolona(2007/02/20 15:09:53)


>memcmp で比較してはどうでしょうか?
>
># CHAR_BIT の心配はいらないのでしょうか?

あーすっかり忘れてた。1バイトが9bitとか7bitの処理系がありますね。
えーと、移植性なんて偉そうに言ってますが、Linux,Windows,Macで動けばいいと思っているのでとりあえず1バイトは何ビットかは気にしないことにします。

次のいずれかの方法を使うことにしました。

1) プリプロセッサで切り分けてtypedefで32bitの型を定義する
2) 文字をlong配列として扱う
3) おとなしくmemcmp()を使う


この投稿にコメントする

削除パスワード

No.7142

Re:移植性の高い型のキャスト
投稿者---YuO(2007/02/20 10:13:42)


>現在、UCS4を扱う関数を作成しています。UCS4はISO 10646 に似た、一文字を4Byteで表すUNICODEです。

えーっと、私が知っているUCS-4はISO/IEC 10646-1で定義された文字集合なのですが……。
# 似たもの,ってどういうことでしょうか……。

ちなみに,UCS-4の1文字は4バイトではなく31ビットです。
# 32ビットではないです。さらに,文字集合の話でバイトを単位とするのは問題を含みます (1バイトの幅って?)


>int型は処理系にとって最も効率的に処理できるサイズと定義されてますので4Byteとは限りません。

最も効率的に処理できるなんていう定義ありましたっけ?よく見るのですが,規格中のどれが根拠かがわかりません。

int型は-32767〜32767を最低限表すことができる,short以上の幅を持つ整数型,程度の定義だったと思います。


>long型もint型以上のサイズとしか定義されていません。そうなると確実にサイズが4Byteの型が確保できません。

long型は-2147483647〜2147483647を最低限表すことができる,int以上の幅を持つ整数型のはずです。
つまり,最低32ビットは保証されています。


>C言語で4Byteが保証されるキャストの方法は無いのでしょうか。
>使用環境は WinXP,borland C++ compiler 5.5.1

えーっと,1バイトが8ビットと仮定して,標準Cとしては<stdint.h>のint32_tでしょうね。
32ビット幅の型が無ければint32_tは定義されないので,確実ではないですが。
# ISO/IEC 9899:1999追加機能なので,BCCでは対応していません。


>以下、int型で4Byte一気に比較するソースです(対象は1Byte文字ですが。)。
>このint*型のキャストを移植性の高いものにしたいという事です。

キャストで無理矢理処理しようとしている以上,移植性はありません。
プリプロセッサで切り分けてint32_tなどを定義して,それをもとに自分でchar => int32_tの変換を行う必要があります。

まぁ,比較だけならばRiSKさんの書かれているmemcmpでしょう。
文字を文字として扱いたいのであれば,最初から32ビット幅 (若しくはそれ以上の幅) を持つ型で扱うべきです。
# あまり考えたくないが,移植性を気にするならRiSKさんの書かれているCHAR_BITの取り扱いが一番面倒そう……。



この投稿にコメントする

削除パスワード

No.7143

Re:移植性の高い型のキャスト
投稿者---kolona(2007/02/20 15:04:04)



>えーっと、私が知っているUCS-4はISO/IEC 10646-1で定義された文字集合なのですが……。
># 似たもの,ってどういうことでしょうか……。
まだ各国言語が登録待ちだと書いてあったので曖昧な書き方をしてしまいました。たしかに定義されてます。
「似たもの」とかおかしな書き方ですね。

JIS X 3010 2003
の7.18.1.3
最速最小幅指定整数型
「少なくとも指定された幅を持つ整数型の中で、通常、最も速く処理できる整数型を示す」
をint型のことだと勘違いしてました。厳密に別の定義がなされてますね。しかしint型自体の定義が見つからない・・・

>ちなみに,UCS-4の1文字は4バイトではなく31ビットです。
># 32ビットではないです。さらに,文字集合の話でバイトを単位とするのは問題を含みます (1バイトの幅って?)
すいません。参考書にはオクテットって書いてありました。なんでオクテットという呼び方にこだわるのかわかった気がします。

>えーっと,1バイトが8ビットと仮定して,標準Cとしては<stdint.h>のint32_tでしょうね。
>32ビット幅の型が無ければint32_tは定義されないので,確実ではないですが。
># ISO/IEC 9899:1999追加機能なので,BCCでは対応していません。

bccは 確かに<stdint.h>用意してないですね。困った・・。

お二方の指摘を考慮して結局、次のいずれかの方法を使うことにします。

1) プリプロセッサで切り分けてtypedefで32bitの型を定義する
2) 文字をlong配列として扱う
3) おとなしくmemcmp()を使う



この投稿にコメントする

削除パスワード

No.7145

移植性の高い型のキャスト:結論
投稿者---kolona(2007/02/20 15:26:56)



結局、新しい型を作ることにしました。int32bit_tをプリプロセッサで定義しておきます。

typedef struct _UCS4{
    INT32BIT_T *sp;
    int datasize;
    int bufsize;
}UCS4;


これまでは下のSTRDATAを使っていたのですが、これにバイト列を読み込み、上のUCS4に変換する仕様にしたいと思います。
typedef struct _STRDATA{
    unsigned char *sp;//データが格納されている配列へのポインタ
    int datasize;//データのサイズ
    int bufsize;//データが格納されるバッファのサイズ
}STRDATA;


これと平行してUCS4型を使わない、memcmp()で比較する関数も作ります。
どっちがうまくいくか、実験します。
キャストはあきらめました。
お二人とも、どうもありがとうございました。


この投稿にコメントする

削除パスワード

No.7147

Re:移植性の高い型のキャスト:結論
投稿者---yoh2(2007/02/20 21:48:05)


移植性を気にされているなら、バイトオーダーによる違いを考慮することもお忘れなく。
例えば、「二」(00004e8c) と「六」(0000516d) とを比較する時、バイトオーダーを間違えてしまうと、
0x8c4e0000と0x6d510000との比較になってしまい、本来「二」<「六」となるべき所で、
逆の「二」>「六」といった結果になってしまいます。


この投稿にコメントする

削除パスワード

No.7148

Re:移植性の高い型のキャスト:結論
投稿者---kolona(2007/02/20 23:12:33)


>移植性を気にされているなら、バイトオーダーによる違いを考慮することもお忘れなく。

頭の痛い問題ですね。内部コードは固定にするとしても。
実装できた時に考えてみます。


この投稿にコメントする

削除パスワード

No.7149

Re:移植性の高い型のキャスト:結論
投稿者---yoh2(2007/02/20 23:55:49)


> 頭の痛い問題ですね。内部コードは固定にするとしても。
> 実装できた時に考えてみます。

すでにお気付きかも知れませんが、ちょいと補足。

> 使用環境は WinXP,borland C++ compiler 5.5.1
> です。

とのことですので、memcmpで比較が正しくなるようなchar配列(「二」なら{0x00, 0x00, 0x4e, 0x8c})を
単にunsigned int*でキャストすると、いきなりハマりますのでお気をつけて。
x86系のCPUでは、0x00004e8cを表わす32ビット整数のメモリ上の配置は{0x8c, 0x4e, 0x00, 0x00}です。
もちろん、逆にunsigned int配列として比較すると正しくなるような配列をそのままmemcmpに
食わせると、これまたおかしな結果になります。

uint32_array: 32ビット符号なし整数の配列
uint8_array: 8ビット符号なし整数の配列

として、相互変換するときに、
    uint32_array[i] = (uint8_array[j] << 24) | (uint8_array[j+1] << 16)
                        | (uint8_array[j+2] << 8) | uint8_array[j+3];
とか
    uint8_array[j]   = uint32_array[i] >> 24;
    uint8_array[j+1] = (uint32_array[i] >> 16) & 0xff;
    uint8_array[j+2] = (uint32_array[i] >> 8) & 0xff;
    uint8_array[j+3] = uint32_array[i] & 0xff

とかしてやれば、バイトオーダーがどちらかを知らずとも移植性は保たれます。
(8ビット整数と32ビット整数がある環境なら、ですけれど)
# signedなら、も少し式が複雑になります。


この投稿にコメントする

削除パスワード

No.7150

Re:移植性の高い型のキャスト:結論
投稿者---kolona(2007/02/21 00:38:16)



>uint32_array: 32ビット符号なし整数の配列
>uint8_array: 8ビット符号なし整数の配列
>
>として、相互変換するときに、
    uint32_array[i] = (uint8_array[j] << 24) | (uint8_array[j+1] << 16)
                        | (uint8_array[j+2] << 8) | uint8_array[j+3];
とか
    uint8_array[j]   = uint32_array[i] >> 24;
    uint8_array[j+1] = (uint32_array[i] >> 16) & 0xff;
    uint8_array[j+2] = (uint32_array[i] >> 8) & 0xff;
    uint8_array[j+3] = uint32_array[i] & 0xff
    

>とかしてやれば、バイトオーダーがどちらかを知らずとも移植性は保たれます。
>(8ビット整数と32ビット整数がある環境なら、ですけれど)
># signedなら、も少し式が複雑になります。



えっと、これでバイトオーダーがどちらでも良い理由がちょっとよくわからないんですが・・。
バイトオーダーを意識しなくても常に同じ順でメモリに突っ込めば(書き出すとき以外は)問題ないという事でしょうか?


この投稿にコメントする

削除パスワード

No.7156

Re:移植性の高い型のキャスト:結論
投稿者---yoh2(2007/02/21 01:33:40)


>えっと、これでバイトオーダーがどちらでも良い理由がちょっとよくわからないんですが・・。

ああ、前提条件を書くのを忘れていました。
uint8_arrayの方は、memcmpに突っ込んで比較(や、その他の操作もろもろ)するための配列、
uint32_arrayの方は、32ビット整数を使って比較(や、以下略)をするための配列という設定です。

でもって、前述のコードは、片方の配列が(正しい形で)存在するとき、もう片方の配列を
移植性を保ったまま作成する方法を示したものです。
32ビット整数のバイトオーダーが分からないのに、8ビット整数列を32ビット整数のメモリレイアウト
として解釈しようとするから問題が起きるのであって、4つの8ビット整数列の最初の要素と
32ビット整数の上位桁〜4つの8ビット整数列の最後の要素と32ビット整数の下位桁が対応するように
きちんと計算してやれば問題は起こらずに済むといった寸法です。
# さらに、別枝でたかぎさんが指摘されている、境界調整の問題も起こらずに済みます。

なお、外部(ファイル等)との読み書きに関しては別問題ということで、一切考慮していません。
そちらを考える場合は、自身のバイトオーダーより、与えられたUCS-4列がどのバイトオーダーに
なっているかの判別で苦労しそうですね。

(以下、うろ覚えなので嘘を書いている可能性あり)
UCS-4は、原則としてビッグエンディアンな(というか、4オクテットの並びで1文字を表す)
符号化体系ですが、コードを交換する者同士の合意があれば、ネイティブな32ビット整数でもよい
という規定もあったハズなので、リトルエンディアンにもなり得るあたりややこしいですね。
バイトオーダー識別のためのBOMを付けなければならないという規定もなかったような……


この投稿にコメントする

削除パスワード

No.7159

Re:移植性の高い型のキャスト:結論
投稿者---kolona(2007/02/21 02:52:46)


>>えっと、これでバイトオーダーがどちらでも良い理由がちょっとよくわからないんですが・・。
>
>ああ、前提条件を書くのを忘れていました。
>uint8_arrayの方は、memcmpに突っ込んで比較(や、その他の操作もろもろ)するための配列、
>uint32_arrayの方は、32ビット整数を使って比較(や、以下略)をするための配列という設定です。
>
>でもって、前述のコードは、片方の配列が(正しい形で)存在するとき、もう片方の配列を
>移植性を保ったまま作成する方法を示したものです。
>32ビット整数のバイトオーダーが分からないのに、8ビット整数列を32ビット整数のメモリレイアウト
>として解釈しようとするから問題が起きるのであって、4つの8ビット整数列の最初の要素と
>32ビット整数の上位桁〜4つの8ビット整数列の最後の要素と32ビット整数の下位桁が対応するように
>きちんと計算してやれば問題は起こらずに済むといった寸法です。
># さらに、別枝でたかぎさんが指摘されている、境界調整の問題も起こらずに済みます。

処理の順番を固定すればどんなバイトオーダーでも比較に困らないというわけですか。理解できました。汎整数拡張についても対応します。

>(以下、うろ覚えなので嘘を書いている可能性あり)
>UCS-4は、原則としてビッグエンディアンな(というか、4オクテットの並びで1文字を表す)
>符号化体系ですが、コードを交換する者同士の合意があれば、ネイティブな32ビット整数でもよい
>という規定もあったハズなので、リトルエンディアンにもなり得るあたりややこしいですね。
>バイトオーダー識別のためのBOMを付けなければならないという規定もなかったような……

私の参考にしている、「文字コード超研究」によると、UCS4は(何も規定がなければ)ビッグエンディアンのようです。(UCS4のエンディアンについて明記がないんですが。)
というか、UCS4は文字集合でもあるのでエンディアンは特に規定してないのかも知れませんが。
面倒なのがエンコーディングに関するUTF32で、リトルエンディアン、ビッグエンディアン、BOMの有無で4通りあるみたいです。
ここら辺になるとお手上げなのでどれか一個に絞って実装することにします。
1個出来たらエンディアン変換もBOMの追加も後付けできるでしょうし。


この投稿にコメントする

削除パスワード

No.7154

Re:移植性の高い型のキャスト:結論
投稿者---たかぎ(2007/02/21 01:14:27)
http://takagi.in/


# 小刻みにレスしていますが...

移植性の観点からは
>    uint32_array[i] = (uint8_array[j] << 24) | (uint8_array[j+1] << 16)
>                        | (uint8_array[j+2] << 8) | uint8_array[j+3];
では駄目で、uint8_array[*]をそれぞれunsigned long型にキャストしてやる必要があります。
uint8_arrayはcharまたはunsigned char型でしょうから、uint8_array[j]等を汎整数拡張するとint型またはunsigned int型(sizeof(char)==sizeof(int)の場合)になります。それを24ビットシフトすると動作が未定義になる場合があります。


この投稿にコメントする

削除パスワード

No.7158

Re:移植性の高い型のキャスト:結論
投稿者---yoh2(2007/02/21 01:50:07)


>uint8_arrayはcharまたはunsigned char型でしょうから、uint8_array[j]等を汎整数拡張するとint型またはunsigned int型(sizeof(char)==sizeof(int)の場合)になります。それを24ビットシフトすると動作が未定義になる場合があります。

そうでした。
- uint8_array[j]がunsignedだから、汎整数拡張してもunsignedだと誤解
- unsigned intは最低32ビットの精度があると思い込む
という二重のミスですね。
対策としては、<<の右オペランド(24、16、8)に接尾子ULを付けることでしょうか。

# intが16ビットな環境から入った身としては、2番目のミスが恥ずかしい……


この投稿にコメントする

削除パスワード

No.7162

Re:移植性の高い型のキャスト:結論
投稿者---たかぎ(2007/02/21 08:57:02)
http://takagi.in/


>対策としては、<<の右オペランド(24、16、8)に接尾子ULを付けることでしょうか。

シフト演算子のオペランドは、通常の算術型変換ではなく汎整数拡張(C99では整数拡張)になりますので、右オペランドがどうであれ、左オペランドの型は影響を受けません。ですから、左オペランドを明示的にキャストする必要があります。



この投稿にコメントする

削除パスワード

No.7152

Re:移植性の高い型のキャスト:結論
投稿者---たかぎ(2007/02/21 00:54:58)
http://takagi.in/


>移植性を気にされているなら、バイトオーダーによる違いを考慮することもお忘れなく。

バイトオーダーだけでなく、境界調整の問題もありますので、キャストでどうにかするのは無理です。
やはり、シフトと論理演算の組み合わせで計算するのが一番です。



この投稿にコメントする

削除パスワード

No.7151

Re:移植性の高い型のキャスト:結論
投稿者---たかぎ(2007/02/21 00:50:14)
http://takagi.in/


>typedef struct _UCS4{
>typedef struct _STRDATA{
移植性を気にされるのであれば、下線で始まり大文字が続く識別子は予約済み識別子ですので、使用すべきではありません。




この投稿にコメントする

削除パスワード

No.7157

Re:移植性の高い型のキャスト:結論
投稿者---kolona(2007/02/21 01:43:54)


>移植性を気にされるのであれば、下線で始まり大文字が続く識別子は予約済み識別子ですので、使用すべきではありません。

ご指摘ありがとうございます。もう既に10ヶ所でこんな構造体を定義してました。

修正前
typedef struct _BI_PHY_TREE{
    struct _BI_PHY_TREE *brch[2];  //枝へのポインタ
    int data[2]; //子データに対応した整数。
    int ndata;  //子のデータ数。2から引いて枝の数を示す。
}BI_PHY_TREE;

修正後
typedef struct _Bi_phy_tree{
    struct _Bi_phy_tree *brch[2];  //枝へのポインタ
    int data[2]; //子データに対応した整数。
    int ndata;  //子のデータ数。2から引いて枝の数を示す。
}BI_PHY_TREE;



この投稿にコメントする

削除パスワード

No.7160

Re:移植性の高い型のキャスト:結論
投稿者---YuO(2007/02/21 03:51:50)


修正後
typedef struct _Bi_phy_tree{
    struct _Bi_phy_tree *brch[2];  //枝へのポインタ
    int data[2]; //子データに対応した整数。
    int ndata;  //子のデータ数。2から引いて枝の数を示す。
}BI_PHY_TREE;

ISO/IEC 9899:1999 7.1.3 Reserved identifiersによると
  • 下線で始まり,次の文字が ( 下線 or 大文字 ) の識別子は全ての目的に予約されています
  • 下線で始まる識別子は, ( 通常の or タグ ) 名前空間のファイル有効範囲を持つ識別子として予約されています
となっています。
よって,この修正は意味をなしません。
# たかぎさんの記述を読み違えた?



この投稿にコメントする

削除パスワード

No.7164

Re:移植性の高い型のキャスト:結論
投稿者---kolona(2007/02/21 11:10:18)


>ISO/IEC 9899:1999 7.1.3 Reserved identifiersによると
  • 下線で始まり,次の文字が ( 下線 or 大文字 ) の識別子は全ての目的に予約されています
  • 下線で始まる識別子は, ( 通常の or タグ ) 名前空間のファイル有効範囲を持つ識別子として予約されています
となっています。
>よって,この修正は意味をなしません。
># たかぎさんの記述を読み違えた?

はい。下線で始まり、大文字が続く、識別子は予約済みと誤解してました。今調べてみたのですがMozilla firefox ではtypedef宣言とstruct宣言で同じ識別子を使っていました。
また、Cではstruct タグの名前空間とtypedefの名前空間は分かれているので
struct タグにアンダスコアで始まる名前をつける必要はない。
との指摘も見つけたので、アンダーバーなしの同じ識別子を用いることにします。ご指摘ありがとうございます。


この投稿にコメントする

削除パスワード

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





掲示板提供:(有)リアル・インテグリティ