Using malloc in C++?

G

Gianni Mariani

James said:
... In practice,
there's no difference.)

.... no difference in the standard ? Or implementations ?

I have yet to encounter a C++ impl that systematically throws bad_alloc
on new char[0]. I can't say the same for malloc(0), although I have not
looked lately.
 
O

Owen Jacobson

I wouldn't even say that a fixed set of outputs is required.
All that it required is that IF it returns a non-null pointer,
the user must be able to read and write the n bytes starting at
that address. The same as with operator new(). The
requirements are exactly the same.

Well, since there are a finite number of potentially-valid values for
any given pointer type, I think that (much more comfortable)
definition is equivalent. :)
I wonder if it's worth noting that some widespread
implementations don't always meet these requirements, be it
malloc or operator new.

Probably. MS Visual C++ 6, as I recall, returns NULL from failed
'new' expressions rather than throwing. Then again, it was released
before the C++ standard.
And others meet them in rather strange
ways: I've encountered cases where malloc/operator new() simply
suspended the process until more memory was available---fully
conforming, since the standard doesn't say how long
malloc/operator new might take:).

Come to think of it, does the C++ standard constrain execution time
anywhere? I can imagine an implementation of int operator+ (int m,
int n) with O(nm*nm) complexity with large constant factors; would it
be conforming? What about the behaviour of the C++ standard library?

OT: Is google groups getting signatures wrong, or are you, James? The
sig separator should be "-- \n"; if that's what you're sending, then
google groups is no longer stripping signatures from replies.
 
J

James Kanze

Well, since there are a finite number of potentially-valid values for
any given pointer type, I think that (much more comfortable)
definition is equivalent. :)
Probably. MS Visual C++ 6, as I recall, returns NULL from failed
'new' expressions rather than throwing. Then again, it was released
before the C++ standard.

Yes. Still, I remember the one time I tested it (some eight or
nine years ago, under Windows NT), it just suspended the
process; it was impossible to get either a null pointer or an
exception. That could have depended on the configuration of our
system, however; in the end, malloc/operator new must sometimes
get memory from the system, and if the system lies to them (e.g.
the default configuration of Linux) or suspends the process for
an indeterminate time, there's not much the library code can do
about it.
Come to think of it, does the C++ standard constrain execution time
anywhere?

No. Usability and quality of implementation do, of course, but
not in any absolute terms.
I can imagine an implementation of int operator+ (int m,
int n) with O(nm*nm) complexity with large constant factors; would it
be conforming?

Of course. I've actually used machines where this was the case
for multiplication and divide.
What about the behaviour of the C++ standard library?

The only guarantees you get are complexity, expressed in the
number of certain primitive operations. Nothing about actual
time, nor the operations not being considered. (Thus, insert
into an std::list is "constant", measured in terms of calls to
the constructor. But it's perfectly conform to use an allocator
which is O(N^2), where N is the number of allocated blocks,
which would, of course, make the actual insertion times O(N^2).)
OT: Is google groups getting signatures wrong, or are you, James? The
sig separator should be "-- \n"; if that's what you're sending, then
google groups is no longer stripping signatures from replies.

I wish I knew. I've modified my environment so that I should be
sending the correct delimiter. At least, that's what, barring
false manipulation, ends up in the text buffer I'm editing (in
vim). Regretfully, a lot happens after that, and I'm having
problems finding out exactly what, much less doing anything
about it. Add to the fact that I post from several different
machines, with slightly different environments, and I don't
always know from which machine the posting which caused problems
comes from.

I've modified my signature on the machine here to indicate the
source; I'll do the same the next time I post from another
machine. If anyone sees a posting that is not conform, please
let me know, by email (no point in flooding the group), with all
of the headers of the posting in question, and I'll try and
trace it down. With no guarantee, however; I have no control
over what Google does. (On the other hand, I have sent error
messages to them before, and they've been acknowledged. I think
they do want to be conform, at least to some degree.)

I'm leaving my editor now. I've just confirmed that the
separator is "-- \n", with the space... And now I'm in the
post buffer of Firefox, and the space is still there.
 
J

James Kanze

On Jun 11, 12:38 pm, Owen Jacobson <[email protected]> wrote:

