[comp.os.msdos.programmer] malloc

duvalj@bionette.cgrb.orst.edu (Joe Duval) (10/02/90)

Hi,
  I am using Turbo C on a 386-20 with 1 Meg of RAM.
  I have a program that needs to read in data from a couple of <30K files 
and display it on the screen in a scrollable window.  (I have the window
stuff down).  I am having a problem when I free up the space held by the
two data file and the windows.  And I have noticed that my program will
completely hang if I try to free up one of the variables before the others.
I have yet to get my program to work correctly.

My question is does the order of mallocing and freeing data matter?  Should
whatever I malloc first be free'd last or the opposite?  My program is showing
inconsistency that seems to relate to the malloc'ing and free'ing I am doing.

Here is how my program "works":
/* the input will be straight ASCII text produced by another Turbo C program.
   I want to be able to read in the data for the two files and display it on
   the screen.
*/

# include <appropriate files>

#define LINEDELTA 100
#define STRLEN     80

typedef char element[STRLEN] /* will be a line of a data file, lines can be up
								to 80 characters long */

int fillarray (char *filename, element *array)
{
	/* fill the variable array with text from the file in filename
	   realloc memory using LINEDELTA to increase the size of the array
	   if the size of the array is not big enough
    */
}
  
main ()
{	element	*data1, *data2;
  
	data1 = (element *) malloc (LINEDELTA*sizeof(element *));
	data2 = (element *) malloc (LINEDELTA*sizeof(element *));

  /* One of the files is usually about 20 times the size of the other.  If I
	 read in the smaller one first, the data in the smaller one gets trashed
	 when I read in the data to the second one.  What can I do to avoid that.
	 things are fine if I read in the data for the larger one first (I may 
	 not know which of the two is the larger at run time)
   */
	lines1 = fillarray ("test1", data1);
	lines2 = fillarray ("test2", data2);

   /* set up two windows.  Uses a structure to keep track of lots of things
	  about a window  ala Al Stevens "Memory Resident Utilities, Screen I/O
	  and Programming Techniques"
	  Windows structures are kept in a linked list

	  Display the data 
   */


/* program gets through to here with no gliches usually :-) */
/* BUT it only will go so far through these lines,  inconsistently crashing
   on one of these lines */

   free (data1);
   free (data2);
   delete_window (window1); /* free's up the space occupied by this window */
   delete_window (window2); /* and deletes it from the linked list */
}

Thanks for reading and I'll be waiting for responses

--
Joe Duval		duvalj@bionette.cgrb.orst.edu
Losing your temper generally represents the incipient stage of rectal-
cranial inversion.

jeenglis@alcor.usc.edu (Joe English Muffin) (10/02/90)

duvalj@bionette.cgrb.orst.edu (Joe Duval) writes:

>Hi,

>  I am using Turbo C on a 386-20 with 1 Meg of RAM.

Which memory model?  If it's small, tiny, or medium,
that could be part of the problem.

>My question is does the order of mallocing and freeing data matter?  Should
>whatever I malloc first be free'd last or the opposite?  My program is showing
>inconsistency that seems to relate to the malloc'ing and free'ing I am doing.

No; you can free() mallocked areas in any order,
as long as you don't free() the same thing twice.


>Here is how my program "works":

[ ... Much deleted ]

>#define LINEDELTA 100
>#define STRLEN     80

>typedef char element[STRLEN] /* will be a line of a data file, lines can be up
>								to 80 characters long */

