2016-10-03 7 views
1
void qsort (void *a, size_t n, size_t es, int (*compare)(const void *, const void *) 

ここで、aは配列アドレスの先頭、nはsizeof配列、esは配列要素のサイズです。Cコードでこのコードの意味は?

私が理解できないCのqsortのソースコードを読みました。コードは次のとおりです。

#define SWAPINT(a,es) swaptype = ((char*)a- (char*)0 % sizeof(long) || \ 
     es % sizeof(long) ? 2: es == sizeof(long)? 0 : 1 

私は

if(((char*)a- (char*)0)% sizeof(long))==1 || es % sizeof(long)==1) 
    swaptype = 2; 
else if(es== sizeof(long)) 
    swaptype = 0; 
else 
    swaptype = 1; 

、でこのマクロを解釈しかし、型変換が実装されている理由を私は理解していない、(CHAR *)A。

この行の意味は?

(char*)a- (char*)0)% sizeof(long)==1 
+0

コードはかなり壊れているようです。マクロには少なくとも1つの構文エラーがあり、括弧がありません。また、(char *)a - (char *)0は、何かが欠けていない限り、何もしないでください。 as should(char *)0%sizeof(long)です。 – jforberg

+0

'(char *)0%sizeof(long)'はポインタ型が算術型ではないので意味をなさない。これが何であれ、これはCに準拠していません。このコードはどこで見つかりましたか?そしてあなたはそれを正しくコピーしたのですか? –

+1

'%'は '-'を打ち消すので'(char *)a-(char *)0%sizeof(long) 'は'(char *)a - ((char *)0%sizeof(long)) 'です。確かに '((char *)a-(char *)0)%sizeof(long)'が必要でした。 – chux

答えて

4

このコードを見つけた場合は、おそらくそれを間違ってコピーした可能性があります。私はlibutil from Canuにいくつかの非常に類似したコードが見つかりました:

c.swaptype = ((char *)a - (char *)0) % sizeof(long) || \ 
    es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1; 

このコードは(著作権ライセンスの条項に違反しているため)illegitimallyそうだったFreeBSDのはlibcからコピー:

//__FBSDID("$FreeBSD: src/lib/libc/stdlib/qsort.c,v 1.12 2002/09/10 02:04:49 wollman Exp $"); 

だから私はあなたを推測しています* BSDのlibc実装から入手しました。 Indeedd FreeBSDのクイックソートの実装はthe SWAPINIT macro(ないSWAPINT)が含まれています

#define SWAPINIT(TYPE, a, es) swaptype_ ## TYPE =  \ 
     ((char *)a - (char *)0) % sizeof(TYPE) ||  \ 
     es % sizeof(TYPE) ? 2 : es == sizeof(TYPE) ? 0 : 1; 

解析した後、あなたは上記のコードを条件として、およそ

condition_one = ((char *)a - (char *)0) % sizeof(long); 
condition_two = es % sizeof(long); 
condition_three = es == sizeof(long); 
c.swaptype = (condition_one || condition_two) ? 2 : condition_three ? 0 : 1; 

なおcondition_twoと同じであることを見つける必要がありますではなく、es % sizeof(long) == 1と同じですが、むしろes % sizeof(long) != 0と同じです。それ以外はあなたの翻訳は正しかった。


これらの条件の目的は、以下のように思える:along -alignedないとき

  • condition_onetrueです。
  • condition_twoは、eslongの倍数でない場合、trueです。
  • condition_threeは、esが正確にlongの場合、trueです。あなたはスワップについて賢いする要素について十分な保証を持っていない時にその結果

  • swaptype == 2
  • swaptype == 1
  • longに沿って整列されている要素を持つ配列のために意図されています境界線(注: は必ずと同じです!)、および
  • swaptype == 0は、上記の説明と一致する配列も対象とし、要素にはlongも含まれています。

avoid*for which type arithmetic is undefinedを入力しているため、明示的な型変換は、このケースです。しかしながら、((char *)a - (char *)0)があまりにも未定義であることに注意:

二つのポインタが減算される場合、両方が同じ配列オブジェクトの要素を指し、または配列オブジェクトの最後の要素過去1なければなりません。結果は2つの配列要素の下付き文字の違いになります。