[...]
I wish I knew. I've modified my environment so that I should be
sending the correct delimiter. At least, that's what, barring
false manipulation, ends up in the text buffer I'm editing (in
vim). Regretfully, a lot happens after that, and I'm having
problems finding out exactly what, much less doing anything
about it. Add to the fact that I post from several different
machines, with slightly different environments, and I don't
always know from which machine the posting which caused problems
comes from.
I've modified my signature on the machine here to indicate the
source; I'll do the same the next time I post from another
machine. If anyone sees a posting that is not conform, please
let me know, by email (no point in flooding the group), with all
of the headers of the posting in question, and I'll try and
trace it down. With no guarantee, however; I have no control
over what Google does. (On the other hand, I have sent error
messages to them before, and they've been acknowledged. I think
they do want to be conform, at least to some degree.)
I'm leaving my editor now. I've just confirmed that the
separator is "-- \n", with the space... And now I'm in the
post buffer of Firefox, and the space is still there.

OK. The message showed up here for display with the space still
their. I hit the answer button, and 1) the signature was
present in the post box, and 2) the trailing space wasn't there.
So the problem seems to be with Google's posting mechanism (but
it could be that Google just doesn't strip signatures, and
Firefox strips the trailing blank). I'll edit my .sig to
suppress the accented characters, try again, and then get back
to Google with a bug report. (I should also try the same thing
from some other configurations. I've noticed that the headers
aren't the same on the machine I post from on week-ends as they
are here.)
 
J

James Kanze

... no difference in the standard ? Or implementations ?
I have yet to encounter a C++ impl that systematically throws bad_alloc
on new char[0]. I can't say the same for malloc(0), although I have not
looked lately.

I've yet to encounter an implementation of operator new which
did anything other than just call malloc, and throw bad_alloc if
it returned null.

Many, many years ago, before the C standard, it wasn't really
clear what malloc(0) should do, and implementations did vary
greatly. The C standards committee set the rules in 1988, and
in practice, I've never had any problems with malloc(0) with a
compiler which claimed to be ANSI or ISO C; they all seem to
return non-null pointers when they can. Obviously, I've not
tried all of the C/C++ compilers out there, but my impression is
that this is a problem that has disappeared.

(Of course, I can understand your hesitancy if you've actually
been burned by it. Even a long time ago. I tend to not forget
my wounds either.)
 
P

Peter

desktop wrote:
For a bucket of bytes or a C style struct, using malloc/free or
new/delete makes little or no difference. For objects with constructors
and/or destructors you have to use new/delete, so you may as well use
the same for all allocations.


it makes a difference even for POD as new throws when it fails and
malloc does not.
So for malloc you need to test for success. For new you don't.
 
G

Gianni Mariani

Peter said:
it makes a difference even for POD as new throws when it fails and
malloc does not.
So for malloc you need to test for success. For new you don't.

How do you handle the exception ?
 
P

Peter

How do you handle the exception ?


The point with exception handling is that you don't need to do
anything at the point where the exception is thrown -- means at the
point you call operator new.
You can deal with the exception in some function down the stack --
usually in main() or in the message function for GUI-applications.
In well designed code try-catch blocks are rare.
 
P

Peter

How do you handle the exception ?


in well designed code try-catch blocks are rare.
Usually they reside in the main function or in the message function in
case of GUI application.
Thus you don't need to do anything in case of new fails,
as some function down the stack will deal with the exception.
 
G

Gianni Mariani

Peter said:
in well designed code try-catch blocks are rare.
Usually they reside in the main function or in the message function in
case of GUI application.
Thus you don't need to do anything in case of new fails,
as some function down the stack will deal with the exception.


The question is specific - "how do *you* handle the exception ?".

I think that most people think like you do - I don't need to worry about
it, someone else will handle it when in fact it's never tested and never
designed to be handled.

Not that I do any better mind you ... I make just as much a mess of it
as anyone else does.

The only point I am making is that it's very unlikely that expecting
some higher up frame to deal with my lower down frame's issue without at
least making the right design choices is going to work is a recipe for
failure.

