[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.c

Passing Dynaically-Sized Arrays

CapCity

7/10/2008 8:45:00 PM

I'm sure I'm missing something simple - I do not code in C regularly, and
the breaks are long enough for me to forget.

The situation I have is I need to create an array but I do not know the
dimension until runtime. I pass the pointer to a function which then
determines the size and then creates and populates it. I can walk the array
OK in the function, but the app crashes when I try to do so in the calling
routine.

Here's a small program that recreates the exception:

#include <stdio.h>
#include <stdlib.h>
#include "ArrayManagement.h"
#include "Test.h"

void MakeIVector(int *iv, int n1) {
int i;
iv = iVector(n1);
for (i = 0; i < n1; i++) iv[i] = i*2;
for (i = 0; i < n1; i++) printf("%i\n", iv[i]);
return;
}

int main() {
int n1 = 8;
int i, j, k;

int* iv = iVector(n1);
for (i = 0; i < n1; i++) iv[i] = i;
for (i = 0; i < n1; i++) printf("%i\n", iv[i]);
Free_iVector(iv);
printf("\n");

int *iv2;
MakeIVector(iv2, n1);
printf("\n");
for (i = 0; i < n1; i++) iv2[i] = i;
for (i = 0; i < n1; i++) printf("%i\n", iv2[i]);

exit(0);
}

ArrayManagement is a tried and tested module that creates 1, 2 and 3
dimesioned arrays based on dimensions passed in.

In main a 1 dimensional array (vector) is created, populated and dumped to
console. No problems.

It then calls a function (MakeIVector) which creates, populates and dumps an
array to the console. This also works.

Upon the return from MakeIVector it blows up while trying to assign values
to elements of iv2. If I remove that loop, and only do the printf loop, I
get values to the screen, but it looks like addresses.

What am I doing wrong? I'd like to be able to pass these arrays to and from
functions.

To extend this, would anything change if I needed to pass these through
several layers of functions?

What about if I need to do this with 2 or three dimensional arrays? What
syntax would I need then?

This is ANSI C, and I use gcc.

Thanks in advance!



5 Answers

roberson

7/10/2008 9:04:00 PM

0

In article <wxudk.77$8M.48@fe181.usenetserver.com>,
CapCity <Cap@City.com> wrote:
>#include <stdio.h>
>#include <stdlib.h>
>#include "ArrayManagement.h"
>#include "Test.h"
>
>void MakeIVector(int *iv, int n1) {
> int i;
> iv = iVector(n1);

iv is a pointer, so presumably iVector(n1) is allocating memory
and returning the address of that memory, then to be stored into iv

> for (i = 0; i < n1; i++) iv[i] = i*2;
> for (i = 0; i < n1; i++) printf("%i\n", iv[i]);
> return;

An initial value for iv was passed in to MakeIVector but
the new value (the address returned by iVector()) is not being
passed out of the routine. Thus the calling routine has no idea
where the allocated memory is.
>}

>int main() {
> int n1 = 8;
> int i, j, k;
>
> int* iv = iVector(n1);
> for (i = 0; i < n1; i++) iv[i] = i;
> for (i = 0; i < n1; i++) printf("%i\n", iv[i]);
> Free_iVector(iv);
> printf("\n");

> int *iv2;
> MakeIVector(iv2, n1);

You have not initialized iv2, but you are passing its value into
MakeIVector

> printf("\n");
> for (i = 0; i < n1; i++) iv2[i] = i;
> for (i = 0; i < n1; i++) printf("%i\n", iv2[i]);
>
> exit(0);
>}


Your fundamental problem is that you are expecting that if a routine
changes the value of a parameter that has been passed in, then the
change will also be made in the calling routine. That is never the case in C.
Any value passed in to a C routine is a *copy* of the value, and
changes to the copy never affect the original.

There are two basic solutions:

1) change MakeIVector so that it does not take iv as an input
but returns the *pointer* iv as an output; or

2) Instead of passing in the -value- of (the uninitialized) iv2,
pass in the -address- of that pointer (making the appropriate
adjustment to the type of the parameter), and write the pointer
through that address:

void MakeIVector(int **iv, int n1) {
int i;
*iv = iVector(n1);

for (i = 0; i < n1; i++) (*iv)[i] = i*2;
for (i = 0; i < n1; i++) printf("%i\n", (*iv)[i]);
return;
}

then later

int *iv2;
MakeIVector(&iv2, n1);

