我对 C 上的 malloc() 和 calloc() 感到非常困惑

发布于 2024-10-03 18:45:12 字数 860 浏览 6 评论 0原文

我一直用 Java 编程,这可能就是我对此如此困惑的原因:

在 Java 中我声明一个指针:

int[] array

并初始化它或为其分配一些内存:

int[] array = {0,1,0}
int[] array = new int[3]

现在,在 C 中,这一切都如此令人困惑。起初我认为这就像声明它一样简单:

int array[]

初始化它或为其分配一些内存:

int array[] = {0,1,0}
int array[] = malloc(3*sizeof(int))
int array[] = calloc(3,sizeof(int))

除非我错了,否则以上所有内容都是等效的 Java-C,对吧?

然后,今天我遇到了一段代码,其中我发现了以下内容:

pthread_t tid[MAX_OPS];

以及下面的一些行,没有任何类型的初始化...

pthread_create(&tid[0],NULL,mou_usuari,(void *) 0);

令人惊讶的是(至少对我来说),该代码有效!至少在 Java 中,这会返回一个漂亮的“NullPointerException”!

那么,按顺序:

  1. 我对所有 Java-C“翻译”的理解是否正确?

  2. 为什么该代码可以工作?

提前致谢

I've always programmed in Java, which is probably why I'm so confused about this:

In Java I declare a pointer:

int[] array

and initialize it or assign it some memory:

int[] array = {0,1,0}
int[] array = new int[3]

Now, in C, it's all so confusing. At first I thought it was as easy as declaring it:

int array[]

and initializing it or assigning it some memory:

int array[] = {0,1,0}
int array[] = malloc(3*sizeof(int))
int array[] = calloc(3,sizeof(int))

Unless I'm wrong, all of the above is equivalent Java-C, right?

Then, today I met a code in which I found the following:

pthread_t tid[MAX_OPS];

and some lines below, without any kind of initialization...

pthread_create(&tid[0],NULL,mou_usuari,(void *) 0);

Surprisingly (at least to me), the code works! At least in Java, that would return a nice "NullPointerException"!

So, in order:

  1. Am I correct with all of the Java-C "translations"?

  2. Why does that code work?

  3. Is there any difference between using malloc(n*sizeof(int)) and calloc(n,sizeof(int))?

Thanks in advance

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

爱情眠于流年 2024-10-10 18:45:12

您不能将内存分配给数组。数组在其整个生命周期内具有固定的大小。数组永远不能为空。数组不是指针。

malloc 返回为程序保留的内存块的地址。您不能将其(作为内存块)“分配”给数组,但您可以将该内存块的地址存储在指针中:幸运的是,数组订阅是通过指针定义的 - 因此您可以“像数组一样使用指针” ,例如,

int *ptr = malloc(5 * sizeof *ptr);
ptr[2] = 5; // access the third element "of ptr"
free(ptr); // always free at the end

当您声明一个没有大小的数组(即array[])时,它仅意味着数组的大小由初始值设定项列表确定。也就是说,

int array[] = {1, 2, 3, 4, 5}; // is equal to
int array[5] = {1, 2, 3, 4, 5};

尝试声明一个没有大小且没有初始值设定项的数组是错误的。


代码 pthread_t tid[MAX_OPS]; 声明一个名为 tid 的数组,其类型为 pthread_t,大小为 MAX_OPS

如果数组具有自动存储(即声明位于函数内部并且不是静态的,不是全局的),则每个数组元素都具有不确定的值(并且会导致尝试读取此类值的未定义行为)。幸运的是,函数调用所做的只是将数组第一个元素的地址作为第一个参数,并可能在函数内初始化它(元素)。


callocmalloc 的区别在于 calloc 返回的内存块被初始化为零。那是;

int *ptr = calloc(5, sizeof *ptr);
// is somewhat equal to
int *ptr = malloc(5 * sizeof *ptr);
memset(ptr, 0, 5 * sizeof *ptr);

两者的区别

int *ptr = malloc(5 * sizeof *ptr);
// and
int array[5];

在于array具有自动存储功能(存储在堆栈上),并且在超出范围后“释放”。然而,ptr(存储在堆上)是动态分配的,并且必须由程序员释放

You can't assign memory to an array. An array has a fixed size, for the whole of its lifespan. An array can never be null. An array is not a pointer.

malloc returns the address to a memory block that is reserved for the program. You can't "assign" that (being the memory block) to an array, but you can store the address of this memory block in a pointer: luckily, array subscription is defined through pointers - so you can "use pointers like arrays", e.g.

int *ptr = malloc(5 * sizeof *ptr);
ptr[2] = 5; // access the third element "of ptr"
free(ptr); // always free at the end

When you declare an array without a size (i.e. array[]), it simply means the size of the array is determined from the initializer list. That is

