Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

FD_ISSET の謎

691 views
Skip to first unread message

名称不定

unread,
Jun 19, 2002, 10:34:15 AM6/19/02
to
はじめまして。

FD_ISSET マクロについての質問です。

FD_ISSET に渡すファイルディスクリプタの値が
定数と変数の場合で動作が異なることに気がつきました。

#include <sys/types.h>

int main(int argc, char *argv[]) {
fd_set fds;
int i = -1;
FD_ZERO(&fds);
return FD_ISSET(-1, &fds);
}

上記のコードはエラー無く実行できます。
しかし、FD_ISSET(-1, &fds) を FD_ISSET(i, &fds) に
書き直すと、実行時にセグメント違反(Redhat Linux) や
バスエラー(FreeBSD) が発生するのです。
Visual C++(Windows) ではエラーは発生しませんでした。
ファイルディスクリプタは非負の整数値なので、負の値が
渡されること自体が誤っているというのは承知していますが、
何故動作が異なるのか知りたいのです。
以下に議論の流れを示します。
どこが誤っているのかご指摘願えませんでしょうか?


FD_ISSET(n, p) マクロは
((p)->fds_bits[(n)/NFDBITS] & _fdset_mask(n)) で定義されている。
 ↓
NFDBITS は (sizeof(fd_mask) * NBBY) で定義されている。
 ↓
fd_mask は unsigned long であるため、
FreeBSD(98) では NFDBITS = 32 となる。
 ↓
[(-1) / NFDBITS] は [(-1) / 32] = [0] だから
(p)->fds_bits[0] を参照するはず。
 ↓
_fdset_mask(n) は
((fd_mask) 1 << ((n) % NFDBITS)) で定義されている。
 ↓
((-1) % 32) = -1 だから ((unsigned long) 1 << -1) = 0 となる。
 ↓
_fdset_mask(-1) = 0 となるはず。
 ↓
定数では正常に動作するのに、変数だと動作しないのは何故?


よろしくお願いします。

miho

unread,
Jun 19, 2002, 5:17:08 PM6/19/02
to
> _fdset_mask(n) は
> ((fd_mask) 1 << ((n) % NFDBITS)) で定義されている。
>  ↓
> ((-1) % 32) = -1 だから

NFDBITS が符号無しだから、さきに -1 を符号無しに変換してから、32 で割る。
FreeBSD(98) ということだから、134217727 ぐらいになる?

Shinobu Kumaoka

unread,
Jun 19, 2002, 8:25:07 PM6/19/02
to
熊岡です。

名称不定 wrote:

> [(-1) / NFDBITS] は [(-1) / 32] = [0] だから

これ、正確には(-1)/32uですね。

ここの計算が、コンパイル時に計算される場合には0になるけれども、
実行時に計算される場合には、0xffffffff/32になってしまうからなんでしょうね。

でも、これが0になってしまうのは、コンパイラのバグなんでないかなあ。

--
cog...@sp.hudson.co.jp
株式会社ハドソン
コア・テクノロジー事業本部
システム部 ソフトウェア課
熊岡 忍(Kumaoka Shinobu)


Takashi SAKAMOTO

unread,
Jun 19, 2002, 1:00:15 PM6/19/02
to
名称不定 <dev_...@anet.ne.jp> writes:

> Visual C++(Windows) ではエラーは発生しませんでした。

FreeBSD 側の実装はさておき、Winsock の fd_set は次のような
構造になってます。

struct {
int fd_count ;
SOCKET fd_array [MAX_FD_SET] ;
} ;

この fd_array の中を順番に fd_count 分だけ比較する実装では
セグメント例外は出ないと思います…。
# __WSAFDIsSet はもっと高度なことをしているかもしれません
# が。

> ファイルディスクリプタは非負の整数値なので、負の値が
> 渡されること自体が誤っているというのは承知していますが、
> 何故動作が異なるのか知りたいのです。

Winsock の SOCKET はファイルディスクリプタじゃない、に一票。
# でも、せめて HANDLE であって欲しかったかな…。
# Winsock2 なら SOCKET に Event Handle を割り当てられるから
# 便利なんですけど。
--
---
Takashi SAKAMOTO (PXG0...@nifty.ne.jp)

Takashi SAKAMOTO

unread,
Jun 20, 2002, 7:16:04 AM6/20/02
to
名称不定 <dev_...@anet.ne.jp> writes:

> しかし、FD_ISSET(-1, &fds) を FD_ISSET(i, &fds) に
> 書き直すと、実行時にセグメント違反(Redhat Linux) や
> バスエラー(FreeBSD) が発生するのです。

(中略)

> ((-1) % 32) = -1 だから ((unsigned long) 1 << -1) = 0 となる。

^^^^^^^^^^^^^^^^^^^^^^^^^


>  ↓
> _fdset_mask(-1) = 0 となるはず。
>  ↓
> 定数では正常に動作するのに、変数だと動作しないのは何故?


