parallel arrays on the heap 
Author Message
 parallel arrays on the heap

I'm getting errors when trying to use parallel arrays on the heap.  The first
array works fine but any subsequent parallel array is altering the input.  I
included a simple program to demonstate this problem.   Note: if I include an
address of operator in the scanf in the getAges function, the program prints
the correct data but crashes the compiler.  What am I doing wrong?  

/* Filename: PARAHEAP.C */
// Stores two parallel arrays on the heap
// Prints the data
#include <stdio.h>
#include <conio.h>        // For Turbo C++ compiler clrscr() function
#include <stdlib.h>       // malloc() and free() require this header file

void getNames(char * names[3]); // Function prototype for data entry
void getAges(char * name[3], int * age[3]);
void prData(char * name[3], int * age[3]);  // Function prototype for
                                    // printing data

main()
{
        int ctr;        // for-loop control variable
        char * name[3]; // 3 element pointer array to char values
        int * age[3]; // 3 element pointer array to int values
        clrscr();       // Clears the output screen

        getNames(name);
        getAges(name, age);
        prData(name, age);

        for (ctr = 0; ctr < 3; ctr++)        // for-loop to deallocate heap memory
                { free(name[ctr]);
                  free(age[ctr]);
                }
        return 0;

Quote:
}

//*************************************************************
void getNames(char * name[3])
{
        int ctr;        // A for-loop control variable
        for (ctr = 0; ctr < 3; ctr++)
                { name[ctr] = (char *)malloc(20 * sizeof(char));
                  // Allocates heap memory to hold a 20 character string
                              printf("Please enter name #%d: ", ctr + 1);
                  gets(name[ctr]);
                }
        return;
Quote:
}

//**************************************************************
void getAges(char * name[3], int * age[3])
{
        int ctr;
        for (ctr = 0; ctr < 3; ctr++)
                { age[ctr] = (int *)malloc(1 * sizeof(int));
                  printf("\nNow enter the age of %s: ", name[ctr]);
                  scanf(" %d", age[ctr]);
                }
        return;
Quote:
}

//*************************************************************
void prData(char * name[3], int * age[3])
{
        int ctr;
        for (ctr = 0; ctr < 3; ctr++)
                { printf("\nEntry #%d, %s, is %d years old.",
                        ctr + 1, name[ctr], age[ctr]);
                }
        return;

Quote:
}

--



Thu, 27 Jun 2002 03:00:00 GMT  
 parallel arrays on the heap

in comp.lang.c.moderated:

Quote:
> I'm getting errors when trying to use parallel arrays on the heap.  The first
> array works fine but any subsequent parallel array is altering the input.  I
> included a simple program to demonstate this problem.   Note: if I include an
> address of operator in the scanf in the getAges function, the program prints
> the correct data but crashes the compiler.  What am I doing wrong?  

C does not define something named a "heap".  It does not specify what
method an implementation uses to allocate memory when you request it.
It only specifies what the results are when the allocation request
succeeds, and when it fails, and how to tell the difference between
the two.

Quote:
> /* Filename: PARAHEAP.C */
> // Stores two parallel arrays on the heap
> // Prints the data
> #include <stdio.h>
> #include <conio.h>   // For Turbo C++ compiler clrscr() function
> #include <stdlib.h>  // malloc() and free() require this header file

> void getNames(char * names[3]);    // Function prototype for data entry
> void getAges(char * name[3], int * age[3]);
> void prData(char * name[3], int * age[3]);  // Function prototype for
>                                // printing data

> main()

This form of definition for the function main() actually defines
main() to be a function which receives no parameters and returns an
int.  It is now illegal under the newly adopted update to the C
language standard, not that your compiler will change.  To be
compliant with the new C standard (you will use a newer compiler some
day!), you must write at least:

int main()

And it is better form to write it out in full:

int main(void)

