[comp.lang.c] Malloc in Turbo C

gordon@qfagus.OZ (Peter Gordon) (02/27/90)

Please tell me I'm doing something stupid.  The following code works on
Microsoft C and a MIPS/1000 runnung UNIX Sys V Release 4_0.
On Turbo C  (ver 2) it crashes after a variable number of frees.  I've tried
all memory models and far pointers and can't think of much else to
try.  Commonsense tells me that such a basic bug would not be
in Version 2, would some kind person point me in the right direction.
(I've tried the code on 8088, and 80386 machines, both with and without
floating point emulation.)
Peter Gordon.
------------------- cut here ------------------------------
echo "Extracting tst.c"
sed -e 's/^X//' > tst.c << END_OF_FILE
X#include	<stdio.h>
X#ifdef __TURBOC__
X#include	<alloc.h>
X#else
X#include	<malloc.h>
X#endif
X#include	<stdlib.h>
Xmain()
X{
X	char **head, **cp;
X	int	i;
X	head = (char **)malloc(200 * sizeof(char **));
X	for(cp = head, i = 0; i < 200; ++i, ++cp)
X	{
X		fprintf(stdout,"Freeing %d\n", i);
X		fflush(stdout);
X		free(cp);
X	}
X}
END_OF_FILE

dfoster@jarthur.Claremont.EDU (Derek R. Foster) (02/28/90)

In article <26316@qfagus.OZ> gordon@qfagus.OZ (Peter Gordon) writes:
>
>Please tell me I'm doing something stupid.  The following code works on
>Microsoft C and a MIPS/1000 runnung UNIX Sys V Release 4_0.
>On Turbo C  (ver 2) it crashes after a variable number of frees.  I've tried
>------------------- cut here ------------------------------
>X	char **head, **cp;
>X	int	i;
>X	head = (char **)malloc(200 * sizeof(char **));

Wait a minute. How can you have a pointer to a pointer to char aimed at an
array of two hundred pointers to pointers to char? This is a type clash.
If it is a pointer to a pointer to char, it should point (surprisingly
enough) to one, or a block of, pointers to char.
shouldn't it be
   head = (char **) malloc(200 * sizeof(char *));
or if the array IS supposed to be of char **'s, 
  char ***head, ***cp;
  head = (char ***) malloc(200 * sizeof(char **));
?

>X	for(cp = head, i = 0; i < 200; ++i, ++cp)
>X	{
>X		fprintf(stdout,"Freeing %d\n", i);
>X		fflush(stdout);
>X		free(cp);
>X	}

Think about what you're doing. You've created ONE array of 200 pointers to
char pointers,
and assigned head to point to the address of it. Then you (200 times)
try to free what cp points to (it starts out as head), then increment
cp. The first time it goes through the loop, it frees your ENTIRE array of
pointers. Every other time, you're not passing 'free' the address of
the start of a malloc'ed block of memory. Instead, you're giving it
a pointer into the MIDDLE of a block of memory (which has already been
freed anyway). If you've only malloc'ed one thing, why are you
trying to free more than one thing? Mallocs and frees should be on a
one-to-one correspondance. Also, you should never pass an address to
'free' that wasn't returned DIRECTLY, WITHOUT BEING INCREMENTED OR
MODIFIED IN ANY WAY, by malloc (or some other allocation routine).
Remember, when you 'malloc' a block of memory, the computer considers
it ONE BLOCK. You can't dispose of it piecemeal (other than with
realloc, etc.)

I think I may see what you are trying to do here. I may be wrong, but
I would guess that head is supposed to point to an array of pointers
(should these be char pointers instead of pointers to char pointers?)
and that those pointers point to other chunks of
memory which are what you are really trying to free. (for instance, a
dynamically allocated array of strings).

If this is the case, you are attempting to free WHAT EACH OF THOSE
POINTERS POINTS TO. Ignoring the fact that the above program never
assigns any memory for those pointers to point to (in fact, does
not initialize them at all), there is another mistake in it.
You are trying to free the memory that cp points to, when in fact what
you want to do is free the memory that what cp POINTS TO points to. In
other words, instead of free(cp), use free(*cp).
Of course, after you've done all that, your original array of pointers
will still have to be freed (via free(head)).

Of course, I could be completely wrong about the intent of this code.
If, in fact, all you are really trying to do is deallocate the space
assigned to your array of head, just free(head). No loop is necessary.

Hope this helps!

Derek Riippa Foster

CMH117@psuvm.psu.edu (Charles Hannum) (02/28/90)

In article <26316@qfagus.OZ>, gordon@qfagus.OZ (Peter Gordon) says:
>
>Please tell me I'm doing something stupid.  The following code works on
Okay.  You're doing something really stupid.  {B-) JOKE B-) JOKE B-) JOKE}