^^^^ で書いた部分は本当に正しいですか? JIS X3010:1993 を見
ると未定義動作と書かれているのですが?
# http://www.jisc.go.jp/ から JIS を閲覧することができます。

現に gcc (x86) に対応するコードをはかせてみると、

movl $1,%ebx
movl %ebx,%esi
movl $-1,%ecx
sall %cl,%esi

のようなコードを出してくれます。sall は算術シフトですから、
255 回 1 を左シフトすれば、結果 0x80000000 が返って来る筈
です。

また、定数を書いた場合には、コンパイラが処理してくれるの
ですから、動作が違ってくるのは仕方ないと思われます。
でも…

warning: left shift count is negative

のような警告が出ると思うのですが…


> Visual C++(Windows) ではエラーは発生しませんでした。

Winsock を FreeBSD や Linux の socket の実装と比較するのは
また違うと思います。

Winsock.h を読めば分かると思いますが、

typedef struct fd_set {
u_int fd_count ;
SOCKET fd_array [FD_SETSIZE] ;
} fd_set ;

となっています。

FD_ISSET の動作は、fd_count 分だけfd_array の内容をチェッ
クするという代物に(実際には __WSAFDIsSet が呼ばれているの
で、中身はもっと賢いかもしれませんけど)なっています。

> ファイルディスクリプタは非負の整数値なので、負の値が

Winsock の SOCKET はファイルディスクリプタではありません。
# ので、select に一緒に渡せなくてはまったりします…。

> 渡されること自体が誤っているというのは承知していますが、
> 何故動作が異なるのか知りたいのです。

というわけで、「未定義動作」「実装の全く違うものと比較し
ている」が答えだと思います。

miho

unread,
Jun 20, 2002, 7:23:00 AM6/20/02
to
Shinobu Kumaoka <cog...@sp.hudson.co.jp> wrote in messagenews:<3D1120E3...@sp.hudson.co.jp>...

> ここの計算が、コンパイル時に計算される場合には0になるけれども、
> 実行時に計算される場合には、0xffffffff/32になってしまうからなんでしょうね。
>
> でも、これが0になってしまうのは、コンパイラのバグなんでないかなあ。

いずれの場合にも 0xffffffff/32 になってしまい、その結果、
(p)->fds_bits[(n)/NFDBITS]
が未定義になり、場合によって正常に動作したりしなかったりしても
なんら不思議ではない、というのが真相では。

名称不定

unread,
Jun 20, 2002, 9:22:26 AM6/20/02
to
miho 様、熊岡様、SAKAMOTO 様
ご指摘ありがとうございます。

NFDBITS が unsigned で扱われるようになっていたんですね。
確かに、-1 / 32u == 134217727 になりました。納得です。

また、int i = -1 で ((unsigned long) 1 << i) == 0x80000000 に
なっていました。

どうもありがとうございました。

Shinobu Kumaoka

unread,
Jun 20, 2002, 8:41:59 PM6/20/02
to
熊岡です。

名称不定 wrote:

> > いずれの場合にも 0xffffffff/32 になってしまい、その結果、
> > (p)->fds_bits[(n)/NFDBITS]
> > が未定義になり、場合によって正常に動作したりしなかったりしても
> > なんら不思議ではない、というのが真相では。
>
> NFDBITS が unsigned で扱われるようになっていたんですね。
> 確かに、-1 / 32u == 134217727 になりました。納得です。

なるほど、私の環境はVCなので確かめられなかったのですが、
どうやらたまたま動作が異なっていただけのようですね。

ところで、

>
> また、int i = -1 で ((unsigned long) 1 << i) == 0x80000000 に
> なっていました。

これはx86のSHL命令の制限のようですが、これってCの規格には反しないのでしょうか?

Kazuo Fox Dohzono

unread,
Jun 21, 2002, 1:05:24 AM6/21/02
to
細かい話ですが.

In article <uhejyx...@nifty.ne.jp>
Takashi SAKAMOTO <PXG0...@nifty.ne.jp> writes:

> 255 回 1 を左シフト

8086 だけは違いますが, それ以降の x86 アーキテクチャではシフトカウンタ
が 5 ビットにマスクされるので 31 回です.

--
Kazuo Fox Dohzono / doh...@hf.rim.or.jp

Kazuo Fox Dohzono

unread,
Jun 21, 2002, 1:10:50 AM6/21/02
to
In article <3D127657...@sp.hudson.co.jp>
Shinobu Kumaoka <cog...@sp.hudson.co.jp> writes:

> > また、int i = -1 で ((unsigned long) 1 << i) == 0x80000000 に
> > なっていました。
>
> これはx86のSHL命令の制限のようですが、これってCの規格には反しないのでしょうか?

undefined behavior なので反してはいないようですね.

6.5.7 Bitwise shift operators

