【问题标题】:Allocating through a pointer to a pointer parameter通过指向指针参数的指针分配
【发布时间】:2020-04-19 11:50:39
【问题描述】:

我有这个宏:


/*
 * 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 void** an acceptable type in ANSI-C?
  • @chux-ReinstateMonica 我的意思是将所有可以在函数中的代码移到一个函数中,剩下的(sizeof(**ptr_),不能进入函数)进入不太危险的宏。
  • CacahueteFrito “但在 POSIX 上所有指针都具有相同的表示”您确定 POSIX 指定对象指针和函数指针具有相同的表示吗? IAC,我认为这只是这篇文章关注的对象指针。
  • @cacahuete:这是关于的可转换性,而不是表示
  • "如果 nelem 或 elsize 为 0,则 应返回空指针或可成功传递给 free() 的唯一指针值。 "

标签: c pointers gcc malloc void-pointers


【解决方案1】:

简单的显式强制转换为 (void **) 安全吗?:
如果没有,void * 类型的中间指针是否足够?:

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

指向对象类型的指针可以转换为指向不同对象类型的指针。如果结果指针未正确对齐引用的类型,则行为未定义。否则,当再次转换回来时,结果将等于原始指针。 C17dr § 6.3.2.2 7

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


还有其他解决办法吗?

为避免“从不兼容的指针类型传递 callocs__ 的参数 1”并保持类型正确性,请考虑将辅助函数 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 的总体目标。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-05-16
    • 1970-01-01
    • 2020-08-21
    • 1970-01-01
    • 2014-08-23
    • 1970-01-01
    • 1970-01-01
    • 2011-04-02
    相关资源
    最近更新 更多