Calloc arguments order

R

Raj Pashwar

Hello

I would like to understand something about the Calloc function.

"

void *calloc(size_t nmemb, size_t size);

The calloc() function allocates memory for an array of nmemb elements
of size bytes each and returns a pointer to the allocated memory. The
memory is set to zero. If nmemb or size is 0, then calloc() returns
either NULL, or a unique pointer value that can later be successfully
passed to free().

"

What I don't understand is: how is this any different from allocating
memory for an array of size elements of nmemb bytes each? Does the order
of arguments make much difference really? What difference does it make
having them in one order instead of the other?

And why is it inconsistent with Malloc (only one argument)? This seems
like a paradox, because in practice Calloc will be implemented by a Malloc
followed by a Bzero.

Cheers,
Raj
 
J

Jens Gustedt

Hello,

Am 19.08.2012 22:55, schrieb Raj Pashwar:
I would like to understand something about the Calloc function.

"

void *calloc(size_t nmemb, size_t size);

The calloc() function allocates memory for an array of nmemb elements
of size bytes each and returns a pointer to the allocated memory. The
memory is set to zero. If nmemb or size is 0, then calloc() returns
either NULL, or a unique pointer value that can later be successfully
passed to free().

"

What I don't understand is: how is this any different from allocating
memory for an array of size elements of nmemb bytes each? Does the order
of arguments make much difference really?
no

What difference does it make
having them in one order instead of the other?
none

And why is it inconsistent with Malloc (only one argument)?

if size_t is narrow, say 16 bit, calloc allows to allocate up to
UINT16_MAX items of your base type, malloc only UINT16_MAX bytes
This seems
like a paradox, because in practice Calloc will be implemented by a Malloc
followed by a Bzero.

Also calloc is not necessarily implemented that way, in particular the
malloc interface might not allow to allocate as much as calloc needs.

Jens
 
M

Malcolm McLean

בת×ריך ×™×•× ×¨×שון, 19 ב×וגוסט 2012 21:55:10 UTC+1, מ×ת Raj Pashwar:
And why is it inconsistent with Malloc (only one argument)? This seems
like a paradox, because in practice Calloc will be implemented by a Malloc
followed by a Bzero.
Calloc's basically a useless function. There's some slight justification for
it in that if you call it with two arguments that make more than the maximum
value of a szie_t, it can detect the situation and return null. Because size_t
is unsigned calling malloc( N * sizeof *ptr) can only produce a request for
the wrong amount of memory in this situation.
But really it's just an unnecessary wrapper to malloc(). You can't even rely on
all bits zero correctly intialising null pointers or zero-valued floating
point types, though it will do so on all current common processors.
 
K

Keith Thompson

Raj Pashwar said:
I would like to understand something about the Calloc function.

Suggestion: Don't capitalize C identifiers. "Calloc" and "calloc" are
distinct names.
"

void *calloc(size_t nmemb, size_t size);

The calloc() function allocates memory for an array of nmemb elements
of size bytes each and returns a pointer to the allocated memory. The
memory is set to zero. If nmemb or size is 0, then calloc() returns
either NULL, or a unique pointer value that can later be successfully
passed to free().

"

What I don't understand is: how is this any different from allocating
memory for an array of size elements of nmemb bytes each? Does the order
of arguments make much difference really? What difference does it make
having them in one order instead of the other?

I don't believe it makes any difference at all.
And why is it inconsistent with Malloc (only one argument)? This seems
like a paradox, because in practice Calloc will be implemented by a Malloc
followed by a Bzero.

There's no paradox; it's just redundant. calloc() is basically a
convenience function that lets you allocate an array of objects without
having to compute the size in bytes of that array; it also zeros the
allocated memory (which usually isn't all that useful, since
all-bits-zero isn't necessarily meaningful for pointers or
floating-point objects).

(The following is a relatively minor point; feel free to ignore it.)

Also, it *should* detect overflow in the multiplication. This:

some_type *ptr = malloc(MANY * sizeof *ptr);