[#3] The integer promotions are performed on each of the
operands. The type of the result is that of the promoted
left operand. If the value of the right operand is negative
or is greater than or equal to the width of the promoted
left operand, the behavior is undefined.

Takashi SAKAMOTO

unread,
Jun 21, 2002, 8:36:13 AM6/21/02
to
調べもせずにいい加減なことを書いてしまいました。すみません。

doh...@hf.rim.or.jp (Kazuo Fox Dohzono) writes:

> 8086 だけは違いますが, それ以降の x86 アーキテクチャではシフトカウンタ
> が 5 ビットにマスクされるので 31 回です.

はい。www.intel.com から instruction set の情報(*1)を調べてみると、
きちんと 5bit mask されると書かれていました。
# 32 回以上シフトしても意味ないですし…

(*1) http://www.intel.com/design/pro/manuals/243191.htm

KATAYAMA Yoshio

unread,
Jun 22, 2002, 12:11:39 AM6/22/02
to
In article <u4rfws...@nifty.ne.jp>,
Takashi SAKAMOTO <PXG0...@nifty.ne.jp> writes:

>> 8086 だけは違いますが, それ以降の x86 アーキテクチャではシフトカウンタ
>> が 5 ビットにマスクされるので 31 回です.

>はい。www.intel.com から instruction set の情報(*1)を調べてみると、
>きちんと 5bit mask されると書かれていました。
># 32 回以上シフトしても意味ないですし…

シフト数が可変で 32 以上の時は 0 になって欲しいという場面は、よ
くあります。そういう時は少し面倒になります。

# 6 ビット(64 ビットデータなら 7 ビット)は見て欲しい
--
片山@PFU

Takashi SAKAMOTO

unread,
Jun 22, 2002, 2:55:23 AM6/22/02
to
ka...@pfu.fujitsu.com (KATAYAMA Yoshio) writes:

> シフト数が可変で 32 以上の時は 0 になって欲しいという場面は、よ
> くあります。そういう時は少し面倒になります。

そうですね。
でも、算術シフトには最上位ビットを保存していて欲しいなんて思いが
あります。…先の投稿の勘違いもここに起因するのですが。

# この考えが間違っているのかもしれませんね…。x86 の算術右シフト
# が最上位ビットを保存していたので、左シフトもそうなんだろうとい
# い加減に考えてました。(しかも 8086 の頃から知識が止まってまし
# た)

C 言語の上では未定義動作のようなので、仕方ないかなと思っています。

Kazuo Fox Dohzono

unread,
Jun 24, 2002, 2:57:03 AM6/24/02
to
In article <KATE.02Ju...@flash.tokyo.pfu.co.jp>
ka...@pfu.fujitsu.com (KATAYAMA Yoshio) writes:

> シフト数が可変で 32 以上の時は 0 になって欲しいという場面は、よ
> くあります。そういう時は少し面倒になります。
> # 6 ビット(64 ビットデータなら 7 ビット)は見て欲しい

word size 以上の shift が undefined behavior というのは今回調べて初め
て知った気がするので, 私は今までそういうケースに出くわしたことがないよ
うです.

# 多倍長 shift だと word size 以上はまず word move して, とかだし.

x << (n (mod WORD_SIZE))

と思えば理解出来るんですが (実際そうだし), それならそれで負数の時は逆
方向にシフトして欲しかったり.

KATAYAMA Yoshio

unread,
Jun 24, 2002, 5:46:22 AM6/24/02
to
In article <af6fmq$295p$1...@news2.rim.or.jp>,

doh...@hf.rim.or.jp (Kazuo Fox Dohzono) writes:

>> シフト数が可変で 32 以上の時は 0 になって欲しいという場面は、よ
>> くあります。そういう時は少し面倒になります。
>> # 6 ビット(64 ビットデータなら 7 ビット)は見て欲しい

>word size 以上の shift が undefined behavior というのは今回調べて初め
>て知った気がするので, 私は今までそういうケースに出くわしたことがないよ
>うです.

白状すると、16 ビット CPU のコンパイラーを作った時、これを利用し
て、シフトカウントを 4 ビットしか見ないようにしてしまいました。

#ハードウェア命令が 4 ビットしか見てないので、、、

ワードサイズ以上のシフトが欲しいと思ったケースは幾つかありました
が、殆んど忘却の彼方です。覚えているのは、マスクビットを作る時で、
ビット数が 0 からワードサイズまで可変になる時です。これを

(1u << n) - 1

で済ませたいな、、、というわけです。

># 多倍長 shift だと word size 以上はまず word move して, とかだし.

0 ビットやワードサイズの場合を特別扱いすれば「同上」なのですが、
上式だけで済めば安全なマクロにできるので。

16 ビット CPU の頃はハードウェアが遅くメモリも少なかったので、で
きるだけ特別扱いをしないで済ませたかったというのが本音ですが。

>と思えば理解出来るんですが (実際そうだし), それならそれで負数の時は逆
>方向にシフトして欲しかったり.

PDP-11 が羨ましかったです。使っていた CPU は PDP-11 の影がチラホ
ラ(ベッタリか :-p)してたのに、、、
--
片山@PFU

0 new messages