After which the value of iv2 will have been changed to be the pointer
to the allocated area.

(There are other improvements possible to the program, such as using
size_t instead of int for the sizes, in case the user wants to
allocate more than 32767 int's.)
--
"Whenever there is a hard job to be done I assign it to a lazy
man; he is sure to find an easy way of doing it."
-- Walter Chrysler

jt

7/10/2008 9:13:00 PM

0

CapCity <Cap@city.com> wrote:
> I'm sure I'm missing something simple - I do not code in C regularly, and
> the breaks are long enough for me to forget.

> The situation I have is I need to create an array but I do not know the
> dimension until runtime. I pass the pointer to a function which then
> determines the size and then creates and populates it. I can walk the array
> OK in the function, but the app crashes when I try to do so in the calling
> routine.

> Here's a small program that recreates the exception:

> #include <stdio.h>
> #include <stdlib.h>
> #include "ArrayManagement.h"
> #include "Test.h"

> void MakeIVector(int *iv, int n1) {

The function receives a copy of the value of the pointer 'iv2'.
Keep in mind that is a pass-by-value language (in contrast to
e.g. FORTRAN where changes to function arguments also change
the values of the variables used as arguments in the caller).

> int i;
> iv = iVector(n1);

This seems to assign some memory and sets the local variable
'iv' to that value, overriding the value it initially had (the
copy of whatever was stored in 'iv2' in main()).

> for (i = 0; i < n1; i++) iv[i] = i*2;
> for (i = 0; i < n1; i++) printf("%i\n", iv[i]);
> return;
> }

> int main() {
> int n1 = 8;
> int i, j, k;

> int* iv = iVector(n1);
> for (i = 0; i < n1; i++) iv[i] = i;
> for (i = 0; i < n1; i++) printf("%i\n", iv[i]);
> Free_iVector(iv);
> printf("\n");

> int *iv2;
> MakeIVector(iv2, n1);

Unfortunately, this doesn't do what you seem to expect. It
passes a copy of whatever address stored in 'iv2' to the
function. MakeIVector() actually never uses this value
(which is reasonable, since it's some random garbage be-
cause 'iv2' is uninitialized). And when MakeIVector() re-
turns nothing has changed about what 'iv2' contains, since
MakeIVector() operated on a local variable that vanished
the moment the function was left.

> printf("\n");
> for (i = 0; i < n1; i++) iv2[i] = i;

And now you use the still unitialized value of 'iv2' and
that's rather likely to crash your program.

> for (i = 0; i < n1; i++) printf("%i\n", iv2[i]);

> exit(0);
> }

There are two solutions to this problem:

a) Instead of passing the value of 'iv2' to MakeIValue()
pass it a pointer to 'iv2'. Then MakeIValue() is able
to change what it's pointing to. But then you have to
change MakeIForm to

void MakeIVector(int **iv, int n1) {
int i;

*iv = iVector(n1);
for (i = 0; i < n1; i++) (*iv)[i] = i*2;
for (i = 0; i < n1; i++) printf("%i\n", (*iv)[i]);
}

b) Just have MakeIVector return the value of the pointer it was
using. Then you have to change the code in main() to

int *iv2 = MakeIVector(n1);

and the necessary changes to MakeIVector() are:

int * MakeIVector(int n1) {
int i;
int *iv = iVector(n1);

for (i = 0; i < n1; i++) iv[i] = i*2;
for (i = 0; i < n1; i++) printf("%i\n", iv[i]);
return iv;
}
Regards, Jens
--
\ Jens Thoms Toerring ___ jt@toerring.de
\__________________________ http://t...

rahul

7/11/2008 9:05:00 AM

0