Quote:
> {
>    int ctr;        // for-loop control variable
>    char * name[3]; // 3 element pointer array to char values
>    int * age[3]; // 3 element pointer array to int values
>    clrscr();       // Clears the output screen

>    getNames(name);
>    getAges(name, age);
>    prData(name, age);

>    for (ctr = 0; ctr < 3; ctr++)        // for-loop to deallocate heap memory
>            { free(name[ctr]);
>              free(age[ctr]);
>            }
>    return 0;
> }
> //*************************************************************
> void getNames(char * name[3])
> {
>    int ctr;        // A for-loop control variable
>    for (ctr = 0; ctr < 3; ctr++)
>            { name[ctr] = (char *)malloc(20 * sizeof(char));

There is one very serious problem, and two questionable things, in the
above line:

1.  SERIOUS PROBLEM:  Always test the pointer value returned by
malloc() for NULL.  malloc() will return NULL if it cannot satisfy the
allocation request, and if you use a NULL pointer to access or store
data you produce undefined behavior and cause a serious bug.

2.  BAD IDEA:  Do not cast the value returned by malloc().  It is not
necessary and can hide errors if <stdlib.h> is not included.

3.  UNNECESSARY:  You NEVER need to multiply by sizeof(char) in C (or
C++ for that matter).  In both languages, sizeof(char) is 1 by
definition.  It always has been, and it always will be.  Also, putting
a specific type in a memory allocation call can cause problems if the
type being allocated ever changes.  The best thing to do is:

type_ptr = malloc(20 * sizeof(*type_ptr));

That way if you decide to change the data type which type_ptr will
point to, this code will still be correct.  You won't have to hunt
down and change every malloc() call.

In this particular case, the best way to write the line in C is:

name[ctr] = malloc(20 * sizeof(*name[ctr]));

And then check it like this before using:

if (name[ctr] == NULL)
{
   /* error handling, clean up and exit function or program */

Quote:
}

else
{
   /* continue with what you want to do here */

Quote:
}
>              // Allocates heap memory to hold a 20 character string
>                               printf("Please enter name #%d: ", ctr + 1);
>              gets(name[ctr]);

The line above is your REAL problem.  Never, never, NEVER use gets().
It is the most dangerous function in the entire C standard library
because there is literally no way to use if safely.  Using gets() in a
program is ALWAYS a bug.

What do you think happens if the user types 50 characters into the 20
character space you just allocated?  Or 100?  Since there is no way to
tell gets() how much space you have allocated, it will place the first
20 characters into your allocated memory and then try to keep on
going, putting the remaining characters somewhere past the end of the
allocated memory, a serious bug.

See the link at the bottom.

Quote:
>            }
>    return;
> }
> //**************************************************************
> void getAges(char * name[3], int * age[3])
> {
>    int ctr;
>    for (ctr = 0; ctr < 3; ctr++)
>            { age[ctr] = (int *)malloc(1 * sizeof(int));

age[ctr] = malloc(1 * sizeof(*age[ctr]));

Again, check for NULL before using.

Quote:
>              printf("\nNow enter the age of %s: ", name[ctr]);
>              scanf(" %d", age[ctr]);

scanf() returns an integer value which tells you whether or not it
successfully converted a value to an integer, but you are not
bothering to check it.  Try entering "xyz" and hitting the enter key
when scanf() is being asked to convert to any numeric type.

Quote:
>            }
>    return;
> }
> //*************************************************************
> void prData(char * name[3], int * age[3])
> {
>    int ctr;
>    for (ctr = 0; ctr < 3; ctr++)
>            { printf("\nEntry #%d, %s, is %d years old.",
>                    ctr + 1, name[ctr], age[ctr]);
>            }
>    return;
> }

For starters, here is what the comp.lang.c (and comp.lang.c.moderated)
FAQ has to say about gets():

http://www.eskimo.com/~scs/C-faq/q12.23.html

Question 12.23
Why does everyone say not to use gets()?

Unlike fgets(), gets() cannot be told the size of the buffer it's to
read into, so it cannot be prevented from overflowing that buffer. As
a general rule, always use fgets(). See question 7.1 for a code
fragment illustrating the replacement of gets() with fgets().

I'd suggest you go take a look at the FAQ.  Read at least sections 7,
which is all about memory allocation, and 12, which is about stdio
functions.

Jack Klein
--
Home: http://jackklein.home.att.net
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.cerfnet.com/~mpcline/c++-faq-lite
alt.comp.lang.learn.c-c++
http://www.raos.demon.co.uk/acllc-c++/faq.html
--



Fri, 28 Jun 2002 03:00:00 GMT  
 parallel arrays on the heap
<pedantic>
<lazy>

Comments below

</lazy>
</pedantic>

--
Kindest Regards,
Guus Leeuw


Quote:
> I'm getting errors when trying to use parallel arrays on the heap.  The
first
> array works fine but any subsequent parallel array is altering the input.
I
> included a simple program to demonstate this problem.   Note: if I include
an
> address of operator in the scanf in the getAges function, the program
prints
> the correct data but crashes the compiler.  What am I doing wrong?

> /* Filename: PARAHEAP.C */
> // Stores two parallel arrays on the heap
> // Prints the data

is // allowed by ISO C 99? (I'm not up to date :-)))