(C11ドラフトN1570、セクション6.5.6、ページ93と94の条項9)

それはまさにC11で綴られていないが、NULLポインタが同じ配列の一部ではないですオブジェクトがaで指し示されているため、ポインタ算術の基本規則に違反するため、動作は未定義です。

1

マクロは、実際にそのようなテストを行うことができない言語Cで移植可能かどうかをチェックしようとしています。そこで、私たちのポインタからヌルポインタを減算して整数を取得し、次にモジュラスをlongのサイズにします。結果がゼロの場合、データはロング・アライメントされ、longとしてアクセスできます。そうでない場合は、他のスキームを試すことができます。

+0

この場合、C11の '_Alignas'と' _Alignof'を使用できませんでしたか? –

+0

たとえ短くても、私はこの答えをよく理解していると思います。しかし、その整数は何を意味しますか?配列の開始アドレスですか?アレイが長いとみなされるのはなぜ重要なのでしょうか?それは例えばバイト配列の場合ですか? – Asoub

+0

2つのポインタを減算すると、それらの間の場所の数を表す整数(intではない)が得られます。 char *からNULLを引くことはノーオペレーションですが、コンパイラーはポインタを整数として受け入れてモジュラスを許すようにトリックします。メモリ空間がセグメント化されている場合は、少し面倒ですが、それは別の話です。 –

0

コメントに記載されているように、%の左側のオペランドがタイプchar *である(char*)0 % sizeof(long)という計算を含むので、あなたが提示するマクロ定義は有効なCコードに展開されません。これは整数型ではありませんが、両方のオペランド%は整数型である必要があります。

さらに、マクロの展開にはアンバランスな括弧があります。それはが本来ではありませんが、そのマクロを使いこなすのは難しいことです。さらに、演算子の優先順位が合理的な結果をもたらす場合であっても、括弧と余分な空白を使用することで、コードの人間の解釈を助け、実行速度を損なうことなく、余分なコンパイルコストを無視できます。

だから、私は、目的のマクロがもっとこのようなものだと思う。私が表現の複雑さを軽減するために

 : (es != sizeof(long)) 

として最後から二番目の行を書く代わりに検討したい

#define SWAPINT(a,es) swaptype = (         \ 
    ((((char*)a - (char*)0) % sizeof(long)) || (es % sizeof(long))) \ 
     ? 2               \ 
     : ((es == sizeof(long)) ? 0 : 1))       \ 
) 

その理解のためのわずかな費用。 aない nlongのバイト数である n -byte境界上に整列され、又は esがある場合であれば

  • 2:いずれの場合においても、目的は、にswaptypeを設定するように見えますサイズの整数倍ではないlong;そうでなければ
  • 1eslongのサイズと等しくない場合、そうでない場合は
  • あなたの解釈に、類似しているが、同一ではありません
  • 0

。ただし、このコードでも、(char*)a - (char*)0のために未定義の動作があることに注意してください。その違いを評価したのは、両方のポインタが同じオブジェクトを指し示すか、または末尾をちょうど超えた場合のみであり、(char *)0はオブジェクトの終わりを指し示すか、指し示すことはありません。

あなたが、具体的に尋ねた:

しかし、型変換は、(char型*)実装されている理由を私は理解していません。ポインタ演算が尖った-TO型の用語で定義されているので、(1)、適合プログラムはvoid *で算術演算を実行することができない、および(2)コードは、減算結果を望んでいるので、実行される

sizeof演算子(バイト)の結果と同じ単位になります。

この行の意味は?

(char*)a- (char*)0)% sizeof(long)==1 

その行は、あなたが提示したマクロに表示されない、そしてそれがために、アンバランス括弧の完全な表現ではありません。 anバイト境界を超えて1を指しているかどうかを判断しようとしているように見えますが、nは上記と同じですが、ポインタの違いを評価すると未定義の動作があります。 xの整数の場合、ブール値コンテキストで評価されるx % sizeof(long) == 1は、同じコンテキストで評価されるx % sizeof(long)とは異なる意味を持つことにも注意してください。後者は、あなたが記述した文脈の中でより意味があります。

関連する問題