【问题标题】:Check and align buffer检查并对齐缓冲区
【发布时间】:2020-05-15 02:06:08
【问题描述】:

我试图了解如何检查指针是否对齐并最终对齐。

为了理解它,我采用了这个函数:

#define PJ_POOL_ALIGNMENT 8

PJ_DEF(pj_pool_t*) pj_pool_create_on_buf(const char *name,
                 void *buf,
                 pj_size_t size)
{
#if PJ_HAS_POOL_ALT_API == 0
struct creation_param param;
pj_size_t align_diff;

PJ_ASSERT_RETURN(buf && size, NULL);

if (!is_initialized) {
if (pool_buf_initialize() != PJ_SUCCESS)
    return NULL;
is_initialized = 1;
}

/* Check and align buffer */
align_diff = (pj_size_t)buf;
if (align_diff & (PJ_POOL_ALIGNMENT-1)) {
    align_diff &= (PJ_POOL_ALIGNMENT-1);
    buf = (void*) (((char*)buf) + align_diff);
    size -= align_diff;
}

param.stack_buf = buf;
param.size = size;
pj_thread_local_set(tls, &param);

return pj_pool_create_int(&stack_based_factory, name, size, 0, 
              pj_pool_factory_default_policy.callback);
#else
PJ_UNUSED_ARG(buf);
return pj_pool_create(NULL, name, size, size, NULL);
#endif
}

显然我感兴趣的部分是/*检查和对齐缓冲区*/ 我认为我唯一理解的是:

让我们专注于 if。 这要验证缓冲区是否与 8 字节多地址对齐。如果if的条件没有对齐,则返回0以外的数字,然后进行对齐,否则,也只有一个位为1,跳过IF就足够了。为了获得这个结果,他们将变量 PJ_POOL_ALIGNMENT 设为 7 (0111),然后他们将与分配缓冲区的地址进行 AND。考虑到如果地址不对齐,我想得到一个不是0的数字,操作如下。

0000... 0111 和

xxx。 . . x100


0000... 0100 未对齐

如果最后 3 位中有 1(或更多 1),因此我知道它与 8 字节块不对齐:x AND 1 = 0,则 if 将为真。然后它会进入校正块。

但是 if 块对我来说是模糊的。 可以确认我的推理是否正确并让我理解障碍的人。

【问题讨论】:

  • 基本上对齐一个块意味着将其起始地址四舍五入到对齐的下一个倍数。对我来说,这段代码看起来很奇怪。我希望在 if 块中增加一行:align_diff = PJ_POOL_ALIGNMENT-align_diff;
  • 您的align_diff 计算需要按照上面@Gerhardh 的建议进行更正。您可以在一个语句中计算align_diff,使用无符号类型的 2 的补码否定规则:align_diff = (pj_size_t)(-(pj_size_t)buf) & (PJ_POOL_ALIGNMENT-1);
  • 澄清一下:这是你的代码吗?您是否尝试理解其他人的代码,或者这是您尝试过的,但您想了解为什么它不起作用?
  • 不是我的代码,是正在研究的项目的代码:pjsip.org
  • 我已将错误报告通过电子邮件发送到项目邮件列表。

标签: c memory alignment buffer


【解决方案1】:

当前对齐代码不正确。它确定与下对齐边界的对齐差异,并错误地将其添加到指针值以到达上对齐边界:

xxxxx000 + 000 = xxxxx000 (OK - no change)
xxxxx001 + 001 = xxxxx010 (WRONG)
xxxxx010 + 010 = xxxxx100 (WRONG)
xxxxx011 + 011 = xxxxx110 (WRONG)
xxxxx100 + 100 = xxxxy000 (OK - rounded up)
xxxxx101 + 101 = xxxxy010 (WRONG)
xxxxx110 + 110 = xxxxy100 (WRONG)
xxxxx111 + 111 = xxxxy110 (WRONG)

与上对齐边界的差异是与下对齐边界的差异的2的补码,以对齐大小为模:

xxxxx000 + 000 = xxxxx000 (OK - no change)
xxxxx001 + 111 = xxxxy000 (OK - rounded up)
xxxxx010 + 110 = xxxxy000 (OK - rounded up)
xxxxx011 + 101 = xxxxy000 (OK - rounded up)
xxxxx100 + 100 = xxxxy000 (OK - rounded up)
xxxxx101 + 011 = xxxxy000 (OK - rounded up)
xxxxx110 + 010 = xxxxy000 (OK - rounded up)
xxxxx111 + 001 = xxxxy000 (OK - rounded up)

当前对齐代码可以通过添加单行来纠正,将下对齐差异转换为上对齐差异:

/* Check and align buffer */
align_diff = (pj_size_t)buf;
if (align_diff & (PJ_POOL_ALIGNMENT-1)) {
    align_diff &= (PJ_POOL_ALIGNMENT-1);
    align_diff = PJ_POOL_ALIGNMENT - align_diff; // upper alignment
    buf = (void*) (((char*)buf) + align_diff);
    size -= align_diff;
}

或者,可以直接在if之前确定上对齐差异:

/* Check and align buffer */
align_diff = (pj_size_t)-(pj_size_t)buf & (PJ_POOL_ALIGNMENT-1);
if (align_diff != 0) {
    buf = (void*) (((char*)buf) + align_diff);
    size -= align_diff;
}

可以说(并且一直是!)这比原始版本的可读性差。

事实上,if 可以省略,因为加零没有区别:

/* Check and align buffer */
align_diff = (pj_size_t)-(pj_size_t)buf & (PJ_POOL_ALIGNMENT-1);
buf = (void*) (((char*)buf) + align_diff);
size -= align_diff;

关于align_diff = (pj_size_t)-(pj_size_t)buf & (PJ_POOL_ALIGNMENT-1);(pj_size_t)buf 将指针转换为无符号整数类型,- 否定值,初始 (pj_size_t) 使用 2 的补码算法将否定值转换为无符号整数类型。 & (PJ_POOL_ALIGNMENT-1) 将模 PJ_POOL_ALIGNMENT 等效转换为 % PJ_POOL_ALIGNMENT(因为 PJ_POOL_ALIGNMENT 是 2 的幂)。

严格来说,为了避免未定义的行为,上述指向整数转换的指针应该使用uintptr_t(由#include <stdint.h>定义)而不是pj_size_t来完成:

align_diff = (uintptr_t)-(uintptr_t)buf & (PJ_POOL_ALIGNMENT-1);

关于buf = (void*) (((char*)buf) + align_diff);,不允许对void * 值进行指针运算(至少在标准C 中),因此(char*)buf 将其转换为指向char 的指针。由于 sizeof(char) 的定义是 1 个字节,因此 + align_diff 根据需要将指针推进 align_diff 字节。然后(void*) 将其转换回void *,然后将其分配回buf。这个(void*) 在C 中可以省略(但在C++ 中不能),因此该语句可以重写为:

buf = (char*)buf + align_diff;

这可以说更具可读性。

【讨论】:

  • 我会将align_diff = (pj_size_t)-(pj_size_t)buf & (PJ_POOL_ALIGNMENT-1); 添加到我的“如何编写同事不理解的 C”指南中。谢谢。
  • @4386427 也许您应该将它添加到“如何让我的同事印象深刻和惊奇”指南中。 :)
  • 不,不是给人留下深刻印象......而是在我的“如何确保您的代码在审核中被拒绝”指南中
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多