C qsort中此代码的含义是什么?

时间:2016-10-03 15:38:49

标签: c qsort

void qsort (void *a, size_t n, size_t es, int (*compare)(const void *, const void *)

其中a是数组地址的开头,n是sizeof数组,es是sizeof数组元素。

我在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

3 个答案:

答案 0 :(得分:4)

无论您何时找到该代码,您都可能错误地复制了该代码。我在libutil from Canu中找到了一些非常相似的代码:

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

从FreeBSD的libc中复制此代码可能是非法的(因为版权许可的条款被违反):

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

所以我猜你是从* BSD libc实现得到的。事实上,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作为条件,与<{1}}不是,而是es % sizeof(long) == 1。除此之外,你的翻译是正确的。

这些条件的意图似乎如下:

    es % sizeof(long) != 0不是condition_one时,
  • truea - 已对齐。
  • long不是condition_two的倍数时,
  • truees
  • long完全condition_three时,
  • truees

结果,

  • long当你没有足够的保证关于要交换聪明的元素时,
  • swaptype == 2适用于具有沿swaptype == 1边界对齐的元素的数组(注意:但不一定对齐为 longs!)和
  • long适用于与之前描述匹配的数组,这些数组也包含swaptype == 0大小的元素。

在这种情况下有明确的类型转换,因为long的类型为afor which type arithmetic is undefined。但是,请注意void*也未定义:

  

当减去两个指针时,两个指针都指向同一个数组对象的元素,或者指向数组对象的最后一个元素的元素;结果是两个数组元素的下标的差异。

(C11草案N1570,第6.5.6节,第93页和第94页第9条。)

它在C11中没有完全拼写出来,但是空指针不是((char *)a - (char *)0)指向的对象的同一个数组的一部分,因此违反了指针算术的基本规则,所以行为未定义。

答案 1 :(得分:1)

宏试图用语言C来移植地检查对齐,这实际上不允许进行这样的测试。所以我们从指针中减去空指针以获得一个整数,然后取模数为long的大小。如果结果为零,则数据是长对齐的,我们可以访问多个长度。如果不是,我们可以尝试其他方案。

答案 2 :(得分:0)

正如评论中所述,您提供的宏定义不会扩展为有效的C代码,因为它涉及计算(char*)0 % sizeof(long),其中%的左侧操作数具有类型{{1} }。这不是整数类型,但char *的两个操作数都需要具有整数类型。

此外,宏的扩展具有不平衡的括号。这不是本身错误,但它使得宏使用起来很棘手。此外,即使运算符优先级产生合理的结果,使用括号和额外的空格也可以帮助人工解释代码,不会降低执行速度,并且可以忽略额外的编译成本。

所以,我认为所需的宏更像是这样:

%

我考虑将倒数第二行写为

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

降低表达式的复杂性,但其可理解性略有降低。无论如何,意图似乎是将 : (es != sizeof(long)) 设置为:

  • swaptype如果2 a字节边界上对齐,其中nn中的字节数1}},或者如果long不是es大小的整数倍;否则
  • long如果1不等于es的大小;否则
  • long

与您的解释相似但不完全相同。但请注意,由于0,即使此代码也有未定义的行为。只有当两个指针指向同一个对象或刚好超过同一个对象的末尾时,评估该差异才会定义行为,并且(char*)a - (char*)0不会指向(仅)指向任何对象的结尾或者只是超出任何对象的结尾。

你具体问过:

  

但我不明白为什么要实现类型转换,(char *)a。

这是因为指针算术是根据指向类型定义的,所以(1),一致程序不能用(char *)0执行算术,(2)代码想要结果减法与void *运算符(字节)的结果具有相同的单位。

  

这条线的意义何在?

sizeof

该行未出现在您呈现的宏中,并且由于括号不平衡而不是完整的表达式。它似乎试图确定(char*)a- (char*)0)% sizeof(long)==1 是否指向一个a字节边界,其中n如上所定义,但同样,评估指针差异具有未定义的行为。另请注意,对于整数n,在布尔上下文中计算的x与在同一上下文中计算的x % sizeof(long) == 1具有不同的含义。后者在你描述的背景下更有意义。