Quote:
> #include <stdio.h>
> #include <conio.h> // For Turbo C++ compiler clrscr() function
> #include <stdlib.h> // malloc() and free() require this header file

> void getNames(char * names[3]); // Function prototype for data entry
> void getAges(char * name[3], int * age[3]);
> void prData(char * name[3], int * age[3]);  // Function prototype for
>       // printing data

> main()
> {
> int ctr; // for-loop control variable
> char * name[3]; // 3 element pointer array to char values

you're off here. This is an array that holds 3 pointers to char

Quote:
> int * age[3]; // 3 element pointer array to int values

you're off here. This is an array that holds 3 pointers to int

Quote:
> clrscr(); // Clears the output screen

> getNames(name);
> getAges(name, age);
> prData(name, age);

> for (ctr = 0; ctr < 3; ctr++) // for-loop to deallocate heap memory
> { free(name[ctr]);
>   free(age[ctr]);
> }
> return 0;
> }
> file://*************************************************************
> void getNames(char * name[3])
> {
> int ctr; // A for-loop control variable
> for (ctr = 0; ctr < 3; ctr++)
> { name[ctr] = (char *)malloc(20 * sizeof(char));
>   // Allocates heap memory to hold a 20 character string
>                               printf("Please enter name #%d: ", ctr + 1);
>   gets(name[ctr]);

what if I type a name that is longer than 20 characters? You'll have
corrupted memory...

Quote:
> }
> return;
> }
> file://**************************************************************
> void getAges(char * name[3], int * age[3])
> {
> int ctr;
> for (ctr = 0; ctr < 3; ctr++)
> { age[ctr] = (int *)malloc(1 * sizeof(int));

age[ctr] now holds the address of the allocated int.

Quote:
>   printf("\nNow enter the age of %s: ", name[ctr]);
>   scanf(" %d", age[ctr]);

and here, you overwrite it with a number which results in a bad pointer of
some sort.
Now, using the &(age[ctr]) is not allowed, for this will result in a pointer
to a pointer to an int.
You might try a dereference, although I'm not sure, whether that fixes it.

Quote:
> }
> return;
> }
> file://*************************************************************
> void prData(char * name[3], int * age[3])
> {
> int ctr;
> for (ctr = 0; ctr < 3; ctr++)
> { printf("\nEntry #%d, %s, is %d years old.",
> ctr + 1, name[ctr], age[ctr]);

age[ctr] still holds a pointer to an int.

Quote:
> }
> return;
> }

Now, last resort comments:
* Why don't you declare age as a 'int age[3]'?
* Why not declare 'name' and 'age' as globals or even better yet statics
(local for translation unit)?

--



Fri, 28 Jun 2002 03:00:00 GMT  
 parallel arrays on the heap


...

Quote:
>> /* Filename: PARAHEAP.C */
>> // Stores two parallel arrays on the heap
>> // Prints the data

>is // allowed by ISO C 99? (I'm not up to date :-)))

Yes.

Quote:
>> #include <stdio.h>
>> #include <conio.h> // For Turbo C++ compiler clrscr() function
>> #include <stdlib.h> // malloc() and free() require this header file

>> void getNames(char * names[3]); // Function prototype for data entry
>> void getAges(char * name[3], int * age[3]);
>> void prData(char * name[3], int * age[3]);  // Function prototype for
>>       // printing data