>main ()
>{	element	*data1, *data2;
>
>	data1 = (element *) malloc (LINEDELTA*sizeof(element *));
>	data2 = (element *) malloc (LINEDELTA*sizeof(element *));

First, you really should check malloc's return
value.  It just might fail and return NULL.

>  /* One of the files is usually about 20 times the size of the other.  If I
>	 read in the smaller one first, the data in the smaller one gets trashed
>	 when I read in the data to the second one.  What can I do to avoid that.
>	 things are fine if I read in the data for the larger one first (I may
>	 not know which of the two is the larger at run time)
>   */

>	lines1 = fillarray ("test1", data1);
>	lines2 = fillarray ("test2", data2);

I would guess, that since fillarray() [description
deleted] calls realloc() several times, the heap
is probably getting too fragmented to allocate
any more big chunks, and one of the realloc()s
eventually fails.

You could try using an exponentially increasing
realloc() size instead of asking for
(current_size + LINEDELTA) elements each time.
That will decrease the number of realloc()s from
O(n) to O(log n), which will probably help.
Some good choices would be: current_size * 2
(O(2^n)) or current_size + last_size (O(Fib(n))).

As for malloc() in general, here are the rule-of-thumb
guidelines that I use:

+ Never malloc() lots of little items; malloc() off a
  big chunk and hand it out a piece at a time.  If you
  need to free() the items individually, write your
  own free-list manager for each size item.  (Or write
  a general-purpose one; that's what I did.)

  This saves malloc() overhead on each item allocated,
  and reduces fragmentation on many implementations of
  the allocator.

+ If you must malloc() small structures, malloc()
  the big stuff first.

  This helps fragmentation on most implementations of
  the allocator.

+ Don't use realloc() to dynamically resize things;
  use linked-lists or other dynamic structures.

+ If you must realloc(), then realloc() exponentially
  increasing sizes.

  This helps fragmentation on most implementations of
  the allocator.


(Why "on most implementations of the allocator?"
Malloc()/free() can be implemented in several
ways; I have no idea which one Turbo C uses.)


--Joe English

  jeenglis@alcor.usc.edu

6600m00n@ucsbuxa.ucsb.edu (Jihad 'R US) (10/05/90)

Your problem is this:
// your line
	data1 = (element *) malloc(LINEDELTA * sizeof(element *));
// the line as is should be.
	data1 = (element *) malloc(LINEDELTA * sizeof(element));

// you need the size of the item you are using.   otherwise, can you
// say "trashed memory ????"

Hope this helps
rob
(6600m00n@ucsbuxa.ucsbuxa)

empty@polari.UUCP (Terry Peterson) (06/13/91)

Under the following conditions, malloc() returns to main(), not to its caller,
foo().-- Using TC V2.0

    1) The penultimate caller, main() is compiled as a small model program
    2) The caller of malloc(), foo(), is a library service compiled as a
       large model.

Graphically,

    main()    /* Part of a small model program */
      |
       ------->foo()    /* A library service compiled as a large model */
                |
                 -------> malloc()

When malloc() is called from within foo() the compiler pushes both the CS
register and the IP register on the stack as expected.  However, malloc() does
not return to the code segment where foo() resides, but rather to the code
segment where main() resides!  

Disassembly of the code shows why:  When malloc() is entered, foo()'s CS and
IP are found in their proper place on the stack.  However, when malloc() is
done a "RET" instruction is executed, not RETF.  Hence, only the IP is popped
and the code "returns" to whatever segment malloc() happened to be mapped
into.  In this case, the segment that also contained main()'s code.

How can I arrange to have malloc() return to the calling procedure, foo(), and
NOT to main()?

Thanks, in advance,

/mtp

a_rubin@dsg4.dse.beckman.com (06/14/91)

In <4432@polari.UUCP> empty@polari.UUCP (Terry Peterson) writes:


>Under the following conditions, malloc() returns to main(), not to its caller,
>foo().-- Using TC V2.0

>    1) The penultimate caller, main() is compiled as a small model program
>    2) The caller of malloc(), foo(), is a library service compiled as a
>       large model.

>Graphically,

>    main()    /* Part of a small model program */
>      |
>       ------->foo()    /* A library service compiled as a large model */
>                |
>                 -------> malloc()

This is unlikely to work (as you reported in the section I did not quote).

If any routines are compiled large model, all routines must be compiled large
model or the linkages will not be correct.  In MSC, this would probably produce
an error message as both libraries SLIB.. and LLIB.. would be searched.

(I haven't tried it, but it's still wrong.)

You can, of course, explicitly declare everything you use in main as near.
--
2165888@mcimail.com 70707.453@compuserve.com arthur@pnet01.cts.com (personal)
a_rubin@dsg4.dse.beckman.com (work)
My opinions are my own, and do not represent those of my employer.