sizeof运算符是否需要用于malloc函数?

6
在这个程序中(使用C而不是C++),为什么malloc函数返回的大小总是正确的,而不受sizeof运算符的影响呢?
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    char *c = malloc(3);
    short *s = malloc(3); /* or malloc(3 * sizeof(short))? */
    int *i = malloc(3);   /* or malloc(3 * sizeof(int))? */
    long *l = malloc(3);  /* or malloc(3 * sizeof(long))? */

    printf("%p\n", c++);
    printf("%p\n", c++);
    printf("%p\n", c++);

    printf("---\n");

    printf("%p\n", s++);
    printf("%p\n", s++);
    printf("%p\n", s++);

    printf("---\n");

    printf("%p\n", i++);
    printf("%p\n", i++);
    printf("%p\n", i++);

    printf("---\n");

    printf("%p\n", l++);
    printf("%p\n", l++);
    printf("%p\n", l++);

    return 0;
}

输出结果为:
0x1e82010 (1 byte)
0x1e82011
0x1e82012
---
0x1e82030 (2 bytes)
0x1e82032
0x1e82034
---
0x1e82050 (4 bytes)
0x1e82054
0x1e82058
---
0x1e82070 (8 bytes)
0x1e82078
0x1e82080

我遗漏了什么吗?

4.0.4-303.fc22.x86_64 clang版本3.5.0(tags/RELEASE_350/final) 目标:x86_64-redhat-linux-gnu 线程模型:posix


你正在进行地址算术运算;你没有访问内存,这很好,因为只分配了3个字节,所以除了char *部分之外,你都在访问越界的内存。 - Jonathan Leffler
1
你现在的做法是未定义行为。它“看起来”能够工作并不代表它是合法的(或有效的)。你可以将那些指针设为NULL,并获得相同的结果。 - Cornstalks
1
你是如何确定malloc分配了多少内存的? - Samuel Edwin Ward
@SamuelEdwinWard:你不需要这样做。如果它返回一个非空结果,那么它至少分配了你要求的内存大小;不要假设它分配了更多。如果它返回一个空指针,那么它没有分配任何东西。 - Keith Thompson
1
@JohnBollinger:是的,指针算术仅在数组对象的范围内定义。即使指针从未被解引用,超出包含数组的末尾会导致未定义的行为。(计算刚好超过数组最后一个元素的指针是合法的,但解引用它会导致未定义的行为。) - Keith Thompson
显示剩余2条评论
5个回答

11
long *l = malloc(3);

这将分配(或者说是尝试分配)3个 字节

通常情况下,malloc() 为了对齐和记录目的,实际上会分配比您请求的更多的内存。所以在调用 malloc(3) 后,您可能能够成功地将 3 个 long 值存储在分配的内存中。但不能保证。

是的,您需要使用 sizeof

而且最好的写法是:

long *l = malloc(3 * sizeof *l);

通过使用指针指向的内容的大小(sizeof *l),您无需两次指定类型long,如果类型以后更改,代码也不会中断。

更好的方法是:

long *l = malloc(3 * sizeof *l);
if (l == NULL) {
    /* malloc failed, recover or bail out */
}

如果你愿意,你可以这样写:

long *l = malloc(3 * sizeof(*l));

但是多余的括号并不必要,sizeof 的大小是一元运算符,而不是函数。

printf("%p\n", l++);
printf("%p\n", l++);
printf("%p\n", l++);

指针加一操作会使其指向的地址增加一个该类型所占用的字节数。在你的系统上,long 类型似乎占用 8 个字节,因此这将使 l 前进至少 24 个字节,远远超出你从 malloc 中请求的 3 个字节。结果是未定义的行为。

通过递增 l,你已经失去了 malloc 返回的原始值;在调用 free() 时需要它。

最后,%p 格式说明符要求使用 void* 类型的参数。传递不同类型的指针可能会“工作”,但你真的应该将其强制转换为 void*

printf("%p\n", (void*)l++);

太好了。感谢您提供的额外提示。 - lmlf
赞成使用 long *l = malloc(3 * sizeof *l); 而非 long *l = malloc(3 * sizeof (long)); - chux - Reinstate Monica

5

我有遗漏吗?

是的,你完全没有理解重点。

你正在测试指针算术,这是根据所指类型的大小定义的。这与malloc()分配的内存量毫不相关,甚至与所讨论的指针是否指向有效地址也无关。


4

将指针递增会使存储的地址增加基本类型的大小,而不取决于指针指向什么。


2
当你增加指针时,所增加的字节数仅基于其类型 - 例如,如果你将一个int*增加1,地址将增加4个字节。这与mallocsizeof无关。
如果你开始在这些指针中存储值,你会发现会遇到奇怪的行为,因为你没有分配足够的空间来存储3个short、3个int或3个long

2

你陷入了一个谬误。你并没有分析malloc返回的内容,你只是看到increment知道如何根据变量类型的大小正确地增加指针。malloc调用并不能告诉你实际分配了多少RAM。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接