Passing Dynaically-Sized Arrays

C

CapCity

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*2;
for (i = 0; i < n1; i++) printf("%i\n", iv);
return;
}

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

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

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

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!
 
W

Walter Roberson

#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*2;
for (i = 0; i < n1; i++) printf("%i\n", iv);
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;
for (i = 0; i < n1; i++) printf("%i\n", iv);
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;
for (i = 0; i < n1; i++) printf("%i\n", iv2);

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*2;
for (i = 0; i < n1; i++) printf("%i\n", (*iv));
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.)
 
J

Jens Thoms Toerring

CapCity said:
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*2;
for (i = 0; i < n1; i++) printf("%i\n", iv);
return;
}

int main() {
int n1 = 8;
int i, j, k;
int* iv = iVector(n1);
for (i = 0; i < n1; i++) iv = i;
for (i = 0; i < n1; i++) printf("%i\n", iv);
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;


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);

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*2;
for (i = 0; i < n1; i++) printf("%i\n", (*iv));
}

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*2;
for (i = 0; i < n1; i++) printf("%i\n", iv);
return iv;
}
Regards, Jens
 
R

rahul

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.
 
Z

zhangfan

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*2;
 
C

CapCity

Walter Roberson said:
#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*2;
for (i = 0; i < n1; i++) printf("%i\n", iv);
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;
for (i = 0; i < n1; i++) printf("%i\n", iv);
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;
for (i = 0; i < n1; i++) printf("%i\n", iv2);

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*2;
for (i = 0; i < n1; i++) printf("%i\n", (*iv));
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.

 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,011
Latest member
AjaUqq1950

Latest Threads

Top