【问题标题】:malloc() successful but allocates less memory than expectedmalloc() 成功但分配的内存少于预期
【发布时间】:2013-10-23 00:10:11
【问题描述】:

我使用malloc分配了8192字节的内存; malloc 成功返回,但由于某种原因,我无法访问超出该内存块 4205 字节的内存。

我也尝试分配更大的内存块(即 8192 * 2),但仍然没有运气,只能访问前 4205 个字节的内存:(

这里是部分代码:

int num_ino = 256;
struct inode * ino_table = malloc(8192);
assert(ino_table);
for(int i = 0; i < num_ino; i ++){
  printf("pre core dump %d\n", i);
  memcpy(ino_table + i * sizeof(struct inode), &inotable[i], sizeof(struct inode));
}

这是 gdb 中发生的事情:

Breakpoint 1, unixfilesystem_init (dfd=3) at unixfilesystem.c:54
54        assert(ino_table);

(gdb) p *(ino_table)
$1 = {i_mode = 0, i_nlink = 0 '\000', i_uid = 0 '\000', i_gid = 0 '\000', i_size0 = 0     '\000', i_size1 = 0, i_addr = {0, 0, 0, 0, 0, 0, 0, 0}, 
  i_atime = {0, 0}, i_mtime = {0, 0}}

(gdb) p *(ino_table + 4205)
$2 = {i_mode = 0, i_nlink = 0 '\000', i_uid = 0 '\000', i_gid = 0 '\000', i_size0 = 0     '\000', i_size1 = 0, i_addr = {0, 0, 0, 0, 0, 0, 0, 0}, 
  i_atime = {0, 0}, i_mtime = {0, 0}}

(gdb) p *(ino_table + 8000)
Cannot access memory at address 0x643a30

(gdb) p *(ino_table + 4206)
Cannot access memory at address 0x625ff0

【问题讨论】:

  • struct inode 的大小是多少?
  • struct inode 的大小是否比 char 大?
  • 你想要struct inode *ino_table = malloc(sizeof(struct inode) * 256);
  • @KepaniHaole 实际上,no he doesn't want to cast malloc in C.
  • 如果你想更好地理解指针运算,试试这个:(gdb) x ino_table + i。您会看到,当您将 i 增加 1 时,地址会增加不止 1。

标签: c memory-management malloc


【解决方案1】:

ino_table进行指针运算时,单位是sizeof(struct inode),不是字节。

因此,

ino_table + i * sizeof(struct inode)

应该变成

ino_table + i

最后,我会像这样更改malloc()

struct inode * ino_table = malloc(num_ino * sizeof(struct inode));

【讨论】:

  • +1 我今天了解到 gdb 进行结构化指针数学运算。谢谢你和其他人。我真的需要摆脱 Windows 代码,回到生活的世界(尽管他们的调试器仍然是猫睡衣)。
  • @WhozCraig 这个答案与 gdb 无关,而是与普通的 C 指针算术;)
  • @griffin 我知道。 在 gdb 打印命令中完成的普通 C 指针算法让我微笑。
  • 顺便说一句:我个人是这样定义指针的(个人喜好):类型 *variable,星号和变量之间没有空格,因为你也这样做了多个指针:类型 *var1, *var2, *var3 - 如果您改为键入 * var1, var2, var3,则只有第一个是指针。但是,这只是口味问题;)
【解决方案2】:

这似乎是合理的,

gdb 中的这个命令:

   p *(ino_table + 8000)

说“打印 ino_table 偏移 8000 字节的内容”。它说,“打印 ino_table 的内容偏移 8000 个元素”。

这意味着 gdb 评估 *(ino_table + 8000) 就像 C 一样,它为您提供第 8000 个元素。 这和说p ino_table[8000]是一样的。

以字节为单位,即sizeof(struct inode) * 8000。除非 sizeof(struct inode) 为 1,否则这将超出范围。

当为一组元素分配空间时,通常会说需要多少个元素,但是您告诉 malloc() 获取 8192 字节。 如果你想要 struct inode 在你的 ino_table 中的 8192 个元素,你会这样做:

 struct inode * ino_table = malloc(8192 * sizeof *ino_table);

请注意,gdb 不会对您的数组/缓冲区进行绑定检查。如果您成功地做到了p *(ino_table + 4205),那么这很可能超出ino_table 的范围,并且可能指向其他地方的可用内存。当 gdb 最终出现 Cannot access memory at address 0x643a30 错误时,这意味着您现在正在尝试访问不存在的内存 - 即它根本没有映射到您进程的地址空间。

【讨论】:

  • +1 假设您比我更了解 gdb(这根本不是什么大问题),这是完全有道理的。
【解决方案3】:

你的问题很可能是指针算术。

ino_table + i * sizeof(struct inode)

很可能不是你想要的,但是

ino_table + i

由于指针算法ino_table + i已经在做

((char *)ino_table) + i * sizeof(struct inode)

这同样适用于 gdb,其中ino_table + 8000 指的是&amp;ino_table[8000]——ino_table 的第 8000 个元素,除非你的结构 inode 被定义为 1 字节大小,否则它将在分配的内存之外。


更笼统地说:

如果你有一个指向类型 T 的指针 P,那么 P++ 会将存储在 P 中的地址增加 sizeof(T),而不是增加一个字节。


还有,而不是

ino_table + i

你可以的

&amp;ino_table[i]

这是许多人的首选(我个人并不在意),因为增加i 会使访问地址增加一个元素,而不是一个元素会更明显字节


对于 malloc 你可以做

malloc(number_of_elements * sizeof(type)) - 不用担心性能差异或代码大小,因为当 number_of_elements 为常数时,表达式无论如何都应该优化为一个常数。

或者,如果您想格外小心和/或不知道您是否会访问未写入的部分,您可以使用:

calloc(number_of_elements, sizeof(type))

malloc 的区别在于calloc 确保您获得空内存(在大多数实现中,通过为您提供指向系统的特殊写时复制清零内存页面的指针),以及返回对齐到sizeof(type)的内存(malloc通常也会返回对齐的内存,但在malloc的情况下,它可能会对齐到最大的可对齐值)


由于在不同的答案中提到了边界检查:您可以查看 gcc mudflap 扩展 - 它并不完全进行边界检查,而是许多其他与内存相关的事情,恕我直言除了 valgrind 之外最容易设置的东西之一(不需要任何设置,但不幸的是并不总是有效)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-01-03
    • 1970-01-01
    • 2012-12-26
    • 2014-02-20
    • 2013-02-23
    • 2020-12-14
    • 2021-03-13
    相关资源
    最近更新 更多