Listing 1

/*----------------------------------------------------------------------
++
  membug.c
  Demonstrate MSC malloc() large size problem

  Description

      membug.c demonstrates a problem that occurs when Microsoft C,
      version 5.10 is used to allocate and free large blocks of
      memory.

      If this program is compiled and run, you will find
      that the first list will have significantly more memory
      allocated to it. The second list will only have 1 to 2
      elements allocated to it, depending on your memory layout.

      The basic problem is that MSC never deallocates a DOS
      allocated memory block, even if the memory call is about to
      fail. Thus, the first list causes the MSC runtime to allocate
      memory in 48K blocks. When the first list is freed, the
      48K blocks remain. Then, when the second list is allocated,
      there are only 2 blocks that DOS can carve the 60K blocks
      from: the default memory segment and the last DOS memory block.
      The default memory segment is 64K, so we should always get
      an allocation from it. The last memory block can be expanded
      by DOS to fit the 60K request if your memory layout will allow
      it.

      Note that if you reverse the order of memory requests, both
      will return the same number of memory blocks because the 48K
      requests will fit in the 60K blocks.

  Compilation

      Compilation is under Microsoft C, version 5.1 using the command:

             c1 /W3 AL membug.c

  Execution

      Execution of the program should use the command line:

             membug > membug.out

+-

  $Log$

--
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dos.h>

/* Local definitions */
/* ----------------- */
#define FIRST_ALLOC_SIZE    48000
#define SECOND_ALLOC_SIZE   60000

/* Memory allocation list structure */
/* -------------------------------- */
typedef struct mb                       /* Memory list node            */
       {                              /* ---------------------------- */
       struct mb *     mb_next ;      /* Pointer to next block        */
       char            mb_data ;      /* Start of data area           */
                                 /* Actual data area size is     */
                                 /* determined by runtime        */
                                 /* malloc() argument            */
       } MEM_BLOCK ;

/* Pointer conversion macros */
/* ------------------------- */
#define FARPTR_SEG(a) ((int) (((unsigned long) (a)) >> 16))
#define FARPTR_OFF(a) ((int) ((long) (a)))
#define MAKE-FARPTR(seg,off) ((void far *) ((((long) (seg)) << 16) +
                         (of)))

/* Function prototypes */
/* ------------------- */
void    main() ;
void    DOS_Mem_Display(char *) ;

/*--------------------------------------------------------------------*/

+
  main
  Entry point for MSC dynamic memory test

  Usage

     void
     main()

  Parameters

     None

Description

  main() is the entry point for the Microsoft C dynamic memory
  test. The function allocates a list of FIRST_ALLOC_SIZE
  elements, frees the first list, allocates a second
  list of SECOND_ALLOC_SIZE, and frees the second list. The
  statistics printed out are the total bytes allocated by each
  allocation and a dump of the DOS memory list after each
  allocation/free.

Notes

     None

-
*/

void
main()
{
MEM_BLOCK * list ;
MEM_BLOCK * p ;
long    first_size ;
long    second_size ;

/* Allocate list using first allocation size */
/* ----------------------------------------- */
list = NULL ;
first_size = 0;
while ((p = (MEM_BLOCK *) malloc(FIRST_ALLOC_SIZE)) != NULL)
   {
   p->mb_next = list ;
   list = p ;
   firstsize += FIRST_ALLOC_SIZE ;
   }

/* Print first allocation results */
/* ------------------------------ */
printf("***** First allocation - %ld *****\n\n", first_size) ;
DOS_Mem_Display("After first allocation/n") ;

/* Free first list */
/* --------------- */
while (list != NULL)
   {
   p = list ;
   list = list->mb_next ;
   }

DOS_Mem_Display("After first free\n") ;

/* Allocate list using second allocation size */
/* ------------------------------------------- */
list = NULL ;
second_size = 0 ;
while ((p = (MEM_BLOCK *) malloc(SECOND_ALLOC_SIZE)) != NULL)
   {
   p->mb_next = list ;
   list = p ;
   second_size += SECOND_ALLOC_SIZE ;
   }

/* Print second allocation results */
/* ------------------------------- */
printf("***** Second allocation - %ld *****\n\n", second_size ;
DOS_Mem_Display("After second allocation\n") ;

/* Free second list */
/* ---------------- */
while (list != NULL)
   {
   p = list ;
   list = list->mb_next ;
   free (p) ;
   }

DOS_Mem_Display("After second free\n") ;
}

/*--------------------------------------------------------------*/

  DOS_Menu_Display

      psp_seg = *(p+1) + ((*(p+2)) << 8) ;
      blk_paras = *(p+3) + ((*(p+4)) << 8) ;
      size = ((long) blk_paras) << 4 ;
      if (psp_seg == 0)
             {
             prg = (unsigned char far *) "(free)" ;
             total += size ;
             }
      else
             {
             ip = (unsigned int far *) MAKE_FARPTR(psp_seg, 0x2c) ;
             prg = MAKE_FARPTR(*ip, 0) ;
             while (*prg != '\0')
                    {
                    prg += strlen((char *) prg) + 1 ;
                    }
             prg += 3 ;
             }
      sprintf(str, "%5d %91d %p", idx++, size, p) ;

      printf("%s\t%s\n", str, prg) ;

      if (*p == 'z'}
             {
             break ;
             }

      p = MAKE_FARPTR(FARPTR_SEG(p) + blk_paras + 1, 0) ;
      }

sprintf(str, "Total Free: %ld", total) ;
printf("%s\n\n", str) ;
}

/*--------------------------------------------------------------------*/