can misbehave if the mathematical result of the multiplication exceeds
SIZE_MAX; this:

some_type *ptr = calloc(MANY, sizeof *ptr);

should detect the overflow (and presumably return a null pointer).

Example:

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

int main(void) {
size_t n = 65537;
printf("SIZE_MAX = %zu\n", SIZE_MAX);
printf("n = %zu\n", n);
printf("n * n = %llu (mathematically)\n",
(unsigned long long)n * n);
printf("n * n = %zu (size_t)\n", n * n);
void *mptr = malloc(n * n);
void *cptr = calloc(n, n);
printf("malloc() %s\n", mptr == NULL ? "failed" : "succeeded");
printf("calloc() %s\n", cptr == NULL ? "failed" : "succeeded");
return 0;
}

Output (on my system):

SIZE_MAX = 4294967295
n = 65537
n * n = 4295098369 (mathematically)
n * n = 131073 (size_t)
malloc() succeeded
calloc() failed

The malloc() call succeeds because the argument it was given was
mathematically incorrect and too small. The calloc() call failed
because it was unable to allocate that much memory.
 
K

Keith Thompson

Jens Gustedt said:
if size_t is narrow, say 16 bit, calloc allows to allocate up to
UINT16_MAX items of your base type, malloc only UINT16_MAX bytes

calloc allows you to *request* an allocation of up to 65535 items of
your base type. In practice, it's unlikely that any implementation will
support objects bigger than SIZE_MAX bytes, and such an allocation will
almost certainly fail.

An implementation that can allocate objects bigger than 65535 bytes
should simply make size_t bigger than 16 bits.
 
E

Eric Sosman

Hello

I would like to understand something about the Calloc function.

"

void *calloc(size_t nmemb, size_t size);

The calloc() function allocates memory for an array of nmemb elements
of size bytes each and returns a pointer to the allocated memory. The
memory is set to zero. If nmemb or size is 0, then calloc() returns
either NULL, or a unique pointer value that can later be successfully
passed to free().

"

What I don't understand is: how is this any different from allocating
memory for an array of size elements of nmemb bytes each? Does the order
of arguments make much difference really? What difference does it make
having them in one order instead of the other?

None. Well, at any rate "very little." Library functions
like qsort() and fwrite() also take element-count and element-size
parameters, and for them the order *does* matter. So it could be
argued that it would be inconsistent to arrange the calloc()
parameters the other way around. But since [1] the request size
will be the product of the two parameters, and [2] multiplication
is commutative, and [3] the allocation must be properly aligned for
any object whatsoever, even if it wouldn't fit, it really doesn't
make any difference.
And why is it inconsistent with Malloc (only one argument)? This seems
like a paradox, because in practice Calloc will be implemented by a Malloc
followed by a Bzero.

