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
答案 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
时,true
为a
- 已对齐。long
不是condition_two
的倍数时,true
为es
。long
完全condition_three
时,true
为es
。结果,
long
当你没有足够的保证关于要交换聪明的元素时,swaptype == 2
适用于具有沿swaptype == 1
边界对齐的元素的数组(注意:但不一定对齐为 longs!)和long
适用于与之前描述匹配的数组,这些数组也包含swaptype == 0
大小的元素。在这种情况下有明确的类型转换,因为long
的类型为a
,for 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
字节边界上对齐,其中n
是n
中的字节数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
具有不同的含义。后者在你描述的背景下更有意义。