通过指向指针参数的指针进行分配

Allocating through a pointer to a pointer parameter

我有这个宏:


/*
 * int  callocs(type **ptr, size_t nmemb);
 *
 * Safe & simple wrapper for `calloc()`.
 *
 * PARAMETERS:
 * ptr:     Memory will be allocated, and a pointer to it will be stored
 *          in *ptr.
 * nmemb:   Number of elements in the array.
 *
 * RETURN:
 *  0:          OK.
 *  != 0:       Failed.
 *
 * FEATURES:
 * - Safely computes the element size (second argument to `calloc()`).
 * - Returns non-zero on error.
 * - Doesn't cast.
 * - The pointer stored in `*ptr` is always a valid pointer or NULL.
 *
 * EXAMPLE:
 *      #define ALX_NO_PREFIX
 *      #include <libalx/base/stdlib/alloc/callocs.h>
 *
 *              int *arr;
 *
 *              if (callocs(&arr, 7))       // int arr[7];
 *                      goto err;
 *
 *              // `arr` has been succesfully allocated here
 *              free(arr);
 *      err:
 *              // No memory leaks
 */
#define callocs(ptr, nmemb) (                                           \
{                                                                       \
        __auto_type     ptr_    = (ptr);                                \
                                                                        \
        *ptr_   = calloc(nmemb, sizeof(**ptr_));                        \
                                                                        \
        !(*ptr_);                                                       \
}                                                                       \
)

而且我希望它成为提高安全性的功能。这将是第一个想法:

#define callocs(ptr, nmemb) (                                           \
{                                                                       \
        __auto_type     ptr_    = (ptr);                                \
                                                                        \
        callocs__(ptr_, nmemb, sizeof(**ptr_));                         \
}                                                                       \
)


int     callocs__(void **ptr, ptrdiff_t nmemb, size_t size)
{

        if (nmemb < 0)
                goto ovf;

        *ptr    = calloc(nmemb, size);

        return  !*ptr;
ovf:
        errno   = ENOMEM;
        *ptr    = NULL;
        return  ENOMEM;
}

但随后编译器抱怨:

error: passing argument 1 of callocs__ from incompatible pointer type [-Werror=incompatible-pointer-types]
note: in expansion of macro callocs
note: expected void ** but argument is of type struct My_Struct **

简单显式转换为 (void **) 安全吗?:

#define callocs(ptr, nmemb) (                                           \
{                                                                       \
        __auto_type     ptr_    = (ptr);                                \
                                                                        \
        callocs__((void **)ptr_, nmemb, sizeof(**ptr_));                \
}                                                                       \
)

我所说的安全是指从标准的角度(我猜不是)和实现的角度(在这个特定情况下,GNU C)(我不确定)。

如果不是,void * 类型的中间指针是否足够?:

#define callocs(ptr, nmemb) (                                           \
{                                                                       \
        __auto_type     ptr_    = (ptr);                                \
        void            *vp_;                                           \
        int             ret_;                                           \
                                                                        \
        ret_    = callocs__(&vp_, nmemb, sizeof(**ptr_))                \
        *ptr_   = vp_;                                                  \
        ret_;                                                           \
}                                                                       \
)

还有其他解决办法吗?

Is a simple explicit cast to (void **) safe?:
And if not, would an intermediate pointer of type void * be enough?:

(void **) 方法是合理的,但依赖于 some_other_type **void ** 兼容。它属于:

A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined. Otherwise, when converted back again, the result shall compare equal to the original pointer. C17dr § 6.3.2.2 7

因此,在极少数情况下,指向一种类型的指针的对齐要求与指向 void * 的指针的对齐要求不同。我怀疑这样的实现是否存在。需要说明的是,这是指针类型对齐要求的差异,而不是它们指向的对象。


Is there any other solution?

为避免 "passing argument 1 of callocs__ from incompatible pointer type" 并保持类型正确性,请考虑将辅助函数 callocs__() 设为

void *callocs__(ptrdiff_t nmemb, size_t size, int *error);
// ^------------return pointer to allocated data

然后

#define callocs(ptr, nmemb) (                             \
{                                                         \
        __auto_type     ptr_    = (ptr);                  \
        int error;                                        \
        *ptr = callocs__(nmemb, sizeof(**ptr_), &error);  \
        error;                                            \
}                                                         \
)

注意:我对制作一个函数来处理 *alloc() 的微妙 vagaries 的整体 objective 表示赞赏。