Go ahead and try it. Set the process memory limit and run various
commands. Many of them crash and burn. I'd at least expect them to
complain very early about needing more memory to run.

One way I handled this in the past was to initially reserve a large
chunk o memory (1MB or somthing like that) and upon failure to get more
memory, I would set a flag and free the block and retry the memory
allocation. This would then set the application to alert the user that
they're out of memory and they should terminate other apps etc. If they
chose to proceed it would attempt to allocate it's memory reserve so it
could do it again. This was the only successful low memory handling
scheme I ever wrote. It can't be implemented using exceptions because
it needs to work before exceptions are thrown.
 
J

James Kanze

The question is specific - "how do *you* handle the exception ?".

Note that there are two very distinct levels involved. Even if
you don't want to handle the error locally, you still have to
ensure that your objects are in a coherent state when an
exception passes through. Something like:

int* p1 = new int[ 100 ] ;
int* p2 = new int[ 100 ] ;

is a memory leak, for example.

Luckily, if you're using operator new (but not malloc), this is
easy to test. Just replace the global operator new with one
which "runs out" on command. (This is a standard part of my
memory checking operator new.)
I think that most people think like you do - I don't need to
worry about it, someone else will handle it when in fact it's
never tested and never designed to be handled.

There's certainly no excuse for it never being tested. Just
insert an appropriate operator new, and write something like:

int allocCount = 0 ;
bool noMem = true ;
while ( noMem ) {
t.nextMinorCycle() ;
++ allocCount ;
noMem = false ;
Gabi::MemoryCheck memchk ;
memchk.setErrorTrigger( allocCount ) ;
try {
SetOfCharacter s( it.begin(), it.end() ) ;
} catch ( std::bad_alloc& ) {
noMem = true ;
}
memchk.resetErrorTrigger() ;
t.verify( memchk.unfreedCount(), 0 ) ;
}
Not that I do any better mind you ... I make just as much a mess of it
as anyone else does.

I know what you mean. I added the option to my MemoryCheck
class many years ago, but if you look at the code at my site,
none of the tests use it. I only started systematically testing
like this a couple of months ago. I might add that I found an
error the very first time I did such a test. And I consider
myself a fairly careful programmer.
The only point I am making is that it's very unlikely that
expecting some higher up frame to deal with my lower down
frame's issue without at least making the right design choices
is going to work is a recipe for failure.
Go ahead and try it. Set the process memory limit and run
various commands. Many of them crash and burn.

And those that don't crash and burn are likely to leak
resources, or end up in an inconsistent internal state.
I'd at least expect them to
complain very early about needing more memory to run.
One way I handled this in the past was to initially reserve a large
chunk o memory (1MB or somthing like that) and upon failure to get more
memory, I would set a flag and free the block and retry the memory
allocation. This would then set the application to alert the user that
they're out of memory and they should terminate other apps etc. If they
chose to proceed it would attempt to allocate it's memory reserve so it
could do it again. This was the only successful low memory handling
scheme I ever wrote. It can't be implemented using exceptions because
it needs to work before exceptions are thrown.

It's probably the best you can do in many cases. I work on
large scale servers. For the most part, they run on dedicated
machines, with what should be sufficient memory. If we run out
of memory, it's almost certainly because of a memory leak; we
free a pre-allocated block with hopefully enough memory to
ensure that logging will still work, log the problem, and abort
(which of course triggers a restart of the application). This
is done, of course, by means of the new_handler, and not by
catching exceptions.

There are exceptions. An LDAP server can receive arbitrarily
complex requests from the user, for example. Typically, it will
build such requests as a tree (with AND, OR and NOT nodes, as
well as predicates in leaf nodes). If one request fails because
of a lack of memory, it may simply be that it is too complex.
Catch bad_alloc, reject that request, and continue running. In
theory, at least; typically, the requests are parsed using
recursive descent, so where you run out of memory is when trying
to grow the stack:). (There's no standard solution for that,
but I know how to handle it under Solaris.)

The fact that there can be such cases, of course, means that in
library code, you have to do the right thing, even if it doesn't
matter in most applications.
 

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

Staff online

Members online

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,816
Latest member
SapanaCarpetStudio

Latest Threads

Top