>Microsoft C and a MIPS/1000 runnung UNIX Sys V Release 4_0.
It shouldn't.  You were lucky.

>On Turbo C  (ver 2) it crashes after a variable number of frees.  I've tried
>all memory models and far pointers and can't think of much else to
>try.  Commonsense tells me that such a basic bug would not be
>in Version 2, would some kind person point me in the right direction.
Okay, I'll give you a few pointers.  ;-)


>#include       <stdio.h>
>#ifdef __TURBOC__
>#include       <alloc.h>
>#else
>#include       <malloc.h>
>#endif
>#include       <stdlib.h>
>main()
>{
>       char **head, **cp;
        ~~~~~~~  A "char **" is a "pointer to a char *" (or, to make Chris
                 Torek cringe, a "pointer to an array??(SIZE_UNKNOWN??) of
                 char *'s)  (Note:  Excuse the ASCII tryglyphs; I'm using an
                                    IBM 3179G terminal right now.)
>       int     i;
>       head = (char **)malloc(200 * sizeof(char **));
                                            ~~~~~~~  Thus, you really want to
                                                     allocate space for 200
                                                     "char *"s; but this is not
                                                     why it crashes.  (The
                                                     pointers are the same
                                                     size, anyway.)
>       for(cp = head, i = 0; i < 200; ++i, ++cp)
>       {
>               fprintf(stdout,"Freeing %d\n", i);
>               fflush(stdout);
>               free(cp);

I almost cried when I saw this.

You are trying to free the space for the 200 "char *"s independently.  You
can't do that; you allocated them as one block, and you must free them as one
block.  Please, PLEASE, read K&R2, or the Turbo C user's guide!!

>       }
>}


As a side note:  No insult intended against you, but this is a prime example
of why we need to teach people a programming language before we expect them
to use it.  The concept of pointers is so basic to the C language that it seems
impossible that you've had any formal education in the language.  Is it that
you've never had the opportunity, never had the time, or never had the need
before?  And have you considered taking a class (assuming one is available)?