int array[] = {1, 2, 3, 4, 5}; // is equal to
int array[5] = {1, 2, 3, 4, 5};

Trying to declare an array without a size and without an initializer is an error.


The code pthread_t tid[MAX_OPS]; declares an array named tid of type pthread_t and of size MAX_OPS.

If the array has automatic storage (i.e. declaration is inside a function and not static, not global), then each of the arrays elements has indeterminate value (and it would cause undefined behavior trying to read such value). Luckily, all that the function call does is that it takes the address of the first element of the array as the first parameter, and probably initializes it (the element) inside the function.


The difference of calloc and malloc is that the memory block that calloc returns is initialized to zero. That is;

int *ptr = calloc(5, sizeof *ptr);
// is somewhat equal to
int *ptr = malloc(5 * sizeof *ptr);
memset(ptr, 0, 5 * sizeof *ptr);

The difference between

int *ptr = malloc(5 * sizeof *ptr);
// and
int array[5];

is that array has automatic storage, (is stored on stack), and is "released" after it goes out of scope. ptr, however, (is stored on heap), is dynamically allocated and must be freed by the programmer.

屌丝范 2024-10-10 18:45:12

您缺少三个非常基本且严格(并且具有误导性!)的 C 主题:

  • 数组和指针之间的区别、
  • 静态分配和动态分配之间的区别、
  • 在堆栈或堆上声明变量的区别

如果您编写 int array[ ] = malloc(3*sizeof(int)); 你会得到一个编译错误(类似于'identifier':数组初始化需要大括号)。

这意味着声明数组仅允许静态初始化:

  • int array[] = {1,2,3};,在堆栈上保留 3 个连续的整数;
  • int array[3] = {1,2,3}; 与上一个相同;
  • int array[3]; 仍然在堆栈上保留 3 个连续的整数,但不初始化它们(内容将是随机垃圾)
  • int array[4] = {1,2, 3}; 当初始化器列表未初始化所有元素时,其余元素将设置为 0 (C99 §6.7.8/19):在这种情况下,您将得到 1,2,3,0

注意在所有这些情况下,您都没有分配新内存,而只是使用已经提交到堆栈的内存。仅当堆栈已满时,您才会遇到问题(猜猜,这将是堆栈溢出)。因此,声明 int array[]; 是错误且毫无意义的。

要使用malloc,您必须声明一个指针:int* array

当您编写 int* array = malloc(3*sizeof(int)); 时,您实际上执行了三个操作:

  1. int* array 告诉编译器在stack(包含内存地址的整数变量)
  2. malloc(3*sizeof(int)) 在堆上分配 3 个连续的整数并返回第一个整数的地址
  3. =将返回值(您分配的第一个整数的地址)的副本分配给指针变量

因此,回到您的问题:

pthread_t tid[MAX_OPS];

是堆栈上的数组,因此不需要分配它(如果 假设 MAX_OPS 是 16,那么堆栈上将保留适合 16 个 pthread_t 所需的连续字节数。该内存的内容将是垃圾(堆栈变量未初始化为零),但 pthread_create 会在其第一个参数(指向 pthread_t 变量的指针)中返回一个值,并且忽略任何以前的内容,因此代码很好。

You are missing three very basic and tighten (and misleading!) C topics:

  • the difference between array and pointers
  • the difference between static and dynamic allocation
  • the difference from declaring variables on the stack or on the heap

If you write int array[] = malloc(3*sizeof(int)); you would get a compilation error (something like 'identifier' : array initialization needs curly braces).

This means that declaring an array allows only static initialization:

  • int array[] = {1,2,3}; that reserves 3 contiguous integers on the stack;
  • int array[3] = {1,2,3}; which is the same as the previous one;
  • int array[3]; that still reserves 3 contiguous integers on the stack, but does not initialize them (the content will be random garbage)
  • int array[4] = {1,2,3}; when the initializer list doesn't initialize all the elements, the rest are set to 0 (C99 §6.7.8/19): in this case you'll get 1,2,3,0

Note that in all these cases you are not allocating new memory, you are just using the memory already committed to the stack. You would run in a problem only if the stack is full (guess it, it would be a stack overflow). For this reason declaring int array[]; would be wrong and meaningless.

To use malloc you have to declare a pointer: int* array.

When you write int* array = malloc(3*sizeof(int)); you are actually doing three operations:

  1. int* array tells the compiler to reserve a pointer on the stack (an integer variable that contains a memory address)
  2. malloc(3*sizeof(int)) allocates on the heap 3 contiguous integers and returns the address of the first one
  3. = assigns copies that return value (the address of the first integer you have allocated) to your pointer variable

So, to come back to your question:

pthread_t tid[MAX_OPS];

is an array on the stack, so it doesn't need to be allocated (if MAX_OPS is, say, 16 then on the stack will be reserved the number of contiguous bytes needed to fit 16 pthread_t). The content of this memory will be garbage (stack variables are not initialized to zero), but pthread_create returns a value in its first parameter (a pointer to a pthread_t variable) and disregards any previous content, so the code is just fine.

源来凯始玺欢你 2024-10-10 18:45:12

C 提供静态内存分配和动态内存分配 - 您可以在堆栈外或可执行内存(由编译器管理)中分配数组。这与 Java 中的方式相同,您可以在堆栈上分配 int 或在堆上分配 Integer。 C 中的数组就像任何其他堆栈变量一样 - 它们超出范围等。在 C99 中,它们也可以具有可变大小,尽管它们无法调整大小。

{} 和 malloc/calloc 之间的主要区别在于,{} 数组是静态分配的(不需要释放)并自动为您初始化,而 malloc/calloc 数组必须显式释放,并且您必须显式初始化它们。但当然,malloc/calloc 数组不会超出范围,您可以(有时)realloc() 它们。

C offers static memory allocation as well as dynamic- you can allocate arrays off the stack or in executable memory (managed by the compiler). This is just the same as how in Java, you can allocate an int on the stack or an Integer on the heap. Arrays in C are just like any other stack variable- they go out of scope, etc. In C99 they can also have a variable size, although they cannot be resized.

The main difference between {} and malloc/calloc is that {} arrays are statically allocated (don't need freeing) and automatically initialized for you, whereas malloc/calloc arrays must be freed explicitly and you have to initialize them explicitly. But of course, malloc/calloc arrays don't go out of scope and you can (sometimes) realloc() them.

乖乖兔^ω^ 2024-10-10 18:45:12

2 - 这个数组声明是静态的:

pthread_t tid[MAX_OPS];

我们不需要分配内存块,而是动态分配:

pthread_t *tid = (pthread_t *)malloc( MAX_OPS * sizeof(pthread_t) );

不要忘记释放内存:

free(tid);

3 - malloc 和 calloc 之间的区别是 calloc 为数组分配一块内存并将其所有位初始化为 0。

2 - This array declaration is static :

pthread_t tid[MAX_OPS];

We don't need to allocate memory block, instead of dynamic allocation :

pthread_t *tid = (pthread_t *)malloc( MAX_OPS * sizeof(pthread_t) );

Don't forget to free the memory :

free(tid);

3 - The difference between malloc and calloc is calloc allocate a block of memory for an array and initializes all its bits at 0.

巾帼英雄 2024-10-10 18:45:12

我发现当您用 C(而不是 C++)编程时,显式指示 *array 很有帮助,记住有一个可以移动的指针。因此,我想首先将您的示例重新表述为:

int array[] = {0,1,2};
int *array = malloc(3*sizeof(int));
int *array = calloc(3,sizeof(int));

第一个清楚表明有一种称为数组的东西,它指向包含 0、1 和 2 的内存块。数组不能移动到其他地方。

您的下一个代码:
pthread_t tid[MAX_OPS];

实际上会导致分配 sizeof(pthread_t) * MAX_OPS 的数组。但它没有分配一个名为 *tid 的指针。数组的基址有一个地址,但不能将其移动到其他地方。

ptherad_t 类型实际上是指针的掩护。所以上面的tid实际上是一个指针数组。而且它们都是静态分配的但没有初始化。

pthread_create 获取数组开头的位置 (&tid[0])(这是一个指针),并分配一块内存来保存 pthread 数据结构。将指针设置为指向新的数据结构,并分配该数据结构。

你的最后一个问题 --- malloc(n*sizeof(int)) 和 calloc(n,sizeof(int)) 之间的区别是后者将每个字节初始化为0,而第一个则没有。

I find it helpful when you are programming in C (as opposed to C++) to indicate *array explicitly, to remember that there is a pointer that can be moved around. So I would like to start by rephrasing your example as:

int array[] = {0,1,2};
int *array = malloc(3*sizeof(int));
int *array = calloc(3,sizeof(int));

The first makes it clear that there is something called array which is pointing to a block of memory that contains a 0, 1 and 2. array can't be moved elesewhere.

Your next code:
pthread_t tid[MAX_OPS];

Does in fact cause an array with sizeof(pthread_t) * MAX_OPS to be allocated. But it does not allocate a pointer called *tid. There is an address of the base of the array, but you can't move it elsewhere.

The ptherad_t type is actually a cover for a pointer. So tid above is actually an array of pointers. And they are all statically allocated but they are not initialized.

The pthread_create takes the location at the beginning of the array (&tid[0]), which is a pointer, and allocates a block of memory to hold the pthread data structure. The pointer is set to point to the new data structure and the data structure is allocated.

Your last question --- the difference between malloc(n*sizeof(int)) and calloc(n,sizeof(int)) is that the later initializes each byte to 0, while the first does not.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文