General method for dynamically allocating memory for a string

R

Richard Heathfield

Richard Bos said:
Yes, you can. My point was that relying on this capability by setting
pointers to null after you're done with them is a bad programming habit,
not a good one, because you _will_ get in the habit of calling free() on
any which pointer, with the reasoning that "it'll either be a valid
pointer or null".

True enough - you shouldn't *rely* on it, in the sense of arbitrarily
littering your code with calls to free(). Nevertheless, setting pointers to
NULL after you're done with them is a /good/ habit, not a bad one. It's
called "defence in depth".
The truly good programming habit, in this case, is to do your bleedin'
bookkeeping, and keep in mind which pointers you have free()d, and which
you haven't.

That's certainly true, but it is also a good idea to recognise that you
might be fallible, and to take precautions against that fallibility. A null
pointer is far more useful than a pointer with an indeterminate value.
 
R

Richard Heathfield

jacob navia said:

Besides, if you want to use modern programming techniques, i.e. a
GARBAGE COLLECTOR and forget about free() accounting and all this mess,
this is

a VERY good habit

So far, so good...
since setting a pointer to NULL tells the GC that
the associated memory is free to reuse.

Oh deary deary me. Consider the following code fragment:

T *p = malloc(n * sizeof *p);
if(p != NULL)
{
T *q = p;
p = NULL;
dosomethingwith(q);

This is legal C, and although it may seem pretty pointless as written,
that's only because all the detail that would make it make sense - perhaps
a great deal of code - has been omitted. In real life, such code could well
form part of a well-written program, and might well be the best way to
express the programmer's intent.

If setting a pointer to NULL told the GC that the associated memory is free
to re-use, writing code like the above would become impossible.
It is interesting that none of the posters considers GC and how
it would enormously SIMPLIFY the writing of this code.

Garbage collection is too important to be left to the system - or, at least,
in comp.lang.c it is. The Lithp people will no doubt have a different
opinion, but that's their problem.
 
C

CBFalconer

Randall said:
I corrected the code to reflect all the feedback. Special thanks to:
Jay, Frederick - pointing out my off-by-one array index error.
Richard - questioning the gratuitous NULLing of already
NULLed pointers.
Frederick - explicitly making a string immutable.
Richard - bringing up the issue of freeing or deleting memory twice.

The points requiring further clarification:
Issue 1: #include <sys/types.h> vs. <stddef.h>
As a Unix programmer I am accustomed to writing the following code:
#include <sys/types.h>
#include <unistd.h>
As unistd.h requires sys/types.h to be used correctly. Only _unistd.h
includes sys/types.h directly (the implementation file).

For straight C coding (platform independent) I would prefer <stddef.h>
because it is smaller. Both are correct; this issue comes down to
taste and personal preference.

This is c.l.c, where we discuss the standard C language, as defined
by the various ISO standards and K&R. None of these standardizes
sys/types.h, nor _unistd.h. Thus they are off topic here. Their
use significantly impedes portability.
 
J

jacob navia

Richard said:
jacob navia said:




So far, so good...




Oh deary deary me. Consider the following code fragment:

T *p = malloc(n * sizeof *p);
if(p != NULL)
{
T *q = p;
p = NULL;
dosomethingwith(q);

???

The GC will find that q is not NULL. Then it will not release any
memory and everything will work as expected.

BUT

consider this

char *p;

int fn(void)
{
p = GC_malloc(1024);
// use of p
return 0;
}

This means this function leaves p pointing to a 1024 byte
memory block. The GC has NO way to know if this memory is used
or not, and will not reuse the block.

If you are done with the block, you set p to NULL and then the
GC knows that the 1024 byte block can be reused.

That's what I wanted to point out.
This is legal C, and although it may seem pretty pointless as written,
that's only because all the detail that would make it make sense - perhaps
a great deal of code - has been omitted. In real life, such code could well
form part of a well-written program, and might well be the best way to
express the programmer's intent.

If setting a pointer to NULL told the GC that the associated memory is free
to re-use, writing code like the above would become impossible.

Yes, Indeed. But this will not produce the effect of fooling the GC.
Garbage collection is too important to be left to the system - or, at least,
in comp.lang.c it is. The Lithp people will no doubt have a different
opinion, but that's their problem.

GC works in C and is in the standard distribution of lcc-win32. If by
"Lithp" you mean Lisp, yes, they use a GC, as do other languages
beside Lisp and C, for instance C#, Java, Eiffel and many others.

The problem with manual Garbage collection (malloc/free) is that is VERY
error prone. AND VERY BORING. The ideal task to be done by a machine
and not by people.

But this is an old discussion here.
 
R

Richard Heathfield

jacob navia said:
???

The GC will find that q is not NULL. Then it will not release any
memory and everything will work as expected.

You said "setting a pointer to NULL tells the GC that the associated memory
is free to reuse". Now you appear to be making a different claim - i.e.
that *all* pointers to that memory have to be set to NULL before the GC can
assume the associated memory is free to reuse. So now the programmer has to
*ensure* that, if he sets up multiple pointers that all end up at the same
address, he *has* to set *all* of them to NULL, to appease the garbage
collector; and if he misses even one, he'll have a memory leak.

Personally, I find it far more convenient to call free() when I'm done with
the memory block. Then, if I am in a tricksy part of the code and wish to
set pointers to NULL as a safety precaution, I can do that, and if I am in
a simple but performance-critical part of the code, I can choose not to do
it. The way you describe automatic garbage collection, I would be denied
those choices.

<snip>
 
C

CBFalconer

Richard said:
jacob navia said:

You said "setting a pointer to NULL tells the GC that the
associated memory is free to reuse". Now you appear to be making
.... snip ...

Revise the example:

T *p;

if (p = malloc(n * sizeof *p) {
T *q;

q = p+1; p = NULL;
dosomethingwith(q);
q--;
....
}

--
Some informative links:
http://www.geocities.com/nnqweb/
http://www.catb.org/~esr/faqs/smart-questions.html
http://www.caliburn.nl/topposting.html
http://www.netmeister.org/news/learn2quote.html
 
J

jacob navia

Richard said:
jacob navia said:




You said "setting a pointer to NULL tells the GC that the associated memory
is free to reuse". Now you appear to be making a different claim - i.e.
that *all* pointers to that memory have to be set to NULL before the GC can
assume the associated memory is free to reuse. So now the programmer has to
*ensure* that, if he sets up multiple pointers that all end up at the same
address, he *has* to set *all* of them to NULL, to appease the garbage
collector; and if he misses even one, he'll have a memory leak.

Yes, this can be a problem with the GC.

As you may know, I am not selling anything here, and I am not saying
that the GC is a "solve it all" magic bullet.

The algorithm of a conservative GC is that if a block of memory
is *reachable* from any of the roots (and in this case a global pointer
is a root) then that block can't be reused.

Setting pointers to NULL after you are done with them is a harmless
operation, nothing will be destroyed unles it can be destroyed.

It is MUCH simpler than calling free() only ONCE for each block.

If you have several dozen *ALIASES* for a memory block, as you did
in the code above, you must free the block ONLY ONCE, what can be
extremely tricky in a real life situation.
Personally, I find it far more convenient to call free() when I'm done with
the memory block.

If you have visibility, as in this example, OK. But if you don't,
i.e. you are passing memory blocks around to all kinds of routines that
may store that pointer in structures, or pass it again around, it
can be extremely tricky to know how many aliases you already have
for that memory block and to invalidate ALL of them.

Then, if I am in a tricksy part of the code and wish to
set pointers to NULL as a safety precaution, I can do that, and if I am in
a simple but performance-critical part of the code, I can choose not to do
it. The way you describe automatic garbage collection, I would be denied
those choices.

Setting a pointer to NULL is such a CHEAP operation in ALL machines
that it is not worth speaking about. It means zeroing a
memory location.
 
J

jacob navia

CBFalconer said:
Revise the example:

T *p;

if (p = malloc(n * sizeof *p) {
T *q;

q = p+1; p = NULL;
dosomethingwith(q);
q--;
....
}

The revised example will not fool the GC since the block can be reached
from q, since it points between the beginning and the end of the block.
 
R

Richard Heathfield

jacob navia said:
The revised example will not fool the GC since the block can be reached
from q, since it points between the beginning and the end of the block.

<shrug> Finding ways to fool it is a moderately trivial exercise:

p = malloc(n * sizeof *p);
pos = ftell(fp);
fwrite(&p, sizeof p, 1, fp);
p = NULL;
fseek(fp, pos, SEEK_SET);
fread(&p, sizeof p, 1, fp);
 
J

jacob navia

Richard said:
jacob navia said:




<shrug> Finding ways to fool it is a moderately trivial exercise:

p = malloc(n * sizeof *p);
pos = ftell(fp);
fwrite(&p, sizeof p, 1, fp);
p = NULL;
fseek(fp, pos, SEEK_SET);
fread(&p, sizeof p, 1, fp);

Obvious.

The GC documentation makes usages like this explicitely off limits.
The GC will not look for pointers:

o in the windows extra bytes provided by Microsoft
o in the hard disk (!!!)
o in your grandmother's mail where she said:

Why did you send me that pointer 0xFF45AC son?

I give it back, I do not needed it.
 
J

jacob navia

Richard said:
jacob navia said:




Indeed. So much for automatic garbage collection.

But what are you trying to prove with that nonsense Heathfield?

Imagine I say:

void foo(void)
{
char *p = malloc(1024);

for (int i=0; i<20; i++) {
free(p); // Crash here
}
}

Then I conclude:

"So much for malloc/free usage".


Wouldn't be stupid to conclude that because incorrect usage
the software crashes?

Is it possible to discuss here technical opinions without
trying just to prove at all costs that the other opinion is
just nonsense?

Note that I am not saying that malloc/free should not be used
neither that they are "bad software", but that the GC provides a very
useful alternative for people that do not like to mess with the
associated problems of manual garbage collection (malloc/free).

jacob
 
J

jacob navia

Richard said:
Well, then it isn't C, is it?

Richard

Of course. Making a restriction in the usage of
a library makes it "not c".

Then, freeing twice an object (a restriction in the usage of free() )
makes free() "not c".

What kind of logic do you use?
 
J

jacob navia

Richard said:
jacob navia said:




So let's not do that, then.




No, I don't know that. There is considerable evidence to the contrary.
What am I "selling"?

A product for zero dollars and zero cents?
 
P

Philip Potter

Richard Heathfield said:
jacob navia said:

Garbage collection is too important to be left to the system - or, at least,
in comp.lang.c it is. The Lithp people will no doubt have a different
opinion, but that's their problem.

....and elsethread...

Richard said:
jacob navia said:

Indeed. So much for automatic garbage collection.

Richard, while you have earned a lot of my respect and admiration through
your posting, I do feel that you are on a little bit of a crusade here. You
seem to be arguing that because garbage collection can't protect against all
memory leaks without care from the user, it is worthless. This is a somewhat
ridiculous argument - after all, manual memory management has precisely the
same property.

GCs don't give you a license not to think, but I didn't see anybody arguing
that they do.

Philip
 
R

Richard Heathfield

jacob navia said:
Richard Heathfield wrote:


But what are you trying to prove with that nonsense Heathfield?

If by "nonsense" you mean "correct C code", I am trying to prove, and
succeeding in proving, that automatic garbage collectors will struggle to
cope with correct C code.
Imagine I say:

void foo(void)
{
char *p = malloc(1024);

for (int i=0; i<20; i++) {
free(p); // Crash here
}
}

Then I conclude:

"So much for malloc/free usage".

It's not valid C. The example I gave /is/ valid C.
Wouldn't be stupid to conclude that because incorrect usage
the software crashes?

The example I gave is correct C. The example you gave is not.
Is it possible to discuss here technical opinions without
trying just to prove at all costs that the other opinion is
just nonsense?

Yes, and it happens all the time. Observe the technical discussions that
take place amongst those who actually know what they're talking about, and
you will see this very plainly.
Note that I am not saying that malloc/free should not be used
neither that they are "bad software", but that the GC provides a very
useful alternative for people that do not like to mess with the
associated problems of manual garbage collection (malloc/free).

Since this "very useful alternative" can't cope with correct C code, it
doesn't seem terribly useful to programmers who are using C. It may well be
useful to programmers who are using some other language, but that would be
a topic for some other newsgroup, not the comp.lang.c newsgroup.
 
R

Richard Heathfield

jacob navia said:
Of course. Making a restriction in the usage of
a library makes it "not c".

What he means, I think, is that the kind of automatic garbage collection you
advocate cannot be used reliably in place of manual garbage collection in
arbitrary C programs, because it fails to deal with some C constructs that
are perfectly legal. It may be useful to those who are prepared to code
around its limitations, but unless you are able to persuade ISO to change
the language definition to forbid C constructs that confuse the collector,
that collector cannot reasonably be advanced as a generic replacement for
manual garbage collection.
Then, freeing twice an object (a restriction in the usage of free() )
makes free() "not c".

Not so. It means that freeing a block of memory /twice/ is "not C".
What kind of logic do you use?

He uses the normal kind. I'm not so sure what logic /you/ are using but,
whatever kind it is, it doesn't work very well in persuading other people
that you have a valid point.
 

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,754
Messages
2,569,522
Members
44,995
Latest member
PinupduzSap

Latest Threads

Top