【问题标题】:Pointer arithmetic for structures gives strange result结构的指针算法给出了奇怪的结果
【发布时间】:2013-12-10 19:27:03
【问题描述】:

我有以下功能:

void deleteInventory( struct book** inventory )
{
    struct book* ptr = NULL;
    ptr = *inventory;

    while( length != 0)
    {
        free(ptr);
        length--;
        ++ptr;
    }
    free(inventory);
}

结构:

#define MAX_LENGTH 64
#define ISBN_LENGTH 11

struct book
{
    char isbn[ISBN_LENGTH];
    char title[MAX_LENGTH];
    char author[MAX_LENGTH];
    int year;
    float price;
};

结构总大小:147 字节 (11 + 64 + 64 + 4 + 4) sizeof(struct book) 返回:148 字节

我的库存变量是结构数组,包含 3 条记录

现在,函数中的调试结果(没有高2字节的地址):

inventory[0] = 0x1350
inventory[1] = 0x14e0
inventory[2] = 0x1670

内存差异:400字节,OK

第一次迭代一切正常,第一条记录删除没有问题

但是之后

++ptr;

ptr 将包含:0x13e4

inventory[0] - ptr = 148 bytes - 根据结构的大小纠正差异

但它不引用下一条记录,因为内存中下一条记录的地址:0x14e0,而且我正在损坏堆。

任何建议:为什么?

【问题讨论】:

  • deleteInventory 中,length 变量在使用时未初始化。
  • 您可能已经对关闭的第二个指针进行了指针运算。 ——仅建议。有专门的 malloc 库和 valgrind 等工具来查明您的问题。编辑:@mbratch 看起来有一个解决方案,除非长度是一个全局变量。 +1
  • @mbratch length 是全局变量 :)
  • 为什么不使用 std::vector 和 std::string 而不是 char 数组?
  • 你能构造一个minimal test-case吗?

标签: c pointers


【解决方案1】:

你说过inventory是一个数组,这很好。

您说过book 占用 148 个字节(当您计算填充时),这很好。

你没有说的是你放在inventory中的指针是一次性分配的,而你有一个指针数组这一事实对我来说意味着它们不是。

无论您如何创建每个books,它似乎都是动态的。不能保证这些书是连续的,只能保证指向它们的指针是连续的。

改变你的功能:

void deleteInventory( struct book** inventory )
{
    struct book* ptr = NULL;
    for (int i = 0; i < length; ++i) {
        ptr = inventory[i];
        free(ptr);
    }
    length = 0;
    free(inventory);
}

这将读取指向book 的连续指针数组以删除所有books,而不假设books 本身是连续分配的。

【讨论】:

  • 这是最简单的解决方案,没兴趣!
  • @Oli Charlesworth,主要目标是使用指针算法,而不是索引
【解决方案2】:

我认为您从inventory 中删除错误。在你free(ptr) 之后,ptr 之后的下一个内存块可能是垃圾内存。我认为这是删除库存的正确方法:

void deleteInventory( struct book** inventory )
{
    book** i =  inventory;
    while( length != 0)
    {
        free(*i);
        length--;
        ++i;//notice the difference (++i) instead of (++(*i))
    }
    free(inventory);
}

【讨论】:

  • ++i 将递增指针对指针。不是期望的行为。
  • 它可以工作,因为它不是结构数组,而是结构指针数组。
  • @Oli Charlesworth 我错过了什么吗? free(ptr) 然后 ptr++free(ptr) 再次,这是 UB
  • @Raxvan:但是在 OP 的代码中,ptr 被初始化为*inventory(注意*)。很明显,OP 没有充分描述他的内存布局......
【解决方案3】:

我们只能推测inventory 及其内容是如何产生的,但由于您单独释放它们,您可能也单独拥有malloc()d 它们。但是您可以通过 ptrptr++ 来遍历它们,它们假定连续分配的结构成员,就像您在数组中一样。

但是没有任何保证(一般的实现也不会这样做)malloc() 将以任何方式返回连续分配的连续存储。

这就是为什么您看到它们的距离超过了您预期的 148 字节。由于库存可能是分配指针的数组,因此您要做的是:

while( length != 0)
{
    free(*inventory);
    length--;
    ++inventory;
}

【讨论】:

  • 最后我将如何为inventory释放内存?
  • @denis90:还是这样,我只提到了关键部分,没有重写整个程序。
【解决方案4】:

指针算术不是问题...问题是您对数组布局方式的假设是错误的。

我猜你做了类似的事情:

  length = 3;
  inventory = (struct book**) malloc(sizeof(struct book *) * length);
  ...
  inventory[0] = (struct book*) malloc(sizeoof(struct book));
  inventory[1] = ... etc ;

然后你将ptr分配给inventory[0];

  ptr = *inventory;

但是你假设

 ++ptr == inventory[1];

事实并非如此。

【讨论】: