malloc()函数是否会将分配的数组初始化为零?

21

这是我正在使用的代码:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *arr;
    int sz = 100000;
    arr = (int *)malloc(sz * sizeof(int));

    int i;
    for (i = 0; i < sz; ++i) {
        if (arr[i] != 0) {
            printf("OK\n");
            break;
        }
    }

    free(arr);
    return 0;
}
程序没有打印出“OK”。malloc函数不会将分配的内存初始化为零。为什么会发生这种情况?

10
内存的内容是不确定的。它可能看起来是随机的,也可能全部为零。你事先无法知道。 - Some programmer dude
2
程序没有打印OK。malloc不应该将分配的内存初始化为零。它也不能保证它不是全部为零。无论哪种方式,通过读取不确定的值,您的程序具有未定义的行为。您不能期望任何结果。 - StoryTeller - Unslander Monica
此外,调试构建实际上可能会导致分配的内存或本地变量被初始化。从而使内存和指针问题更容易检测出来。 - Some programmer dude
3
C标准并未定义类型是否具有陷阱值,如果读取了陷阱值,则行为明确未定义。因此,我认为这在所有方面都属于未定义行为(UB)。 - StoryTeller - Unslander Monica
1
@StoryTeller 你可能说得有道理。 - Some programmer dude
显示剩余13条评论
9个回答

20

malloc不会将分配的内存初始化为零。为什么会这样?

这是40多年前设计的方式。

但同时,calloc()函数被创建出来以将分配的内存初始化为零,它是为数组分配内存的推荐方式。

这行代码:

arr = (int *)malloc(sz * sizeof(int));

应该这样阅读:

arr = calloc(sz, sizeof(int));

如果您正在学习C语言的旧书,它会教您始终将malloc()calloc()(一个void *)返回的值强制转换为您分配该值的变量类型(在您的情况下是int *)。但这种方法已过时了,如果malloc()calloc()返回的值直接赋给变量,则现代版本的C语言不再需要进行强制转换。


3
在除赋值运算符之外的上下文中,强制类型转换是必要的。 - Ryan B.
3
@RyanB. - GCC在这里没有遵循C标准。void*可以隐式转换为所有对象类型,不需要强制转换。 - StoryTeller - Unslander Monica

15

malloc的手册页说明:

malloc()函数分配大小为size字节的内存,并返回指向已分配内存的指针。内存未初始化。如果size为0,则malloc()返回NULL或一个唯一的指针值,该值稍后可以成功传递给free()。

因此,malloc()返回未初始化的内存,其内容是不确定的。

 if (arr[i] != 0)

在您的程序中,您尝试访问一个内存块的内容,这会导致未定义行为


最后一段不正确。仅仅使用一个不确定的值并不一定会导致未定义的行为,但可能会非常不稳定。当你把它传递给一个库函数时,那就成为了未定义的行为。 - Antti Haapala -- Слава Україні

11

malloc不会将分配的内存初始化为零。

malloc分配的内存未被初始化。这些位置上的值是不确定的。如果该位置上的值是类型的陷阱表示,则访问该内存可能导致未定义的行为。

n1570-§6.2.6.1 (p5):

某些对象表示不需要表示对象类型的值。如果对象的存储值具有这样的表示,并且由没有字符类型的lvalue表达式读取,则行为未定义。[...]

脚注说:

因此,自动变量可以被初始化为陷阱表示而不会导致未定义的行为,但不能在变量中使用该值,直到正确的值存储在其中。

如果行为未定义,就不会有任何好结果。您可能会得到预期的结果,也可能不会。


10

根据C语言标准7.22.3.4:

摘要

#include <stdlib.h>
void *malloc(size_t size);

描述

malloc函数分配一个大小由size指定且值不确定的对象的空间。

该值是不确定的。因此,每个编译器都可以自由地按照其意愿进行操作。例如,在Microsoft Visual C++中,在Debug模式下,由malloc()分配的内存区域全部设置为0xCDCDCDCD,而在Release模式下则是随机的。在现代版本的GCC中,如果您没有启用代码优化,则将其设置为0x000000,否则为随机值。我不知道其他编译器的情况,但您已经有了想法。


2
你能详细说明一下 "在现代版本的GCC中,如果您没有启用代码优化,则设置为0x000000,否则为随机值。" 吗?可能包括一个参考文献? - S.S. Anne

4
void *malloc(size_t size) 函数只是保留了指定大小的空间,不保证该空间中存在什么内容。引用man手册上的一句话:“malloc()函数分配size字节的内存,并返回指向分配内存的指针。该内存未初始化。如果size为0,则malloc()返回NULL或一个唯一的指针值,后者可以稍后成功地传递给free()。” 除了使用calloc()之外,您还可以使用memset()函数将一块内存清零。

3
第一次调用malloc(3)时,它会向操作系统请求获取堆空间的内存。
出于安全考虑,Unix/Linux内核(以及许多其他操作系统)通常将要提供给进程的页面内容清零,因此没有进程可以访问该内存的先前内容并进行恶意操作(例如搜索旧密码或类似事情)。
如果您对内存进行多次分配和释放,在malloc模块重用先前的内存时,您会看到来自malloc(3)的垃圾。

2

在Linux内核中,页面内容一开始都被赋值为零。

下面的程序解释了malloc和calloc在内存初始化方面的区别:

#include<stdio.h>
#include<stdlib.h>

#define SIZE 5

int main(void) {
    int *mal = (int*)malloc(SIZE*sizeof(int));
    int *cal = (int*)calloc(SIZE, sizeof(int));

    mal[4] = cal[4] = 100;

    free(mal); free(cal);

    mal = (int*)malloc(SIZE*sizeof(int));
    cal = (int*)calloc(SIZE, sizeof(int));

    for(int i=0; i<SIZE; i++) {
        printf("mall[%d] = %d\n", i, mal[i]);
    }
    for(int i=0; i<SIZE; i++) {
        printf("call[%d] = %d\n", i, cal[i]);
    }
}

0

我以前使用malloc从堆(动态内存)中分配所有内容,现在应该使用calloc,而memset非常适合用于将内存段填充为任何选择的字符。

使用GCC编译并工作得很好:

#include <stdio.h>
#include <stdlib.h>
#include <mem.h>

int main() 
{

  int *arr;
    int sz = 100000;
  arr = (int *)malloc(sz * sizeof(int));

    memset(arr, 0, sz*sizeof(int) );


   int i;
    for (i = 0; i < sz; ++i) {
        if (arr[i] != 0) {
          printf("OK\n");
         break;
       }
 }

 free(arr);
 return 0;
}

参考:http://www.cplusplus.com/reference/cstring/memset/


如果您将指向arr的内存块的字节设置为0,为什么不使用calloc - OpSocket
我个人更喜欢使用calloc,因为它允许我更灵活地改变分配块的大小,但这里的重点是要准确回答作者的问题,并对原始代码进行少量更改。 - Luke Demandelemoi

-1

嗯,在malloc中,该值未初始化。 而在VS Code中,它确实打印出“OK”。

因此,在VS Code中,输出为:“OK”,后跟一个垃圾值。

在基于Web的编译器中(这是链接:https://www.programiz.com/c-programming/online-compiler/),

输出为 “LOL”,后跟'0'

因此,一些编译器确实会初始化该值...但实际上,在malloc中该值未初始化。因此,当像上面在VS Code中的示例中打印时,它将返回一个垃圾值。

int main()
{
    int *arr;
    int sz = 100000;
    arr = (int *)malloc(sz * sizeof(int));

    int i;
    for (i = 0; i < sz; i++)
    {
        if (arr[i] != 0)
        {
            printf("OK\n");
            break;
        }
        else
        {
            printf("LOL \n");
            break;
        }
    }

    printf("%d", arr[0]);

    free(arr);

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