>> main()
>> {
>> int ctr; // for-loop control variable
>> char * name[3]; // 3 element pointer array to char values

>you're off here. This is an array that holds 3 pointers to char

That appears to be essentially what the comment says.

Quote:
>> int * age[3]; // 3 element pointer array to int values

>you're off here. This is an array that holds 3 pointers to int

That appears to be essentially what the comment says.

...

Quote:
>> file://**************************************************************
>> void getAges(char * name[3], int * age[3])
>> {
>> int ctr;
>> for (ctr = 0; ctr < 3; ctr++)
>> { age[ctr] = (int *)malloc(1 * sizeof(int));

>age[ctr] now holds the address of the allocated int.

>>   printf("\nNow enter the age of %s: ", name[ctr]);

>>   scanf(" %d", age[ctr]);

The space in the format string is redundant here because %d skips
leading white-space anyway.

Quote:
>and here, you overwrite it with a number which results in a bad pointer of
>some sort.

It is not possible to overwrite the value of a function argument in C since
C always passes function arguments by value. scanf() writes to what
the argument points at. age[ctr] looks reasonable here.

...

Quote:
>> file://*************************************************************
>> void prData(char * name[3], int * age[3])
>> {
>> int ctr;
>> for (ctr = 0; ctr < 3; ctr++)
>> { printf("\nEntry #%d, %s, is %d years old.",
>> ctr + 1, name[ctr], age[ctr]);

>age[ctr] still holds a pointer to an int.

This on the other hand is an error. Try *age[ctr].

Quote:
>> }
>> return;
>> }

>Now, last resort comments:
>* Why don't you declare age as a 'int age[3]'?
>* Why not declare 'name' and 'age' as globals or even better yet statics
>(local for translation unit)?

It is generally better to avoid globals and even statics where you can.

--
-----------------------------------------


-----------------------------------------
--



Fri, 28 Jun 2002 03:00:00 GMT  
 parallel arrays on the heap


Quote:

>in comp.lang.c.moderated:

>> I'm getting errors when trying to use parallel arrays on the heap.  The first
>> array works fine but any subsequent parallel array is altering the input.  I
>> included a simple program to demonstate this problem.   Note: if I include an
>> address of operator in the scanf in the getAges function, the program prints
>> the correct data but crashes the compiler.  What am I doing wrong?  

>C does not define something named a "heap". It does not specify what
>method an implementation uses to allocate memory when you request it.
>It only specifies what the results are when the allocation request
>succeeds, and when it fails, and how to tell the difference between
>the two.

Still, "heap" is a common informal name for the area of memory managed
by malloc. This usage doesn't relate to the form of datastructure with the
same name.

--
-----------------------------------------


-----------------------------------------
--



Fri, 28 Jun 2002 03:00:00 GMT  
 parallel arrays on the heap

Kirby) wrote in comp.lang.c.moderated:

Quote:



> >in comp.lang.c.moderated:

> >> I'm getting errors when trying to use parallel arrays on the heap.  The first
> >> array works fine but any subsequent parallel array is altering the input.  I
> >> included a simple program to demonstate this problem.   Note: if I include an
> >> address of operator in the scanf in the getAges function, the program prints
> >> the correct data but crashes the compiler.  What am I doing wrong?  

> >C does not define something named a "heap". It does not specify what
> >method an implementation uses to allocate memory when you request it.
> >It only specifies what the results are when the allocation request
> >succeeds, and when it fails, and how to tell the difference between
> >the two.

> Still, "heap" is a common informal name for the area of memory managed
> by malloc. This usage doesn't relate to the form of datastructure with the
> same name.

The reason that I point this out, which is only if I am answering the
post for other reasons, is that it leads to discussions of the
difference between the "stack" and the "heap" when the language does
not have either, although implementations might have either or both.

In this case I really like C++ term "free store", as it implies
nothing about location or organization.

Jack Klein
--
Home: http://jackklein.home.att.net
--



Sat, 29 Jun 2002 03:00:00 GMT  
 parallel arrays on the heap

Quote:

> I'm getting errors when trying to use parallel arrays on the heap.  The first
> array works fine but any subsequent parallel array is altering the input.  I
> included a simple program to demonstate this problem.   Note: if I include an
> address of operator in the scanf in the getAges function, the program prints
> the correct data but crashes the compiler.  What am I doing wrong?

> /* Filename: PARAHEAP.C */
> // Stores two parallel arrays on the heap
> // Prints the data
> #include <stdio.h>
> #include <conio.h>      // For Turbo C++ compiler clrscr() function
> #include <stdlib.h>     // malloc() and free() require this header file

> void getNames(char * names[3]); // Function prototype for data entry
> void getAges(char * name[3], int * age[3]);
> void prData(char * name[3], int * age[3]);  // Function prototype for
>                                     // printing data

> main()
> {
>         int ctr;        // for-loop control variable
>         char * name[3]; // 3 element pointer array to char values
>         int * age[3]; // 3 element pointer array to int values
>         clrscr();       // Clears the output screen

>         getNames(name);
>         getAges(name, age);
>         prData(name, age);

>         for (ctr = 0; ctr < 3; ctr++)   // for-loop to deallocate heap memory
>                 { free(name[ctr]);
>                   free(age[ctr]);
>                 }
>         return 0;
> }
> //*************************************************************
> void getNames(char * name[3])
> {
>         int ctr;        // A for-loop control variable
>         for (ctr = 0; ctr < 3; ctr++)
>                 { name[ctr] = (char *)malloc(20 * sizeof(char));
>                   // Allocates heap memory to hold a 20 character string
>                               printf("Please enter name #%d: ", ctr + 1);
>                   gets(name[ctr]);
>                 }
>         return;
> }
> //**************************************************************
> void getAges(char * name[3], int * age[3])
> {
>         int ctr;
>         for (ctr = 0; ctr < 3; ctr++)
>                 { age[ctr] = (int *)malloc(1 * sizeof(int));
>                   printf("\nNow enter the age of %s: ", name[ctr]);
>                   scanf(" %d", age[ctr]);
>                 }
>         return;
> }
> //*************************************************************
> void prData(char * name[3], int * age[3])
> {
>         int ctr;
>         for (ctr = 0; ctr < 3; ctr++)
>                 { printf("\nEntry #%d, %s, is %d years old.",
>                         ctr + 1, name[ctr], age[ctr]);
>                 }
>         return;
> }

> --


Well, the 'main' problem is that you aren't passing the array addresses
that you think. C passes arguments by value, not reference. Your
sub-functions (getNames(), etc.) are being passed an array of pointers
to char, NOT the address of the arrays you are trying to set. So, as a
result, the arguments you are passing are temporary. You aren't actually
modifying the arrays you are passing. Try the following changes to
getNames() and getAges().

void getNames( char** name )
{
         int ctr;        // A for-loop control variable
         for (ctr = 0; ctr < 3; ctr++)
                 { name[ctr] = (char *)malloc(20 * sizeof(char));
                   // Allocates heap memory to hold a 20 character
string
                   printf("Please enter name #%d: ", ctr + 1);
                   fflush(stdout); /* Make sure the prompt is visible.
*/
                   gets(name[ctr]);
                 }

Quote:
}

/* Since you are only reading the names, this can be passed by value, if
you wish. */
void getAges( char* name[3], int** age )
{
         int ctr;
         for (ctr = 0; ctr < 3; ctr++)
                 { age[ctr] = (int *)malloc(1 * sizeof(int));
                   printf("\nNow enter the age of %s: ", name[ctr]);
                   fflush(stdout); /* Make sure the prompt is visible.
*/
                   scanf(" %d", age[ctr]);
                 }

Quote:
}

There are a number of other issues, but this should at least allow you
to set the passed arrays correctly.

I hope this helps.

William Boyle
Principal Software Engineer
Brooks Automation, Inc.

--



Mon, 08 Jul 2002 03:00:00 GMT  
 parallel arrays on the heap

Quote:


[...]
> > main()
> > {
> >         int ctr;        // for-loop control variable
> >         char * name[3]; // 3 element pointer array to char values
> >         int * age[3]; // 3 element pointer array to int values
> >         clrscr();       // Clears the output screen

> >         getNames(name);
[...]
> > void getNames(char * name[3])
[...]
> Well, the 'main' problem is that you aren't passing the array addresses
> that you think. C passes arguments by value, not reference.

But the array name in the call becomes a pointer to the first element,
so the array is effectively passed by reference.  (Except the sizeof
operator is a bit tricky.  If getNames() computed sizeof(name), the
value would be sizeof(char **), not sizeof(char *[3]).)

Here is a simple program which demonstrates that the array is not a
copy.

==================================================
#include <stdio.h>

void getNames(char *name[3]);

int main(void)
{
  /* This array needs only 3 elements, but I put in four to show that
     the compiler does not check sizes of array parameters.  */
  char *data[4];
  data[0] = "original";
  data[1] = "values";
  data[2] = "lost";
  data[3] = "(unused)";

  getNames(data);

  puts(data[0]);
  puts(data[1]);
  puts(data[2]);

  return 0;

Quote:
}

void getNames(char *name[3])
{
  name[0] = "array";
  name[1] = "becomes";
  name[2] = "pointer";
Quote:
}

==================================================

[...]

Quote:
> Try the following changes to getNames() and getAges().

> void getNames( char** name )

This transformation is automatically done by the compiler.

[...]

Quote:
> /* Since you are only reading the names, this can be passed by value, if
> you wish. */
> void getAges( char* name[3], int** age )

It might be appropriate to use const here.  OTOH, there was
recently a thread about problems with const *const *.
--



Thu, 11 Jul 2002 03:00:00 GMT  
 
 [ 8 post ] 

 Relevant Pages 

1. HEAP[dllhost.exe]: HEAP: Free Heap block 1e32c28 modified at 1e32dc4 after it was freed

2. How to create an array on heap ?

3. Unable to load a file into parallel arrays...*help*

4. Help on Parallel Array's

5. 2D arrays in heap

6. allocating a multi-dimensional array on the heap?

7. array and heap

8. Binomial heaps / Fibonacci heaps

9. (ATL) COM dll heap vs CRT heap

10. HEAP error: Free heap block xxx mdofied at xxx

11. Invalid heap signature for heap

12. Heap errors when stressing Automation, _bstr_t, and watching heap blocks

 

 
Powered by phpBB® Forum Software