On Jul 11, 1:45 am, "CapCity" <C...@City.com> wrote:
> void MakeIVector(int *iv, int n1) {
>  int i;
>  iv = iVector(n1);
iv is passed by value. In this function, iv points to the memory
allocated by iVector(persuming it as you haven't posted the code) but
on return, the old value is popped from the stack.


>  int* iv = iVector(n1);
This thing works as it is capturing the return value.

>  int *iv2;
>  MakeIVector(iv2, n1);
This won't work. You will have to pass a pointer to the pointer.
int **iv2;
MakeIVector (iv2, nl);
And code for MakeIVector will have to be changed accordingly.

zhangfan

7/11/2008 9:42:00 AM

0

On Jul 11, 4:45 am, "CapCity" <C...@City.com> wrote:
> I'm sure I'm missing something simple - I do not code in C regularly, and
> the breaks are long enough for me to forget.
>
> The situation I have is I need to create an array but I do not know the
> dimension until runtime. I pass the pointer to a function which then
> determines the size and then creates and populates it. I can walk the array
> OK in the function, but the app crashes when I try to do so in the calling
> routine.
>
> Here's a small program that recreates the exception:
>
> #include <stdio.h>
> #include <stdlib.h>
> #include "ArrayManagement.h"
> #include "Test.h"
>
> void MakeIVector(int *iv, int n1) {
>  int i;
>  iv = iVector(n1);
This is wrong.
For simplicity, `iv' is a local scoped variable in function
MakeIVector, so it takes no effect outside MakeIVector to use `iv' as
a left value.

>  for (i = 0; i < n1; i++) iv[i] = i*2;


CapCity

7/11/2008 12:32:00 PM

0


"Walter Roberson" <roberson@ibd.nrc-cnrc.gc.ca> wrote in message
news:g55tgs$j63$1@canopus.cc.umanitoba.ca...
> In article <wxudk.77$8M.48@fe181.usenetserver.com>,
> CapCity <Cap@City.com> wrote:
>>#include <stdio.h>
>>#include <stdlib.h>
>>#include "ArrayManagement.h"
>>#include "Test.h"
>>
>>void MakeIVector(int *iv, int n1) {
>> int i;
>> iv = iVector(n1);
>
> iv is a pointer, so presumably iVector(n1) is allocating memory
> and returning the address of that memory, then to be stored into iv
>
>> for (i = 0; i < n1; i++) iv[i] = i*2;
>> for (i = 0; i < n1; i++) printf("%i\n", iv[i]);
>> return;
>
> An initial value for iv was passed in to MakeIVector but
> the new value (the address returned by iVector()) is not being
> passed out of the routine. Thus the calling routine has no idea
> where the allocated memory is.
>>}
>
>>int main() {
>> int n1 = 8;
>> int i, j, k;
>>
>> int* iv = iVector(n1);
>> for (i = 0; i < n1; i++) iv[i] = i;
>> for (i = 0; i < n1; i++) printf("%i\n", iv[i]);
>> Free_iVector(iv);
>> printf("\n");
>
>> int *iv2;
>> MakeIVector(iv2, n1);
>
> You have not initialized iv2, but you are passing its value into
> MakeIVector
>
>> printf("\n");
>> for (i = 0; i < n1; i++) iv2[i] = i;
>> for (i = 0; i < n1; i++) printf("%i\n", iv2[i]);
>>
>> exit(0);
>>}
>
>
> Your fundamental problem is that you are expecting that if a routine
> changes the value of a parameter that has been passed in, then the
> change will also be made in the calling routine. That is never the case in
> C.
> Any value passed in to a C routine is a *copy* of the value, and
> changes to the copy never affect the original.
>
> There are two basic solutions:
>
> 1) change MakeIVector so that it does not take iv as an input
> but returns the *pointer* iv as an output; or
>
> 2) Instead of passing in the -value- of (the uninitialized) iv2,
> pass in the -address- of that pointer (making the appropriate
> adjustment to the type of the parameter), and write the pointer
> through that address:
>
> void MakeIVector(int **iv, int n1) {
> int i;
> *iv = iVector(n1);
>
> for (i = 0; i < n1; i++) (*iv)[i] = i*2;
> for (i = 0; i < n1; i++) printf("%i\n", (*iv)[i]);
> return;
> }
>
> then later
>
> int *iv2;
> MakeIVector(&iv2, n1);
>
> After which the value of iv2 will have been changed to be the pointer
> to the allocated area.
>
> (There are other improvements possible to the program, such as using
> size_t instead of int for the sizes, in case the user wants to
> allocate more than 32767 int's.)

Thanks, Walter (and to the rest). That straightened things out for me.

I was aware (to a point) of the pass "by value" as opposed to "by
reference". To have a function change the value of an int or double you pass
in a pointer to the int or double.

But I needed to abstract that concept out a level for the one dimensional
array - I needed the function to change the value of a *pointer* so I needed
to pass in a pointer to the pointer. I was only providing the pointer
itself, thinking that was sufficient.

Thanks for getting me back on track.


> --
> "Whenever there is a hard job to be done I assign it to a lazy
> man; he is sure to find an easy way of doing it."
> -- Walter Chrysler