Virtually,
- Charles Martin Hannum II       "Klein bottle for sale ... inquire within."
    (That's Charles to you!)     "To life immortal!"
  cmh117@psuvm.{bitnet,psu.edu}  "No noozzzz izzz netzzzsnoozzzzz..."
  c9h@psuecl.{bitnet,psu.edu}    "Mem'ry, all alone in the moonlight ..."

lmiller@aerospace.aero.org (Lawrence H. Miller) (02/28/90)

In article <26316@qfagus.OZ> gordon@qfagus.OZ (Peter Gordon) writes:
>
>Asks why the following code produces anomalous results with Turb C.

(various header files removed for clarity)

main()
{
	char **head, **cp;
	int	i;
	head = (char **)malloc(200 * sizeof(char **));
	for(cp = head, i = 0; i < 200; ++i, ++cp)
	{
		fprintf(stdout,"Freeing %d\n", i);
		fflush(stdout);
		free(cp);
	}
}

From a VERY early draft of the ANSI C standard:

	void free(void *ptr)

	...if the argument does not match a pointer earlier returned by
	the calloc, malloc, or realloc function, or if the space has been
	deallocated by a call to free or realloc, the behavior is
	undefined.

Your first call to free frees all the allocated space.  Subsequent calls
to free using a pointer into freed space, produces undefined behavior.

Here is the definition of "Undefined behavior":

	Behavior for an erroneous program construct, for which the standard
	imposes no requirements.  Permissible undefined behaavior ranges
	from ignoring the situation completely with unpredictable
	results...to terminating a[n]...execution with a diagnostic
	message.
Larry Miller
Aerospace Corporation
lmiller@aerospace.aero.org
213-336-5597

cpcahil@virtech.uucp (Conor P. Cahill) (02/28/90)

In article <26316@qfagus.OZ> gordon@qfagus.OZ (Peter Gordon) writes:
>X	head = (char **)malloc(200 * sizeof(char **));
>X	for(cp = head, i = 0; i < 200; ++i, ++cp)
>X	{
>X		fprintf(stdout,"Freeing %d\n", i);
>X		fflush(stdout);
>X		free(cp);
>X	}

The problem is that you can only call free with a pointer that is 
returned by malloc.  In this code the only valid call to free 
is free(head) or free(cp) for the first loop.

Due to a small amount of malloc chain info, most of your calls to free
are ignored, but you hit just the right circumstances to cause a problem
once in a while.

-- 
+-----------------------------------------------------------------------+
| Conor P. Cahill     uunet!virtech!cpcahil      	703-430-9247	!
| Virtual Technologies Inc.,    P. O. Box 876,   Sterling, VA 22170     |
+-----------------------------------------------------------------------+

pdsmith@bbn.com (Peter D. Smith) (02/28/90)

In article <90058.153755CMH117@psuvm.psu.edu> CMH117@psuvm.psu.edu (Charles Hannum) writes:
>
>As a side note:  No insult intended against you, but this is a prime example
>of why we need to teach people a programming language before we expect them
>to use it.  The concept of pointers is so basic to the C language that it seems
>impossible that you've had any formal education in the language.  Is it that
>you've never had the opportunity, never had the time, or never had the need
>before?  And have you considered taking a class (assuming one is available)?
>

C'mon lay off the guy.  Malloc and Free are tough to get right.  A story:
at a previous job, we built a run time allocation checker (bounds checking,
freeing the same memory twice, freeing a pointer after incrementing it, not
freeing memory, allocation statistics... the whole ball of wax). **Every
single library** by **every single C engineer** had problems!

Most implementations of C are screwed up: they give you a dangerous tool
but don't give you a handle on double-checking the results.

				Peter D. Smith

darcy@druid.uucp (D'Arcy J.M. Cain) (02/28/90)

In article <26316@qfagus.OZ> gordon@qfagus.OZ (Peter Gordon) writes:
>
>Please tell me I'm doing something stupid.
OK.  You're doing something stupid.  :-)

>#include	<stdio.h>
>#ifdef __TURBOC__
>#include	<alloc.h>
>#else
>#include	<malloc.h>
>#endif
>#include	<stdlib.h>
>main()
>{
>	char **head, **cp;
>	int	i;
>	head = (char **)malloc(200 * sizeof(char **));
>	for(cp = head, i = 0; i < 200; ++i, ++cp)
>	{
>		fprintf(stdout,"Freeing %d\n", i);
>		fflush(stdout);
>		free(cp);
>	}
>}
The first call to free(cp) frees up the entire memory allocated by malloc.
The functions malloc and free don't have any idea what the allocated space
is used for and don't know what size the items are.  The value returned by
malloc is a pointer and the argument to free is a previously returned
pointer.  For 199 times in the above code you are freeing pointers that
haven't been allocated.  in malloc(3C) this is stated to produce undefined
results so you got what was promised.

Also, even given what you were trying to do, didn't you mean 'cp++' in
the for loop statement, not '++cp'?

-- 
D'Arcy J.M. Cain (darcy@druid)     |   Thank goodness we don't get all 
D'Arcy Cain Consulting             |   the government we pay for.
West Hill, Ontario, Canada         |
(416) 281-6094                     |

richard@calvin.spp.cornell.edu (Richard Brittain) (03/02/90)

In article <52749@bbn.COM> pdsmith@spca.bbn.com (Peter D. Smith) writes:
>
>C'mon lay off the guy.  Malloc and Free are tough to get right.  A story:
>at a previous job, we built a run time allocation checker (bounds checking,
>freeing the same memory twice, freeing a pointer after incrementing it, not
>freeing memory, allocation statistics... the whole ball of wax). **Every
>single library** by **every single C engineer** had problems!
>
>Most implementations of C are screwed up: they give you a dangerous tool
>but don't give you a handle on double-checking the results.
>
>				Peter D. Smith

As a side note to this side note, I'll just mention that with Turbo-C, the
chances of bombing out if you try to use something from a free'd block are
VERY high, since the free() routine seems to modify the contents of the
block immediately.  I have found cases of code ported from various unix
compilers, and from Microsoft C, which had been doing this succesfully 
and showed up the error right away with TurboC.  In one case the original
author told me the bug had been there over 3 years of development, but
no-one noticed on the other systems.   On a pc, you have even more incentive
to get it right, since getting it wrong often means reaching for the red button.

Richard Brittain,                   School of Elect. Eng.,  Upson Hall   
                                    Cornell University, Ithaca, NY 14853
ARPA: richard@calvin.spp.cornell.edu	
UUCP: {uunet,uw-beaver,rochester,cmcl2}!cornell!calvin!richard

CMH117@psuvm.psu.edu (Charles Hannum) (03/02/90)

In article <52749@bbn.COM>, pdsmith@bbn.com (Peter D. Smith) says:
>
>Most implementations of C are screwed up: they give you a dangerous tool
>but don't give you a handle on double-checking the results.

Too much error-checking slows things down.  The assumption in C is that the
programmer *knows* what {s}he is doing.  With power come responsibility ...


Virtually,
- Charles Martin Hannum II       "Klein bottle for sale ... inquire within."
    (That's Charles to you!)     "To life immortal!"
  cmh117@psuvm.{bitnet,psu.edu}  "No noozzzz izzz netzzzsnoozzzzz..."
  c9h@psuecl.{bitnet,psu.edu}    "Mem'ry, all alone in the moonlight ..."