这段代码是什么意思?

7

我发现了一段代码。我不理解它。似乎变量__rem完全没有用处。下面的这行代码什么有用的工作也没做:

(void)(((typeof((n)) *)0) == ((uint64_t *)0));   \

整个代码段如下所示:
#define do_div(n,base)  do{             \
    uint32_t __base = (base);           \
    uint32_t __rem;                 \
    (void)(((typeof((n)) *)0) == ((uint64_t *)0));   \
    if (((n) >> 32) == 0) {         \
        __rem = (uint32_t)(n) % __base;     \
        (n) = (uint32_t)(n) / __base;       \
    } else                      \
        __rem = __div64_32(&(n), __base);   \
    __rem;                      \
 }while(0)
/* Wrapper for do_div(). Doesn't modify dividend and returns
 * the result, not reminder.
 */
static inline uint64_t lldiv(uint64_t dividend, uint32_t divisor)
{
    uint64_t __res = dividend;
    do_div(__res, divisor);
    return(__res);
}

这里为什么会有无用的代码?


1
编译器的健全性检查? - EOF
1
请再提供有关工具链的信息。这显然是编译器内在的(标识符中的双下划线)。 - Quentin
1
__rem 是无用的,也许这段代码的作者从其他地方复制粘贴了它,并决定不去管它。一般来说,在执行此算法后,您可能想要知道 rem。猜测一下,也许这段代码曾经在 gcc 的 statement-expression 中,其中结尾处的 __rem; 意味着 __rem 的最终值是语句表达式的“返回值”。 - M.M
@Quentin 我一点也不确定。历史上,标准曾经说过某些标识符是“保留”的,但并没有说明保留给谁。编译器制造商决定将其解释为“保留给编译器”,而意图实际上可能是“保留给 C 语言的未来扩展”。这在 C99 中有所澄清,但那时候没有编译器会重写他们所有的内部库。这一切都相当混乱。 - Lundin
@Lundin 存在 _Bool_Alignas 可能暗示了第二个,但确实不清楚。幸运的是,从用户的角度来看,答案只是“不要”。 - Quentin
显示剩余5条评论
1个回答

12

1. (void)(((typeof((n)) *)0) == ((uint64_t *)0));

参见Linux/include/asm-generic/div64.h

这不必要的指针比较是为了检查类型安全性(n 必须是 64 位)

示例:

n 必须是 int,但它是short

void main()
{
    short n;
    (void)(((typeof((n)) *)0) == ((int *)0));
}

我们得到了警告:comparison of distinct pointer types lacks cast

编译命令为:gcc -o main main.c

编译器版本为:gcc (GCC) 4.9.2 20141101 (Red Hat 4.9.2-1)

结论:

指针比较并非无用。如果传递给do_div()的变量类型错误,它会生成一个警告。

2. __rem

花括号中的代码是gcc语句表达式。 __remdo_div()的返回值。

例子:

#include <stdio.h>

#define do_div(n,base)  ({ \
    int __rem = n % base;  \
    n /= base;             \
    __rem;                 \
})

int main()
{
    int a = 9;
    int b = 2;
    int c = 0;

    printf("%i / %i = ", a, b);
    c = do_div(a, b);
    printf("%i, reminder = %i\n", a, c);
    return 0;
}

输出:9 / 2 = 4,余数=1

在上面的例子中,c = do_div(a, b) 等价于 c = ({int rem = a % b; a /= b; rem;})

结论:

__rem 并不无用,它是 do_div() 的“返回值”。


默认情况下会产生警告,这很奇怪,因为它是完全合法的:C11草案标准:6.3.2.3指针4[...]任何两个空指针都应该相等比较。[...] - EOF
哦,它在我的gcc 4.8.4上也产生了一个警告。我只是惊讶它不需要-Wall-Wextra或类似的东西。 - EOF
@EOF 表示它们在转换为相同类型后应该相等。然而,在这种情况下,需要进行强制转换才能转换为相同类型。(C11 6.5.8/2) - M.M
@MattMcNabb:我猜你是指6.5.9/2? - EOF
你好!这是一个非常晚的问题,但原始源代码似乎没有使用do_div()的结果值_rem。这里的语句__rem;是什么意思? - Chan Kim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接