编写一个通用函数来检测指针数组是否包含 NULL

Writing a generic function to detect if an array of pointers contains NULL

我想编写一个通用函数来检测指向某个任意类型的指针数组是否包含 NULL。我最初的尝试是这样的:

bool find_null (void *ptrs, size_t num_ptrs) {
    void **array = ptrs;
    size_t i;
    for (i = 0; i < num_ptrs; ++i) {
        if (array[i] == NULL) return true;
    }
    return false;
}

有人指出,这可能会导致严格的别名冲突,因为指向 Foo 的指针数组将作为指向 void 的指针数组进行访问,而 void 未被列为其中之一在 C.2011 §6.5¶7.

中允许访问对象的允许方式

我可以重写函数以访问指针数组,改为 unsigned char *,但我不确定如何在不破坏严格别名的情况下执行 NULL 检查。有人可以提供有效的技术吗?

bool find_null (void *ptrs, size_t num_ptrs) {
    unsigned char *array = ptrs;
    void *p;
    size_t i;
    for (i = 0; i < num_ptrs; ++i) {
        memcpy(&p, array + i * sizeof(p), sizeof(p));
        if (p == NULL) return true;
        /*
         * Above seems to still break strict aliasing.
         * What should be done instead?
         */
    }
    return false;
}

目标是编写一个通用函数,它的工作方式与特定类型的函数相同。换句话说,以下函数的通用版本:

bool find_null_Foo (Foo *array[], size_t num_ptrs) {
    size_t i;
    for (i = 0; i < num_ptrs; ++i) {
        if (array[i] == NULL) return true;
    }
    return false;
}

您无法使用您提供的特定界面来完成此操作,但您可以通过这种有点笨拙的方式来完成此操作:

bool find_null (const void *array, size_t num_ptrs, size_t ptr_size,
        const void *null) {
    const char (*ptr_array)[ptr_size] = array;
    size_t i;
    for (i = 0; i < num_ptrs; ++i) {
        if (!memcmp(array[i], null, ptr_size)) return true;
    }
    return false;
}

你可以这样称呼它:

struct Foo;

#define ARRAY_SIZE 53
int main(void) {
    struct Foo *my_array[ARRAY_SIZE] = { ... };
    struct Foo * const foo_null = (struct Foo *) 0;
    if (find_null(my_array, ARRAY_SIZE, sizeof(*my_array), &foo_null)) {
        puts("It contains NULL");
    } else {
        puts("It does not contain NULL");
    }
}

请注意,这假设所讨论类型的空指针只有一种表示形式,这在许多实现中都是正确的,但语言不需要。

另请注意,这实际上并不是特定于查找空指针的,因此实际上您可以使用它在指针数组中搜索任何指针值。事实上,它甚至不特定于指针数组——您可以使用它在任何数组中搜索任何值,只要字节对字节相等是合适的匹配标准(它不适用于结构或工会,可能不适用于某些其他类型)。

此外,如果这适合您,那么您可以设计一个包装器宏,使其更容易用于一些更常见的场景。

基于,通用函数似乎需要额外的参数。由于无法将指针的 unsigned char 表示形式转换为 void *,因此必须通过回调来完成。

bool find_null_generic (const void *ptrs, size_t ptr_sz, size_t num_ptrs,
                        const void *(*convert)(const void *)) {
    const unsigned char *array = ptrs;
    size_t i;
    for (i = 0; i < num_ptrs; ++i) {
        if (convert(array + i * ptr_sz) == NULL) return true;
    }
    return false;
}

对于指向Foo的指针的假设数组:

const void *convert_Foo (const void *data) {
    const Foo *foo;
    memcpy(&foo, data, sizeof(foo));
    return foo;
}

Foo *foo_array[N] = {...};
bool result = find_null_generic(foo_array, sizeof(Foo *), N, convert_Foo);

不能保证泛型函数在指向不同类型的指针可能具有不同表示 and/or 大小的系统上按预期工作。值得庆幸的是,如今这样的架构非常罕见。 Posix 例如,兼容系统保证对所有指针类型使用相同的表示和大小。