First, a lot of the Standard library functions are older than
the original ANSI Standard itself. One of the important purposes
of that Standard was to codify existing practice, ironing out the
differences that had developed between different C implementations.
The ANSI Standard took malloc() and calloc() and realloc() as they
already existed at the time, formalized their specifications, but
did not try to invent a new library. A few changes were unavoidable
(different implementations disagreed), a few were mostly cosmetic
(changing the value from `char*' to `void*', for example), but there
was no effort to "improve" the API of existing functions. That's
why (I imagine) some I/O functions put the `FILE*' parameter first
and others put it last.

So, why did the primeval calloc() have two parameters instead
of just one? I'm only guessing, but my guess is that calloc() may
have been invented on a system where memory was scarce, and perhaps
the earliest versions *did* in fact treat the parameters differently.
Maybe calloc(128,1) said "Oh, 1-byte objects: I don't need to worry
about alignment" whereas calloc(16,8) would say "Oh, 8-byte objects:
they might be `double', so I'd better use strict alignment." Such
a stratagem (if it existed; remember, I'm just guessing) ceased to
have meaning when ANSI decreed that all dynamic allocations had to
be strictly aligned, but things might have been different in the
Bronze Age.

Also, calloc(n,s) differs from malloc(n*s) when `n' and `s' are
large enough that their product is too big for a `size_t'. Imagine
a 16-bit `size_t', and consider

size_t count = 3000;
size_t esize = 32;
void *p = calloc(count, esize);
void *q = malloc(count * esize);

The calloc() call fails and returns NULL because it cannot allocate
enough memory for 3000 32-byte objects. But the product in the second
call gets truncated, and malloc() receives a request for 30464 bytes
(3000 * 32 = 96000, 96000 % 65536 = 30464) -- which it might succeed
in finding, returning a pointer to the allocated memory. Unfortunately,
the allocation is only large enough for 952 objects, so the programmer
who thinks all is well and tries to store 3000 of them there is in
for an unpleasant surprise ...

Personally, I seldom use calloc() and rely on malloc()/realloc()
instead. When I allocate memory, it's generally because I want to
store something in it; it's not often that what I want to store is
lots and lots of zero bytes.
 
B

Barry Schwarz

On Sun, 19 Aug 2012 20:55:10 +0000 (UTC), Raj Pashwar

snip
And why is it inconsistent with Malloc (only one argument)? This seems
like a paradox, because in practice Calloc will be implemented by a Malloc
followed by a Bzero.

There is no standard C function named Bzero (or bzero).
 
J

Joe Pfeiffer

Malcolm McLean said:
בת×ריך ×™×•× ×¨×שון, 19 ב×וגוסט 2012 21:55:10 UTC+1, מ×ת Raj Pashwar:
Calloc's basically a useless function. There's some slight justification for
it in that if you call it with two arguments that make more than the maximum
value of a szie_t, it can detect the situation and return null. Because size_t
is unsigned calling malloc( N * sizeof *ptr) can only produce a request for
the wrong amount of memory in this situation.
But really it's just an unnecessary wrapper to malloc(). You can't even rely on
all bits zero correctly intialising null pointers or zero-valued floating
point types, though it will do so on all current common processors.

The fact that it initializes the allocated space makes it useful.
 
K

Keith Thompson

Joe Pfeiffer said:
The fact that it initializes the allocated space makes it useful.

The fact that it initializes the allocated space to all-bits-zero makes
it useful only if (a) the space is used only for objects of integer type
(and, recursively, arrays, structs, and unions containing integer
elements), or (b) the code needn't be 100% portable.

Even then, initializing everything to zero isn't necessarily all that
useful if you're careful to initialize all subobjects before reading
them. This does require some care - but on the other hand zeroing
everything can sometimes mask errors.
 
X

Xavier Roche

Le 20/08/2012 13:52, army1987 a écrit :
Well, you could use memset() for that.

Why call two functions when one is good ?

Moreover, calloc() will probably allocate large chunks of memory by
giving directly a mmap()'ed area. Using malloc()/memset() will probably
return a mmap()'ed area AND uselessly clear an already cleared area.
 
E

Eric Sosman

[...]
None. Well, at any rate "very little." Library functions
like qsort() and fwrite() also take element-count and element-size
parameters, and for them the order *does* matter. So it could be
argued that it would be inconsistent to arrange the calloc()
parameters the other way around.[...]

"Upon further review," as they say in American football,
it *is* inconsistent! But it's not the fault of calloc()
alone:

- calloc(), qsort(), and bsearch() put the element count
before the element size, but

- fread() and fwrite() put the element size before the
element count.

This is just Yet Another Illustration of what happens
when "a" library is assembled from independent origins. It's
also Yet Another Illustration of why a quick reference should
be one's constant companion -- Otherwise, one's likely to say
or do something silly ...
 
R

ralph

Hello

I would like to understand something about the Calloc function.

"

void *calloc(size_t nmemb, size_t size);

The calloc() function allocates memory for an array of nmemb elements
of size bytes each and returns a pointer to the allocated memory. The
memory is set to zero. If nmemb or size is 0, then calloc() returns
either NULL, or a unique pointer value that can later be successfully
passed to free().

"

What I don't understand is: how is this any different from allocating
memory for an array of size elements of nmemb bytes each? Does the order
of arguments make much difference really? What difference does it make
having them in one order instead of the other?

None. Well, at any rate "very little." Library functions
like qsort() and fwrite() also take element-count and element-size
parameters, and for them the order *does* matter. So it could be
argued that it would be inconsistent to arrange the calloc()
parameters the other way around. But since [1] the request size
will be the product of the two parameters, and [2] multiplication
is commutative, and [3] the allocation must be properly aligned for
any object whatsoever, even if it wouldn't fit, it really doesn't
make any difference.
And why is it inconsistent with Malloc (only one argument)? This seems
like a paradox, because in practice Calloc will be implemented by a Malloc
followed by a Bzero.

First, a lot of the Standard library functions are older than
the original ANSI Standard itself. One of the important purposes
of that Standard was to codify existing practice, ironing out the
differences that had developed between different C implementations.
The ANSI Standard took malloc() and calloc() and realloc() as they
already existed at the time, formalized their specifications, but
did not try to invent a new library. A few changes were unavoidable
(different implementations disagreed), a few were mostly cosmetic
(changing the value from `char*' to `void*', for example), but there
was no effort to "improve" the API of existing functions. That's
why (I imagine) some I/O functions put the `FILE*' parameter first
and others put it last.

So, why did the primeval calloc() have two parameters instead
of just one? I'm only guessing, but my guess is that calloc() may
have been invented on a system where memory was scarce, and perhaps
the earliest versions *did* in fact treat the parameters differently.
Maybe calloc(128,1) said "Oh, 1-byte objects: I don't need to worry
about alignment" whereas calloc(16,8) would say "Oh, 8-byte objects:
they might be `double', so I'd better use strict alignment." Such
a stratagem (if it existed; remember, I'm just guessing) ceased to
have meaning when ANSI decreed that all dynamic allocations had to
be strictly aligned, but things might have been different in the
Bronze Age.

Also, calloc(n,s) differs from malloc(n*s) when `n' and `s' are
large enough that their product is too big for a `size_t'. Imagine
a 16-bit `size_t', and consider

size_t count = 3000;
size_t esize = 32;
void *p = calloc(count, esize);
void *q = malloc(count * esize);

The calloc() call fails and returns NULL because it cannot allocate
enough memory for 3000 32-byte objects. But the product in the second
call gets truncated, and malloc() receives a request for 30464 bytes
(3000 * 32 = 96000, 96000 % 65536 = 30464) -- which it might succeed
in finding, returning a pointer to the allocated memory. Unfortunately,
the allocation is only large enough for 952 objects, so the programmer
who thinks all is well and tries to store 3000 of them there is in
for an unpleasant surprise ...

Personally, I seldom use calloc() and rely on malloc()/realloc()
instead. When I allocate memory, it's generally because I want to
store something in it; it's not often that what I want to store is
lots and lots of zero bytes.

As a past inhabitant of the Bronze Age I would say you "get the
cigar!" <smile>

Can't remember ever seeing it spelled out, (and personally doubt it
ever did), but there was a general impression that the "c" in "calloc"
stood for char thus the initialization to zero. That was its primary
use - or rather one just tended to grab calloc when dealing with text.
And we piddled with text, and looked for nuls a lot back then.

You're dead on about the memory. We tend to forget but on many boxes
in the Bronze Age, heap was shared with stack within very limited
space. (One grew up, the other grew down, and vice versa.) All the
early implementations I'm aware of were built on malloc and did a safe
test. Meaning many shops considered it a first choice for average
allocations and portability, and malloc a second choice for when a
"custom storage" was needed. How times change. <g>

-ralph
 
E

Edward Rutherford

Keith said:
Also, it *should* detect overflow in the multiplication. This:

some_type *ptr = malloc(MANY * sizeof *ptr);

can misbehave if the mathematical result of the multiplication exceeds
SIZE_MAX; this:

some_type *ptr = calloc(MANY, sizeof *ptr);

should detect the overflow (and presumably return a null pointer).

Example:

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

int main(void) {
size_t n = 65537;
printf("SIZE_MAX = %zu\n", SIZE_MAX); printf("n = %zu\n",
n);
printf("n * n = %llu (mathematically)\n",
(unsigned long long)n * n);
printf("n * n = %zu (size_t)\n", n * n); void *mptr =
malloc(n * n);
void *cptr = calloc(n, n);
printf("malloc() %s\n", mptr == NULL ? "failed" : "succeeded");
printf("calloc() %s\n", cptr == NULL ? "failed" : "succeeded");
return 0;
}

Output (on my system):

SIZE_MAX = 4294967295
n = 65537
n * n = 4295098369 (mathematically) n * n = 131073 (size_t)
malloc() succeeded
calloc() failed

The malloc() call succeeds because the argument it was given was
mathematically incorrect and too small. The calloc() call failed
because it was unable to allocate that much memory.

In my opinion, this is a programming bug. I'd say it's really the
programmer's responsibility to be aware of the sizes of the types he is
using, and to avoid causing an overflow by using appropriate types and/or
casts.
 
A

army1987

"Upon further review," as they say in American football,
it *is* inconsistent! But it's not the fault of calloc() alone:

- calloc(), qsort(), and bsearch() put the element count
before the element size, but

- fread() and fwrite() put the element size before the
element count.

Well, at least it's consistent with other <stdlib.h> functions, and
<stdio.h> functions are consistent with each other...
 
K

Keith Thompson

Xavier Roche said:
Le 20/08/2012 13:52, army1987 a écrit :

Why call two functions when one is good ?

Because memset()ing a malloc()ed space to zero isn't all that useful in
most cases.
Moreover, calloc() will probably allocate large chunks of memory by
giving directly a mmap()'ed area. Using malloc()/memset() will probably
return a mmap()'ed area AND uselessly clear an already cleared area.

If the C standard library were being designed from scratch today,
it might make more sense to provide, say, a cmalloc() function that
acts like malloc() except that it guarantees the allocated space is
zeroed. It could take a single size_t argument, just like malloc().

calloc() ties together two features that don't seem closely
related, specifying the size with two arguments and zeroing the
allocated space. It's probably just yet another example of how the
C standard library evolved rather than being cleanly designed from
a clean slate.
 
K

Keith Thompson

ralph said:
Can't remember ever seeing it spelled out, (and personally doubt it
ever did), but there was a general impression that the "c" in "calloc"
stood for char thus the initialization to zero. That was its primary
use - or rather one just tended to grab calloc when dealing with text.
And we piddled with text, and looked for nuls a lot back then.

Hmm. I would have thought the "c" would stand for "clear". Since
calloc()'s parameters specify the number of objects and the size in
bytes of each object, it doesn't seem specifically suited for text.
 
K

Keith Thompson

Edward Rutherford said:
Keith said:
Also, it *should* detect overflow in the multiplication. This:

some_type *ptr = malloc(MANY * sizeof *ptr);

can misbehave if the mathematical result of the multiplication exceeds
SIZE_MAX; this:

some_type *ptr = calloc(MANY, sizeof *ptr);

should detect the overflow (and presumably return a null pointer).
[snip example]
Output (on my system):

SIZE_MAX = 4294967295
n = 65537
n * n = 4295098369 (mathematically) n * n = 131073 (size_t)
malloc() succeeded
calloc() failed

The malloc() call succeeds because the argument it was given was
mathematically incorrect and too small. The calloc() call failed
because it was unable to allocate that much memory.

In my opinion, this is a programming bug. I'd say it's really the
programmer's responsibility to be aware of the sizes of the types he is
using, and to avoid causing an overflow by using appropriate types and/or
casts.

Certainly it's a programming bug. The point is that calloc() and
malloc(0 deal with that bug differently.
 
R

ralph

Hmm. I would have thought the "c" would stand for "clear". Since
calloc()'s parameters specify the number of objects and the size in
bytes of each object, it doesn't seem specifically suited for text.

Sounds good to me.

-ralph
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,989
Messages
2,570,207
Members
46,783
Latest member
RickeyDort

Latest Threads

Top