Pointer to a 2 dimensional array?

L

Louise Hoffman

Dear readers,

Full source code with data file at (50k):
http://www.sendspace.com/file/32ghfp

My C book says that an array is the same as a pointer to the array.

So I would really like to have fit_data.c to return a pointer to a 2
dimensional array, data_space.

How would you put char's into such an array?

That is what I am trying to do in the last for-loop in fit_data.c, but
doesn't seam to be valid.

Lots of love,
Louise =)

--- tit_data.c ---

char fit_data(double start_x, double slut_x,
double start_y, double slut_y,
double *x, double *y,
int n) {

char *data_space;

int size = X_BINS * Y_BINS * sizeof(char);
data_space = malloc(size);
if (data_space == 0) {
printf("Out of memory\n");
exit(EXIT_FAILURE);
}

/* Fill array with white space */
int i=0;
for(i=0; i<size; i++) {
*(data_space + i) = ' ';
}

/* check if a data point should be inserted, and in such case where
*/
i=0;
int ybin, xbin;
for (i=0; i<n; i++) {
if (x >= start_x && x <= slut_x &&
y >= start_y && y <= slut_y) {

/* where to put x */
xbin = (int) round( (x - start_x)/(slut_x - start_x)
*X_BINS );
/* where to put y */
ybin = (int) round( (y - start_y)/(slut_y - start_y)
*Y_BINS );

data_space[xbin][ybin] = '*';
}
}

return data_space;
}

--- tit_data.c ---
 
E

Eric Sosman

Louise said:
[...]
My C book says that an array is the same as a pointer to the array.

This means that the author of your C book has not
acquainted himself or herself with Question 6.9 of the
comp.lang.c Frequently Asked Questions (FAQ) list at
<http://www.c-faq.com/>. It wouldn't surprise me to
learn that the rest of Section 6 had also escaped his
or her notice, which is too bad because a good deal of
that section contains information that would have been
helpful for your problem, had the C book's author included
any of it. Alas! for an opportunity lost, a life wasted!
 
B

Ben Bacarisse

Louise Hoffman said:
Full source code with data file at (50k):
http://www.sendspace.com/file/32ghfp

My C book says that an array is the same as a pointer to the array.

It is leading you astray, then! An expression of array type is
converted to a pointer to the first element in all but three (very
important) cases[1]. This means that you can end up thinking that an array
is a pointer, but you will get bitten by this if don't keep reminding
your self that an array is an array and a pointer is something else
entirely even though there is a handy conversion from one to the
other in most cases.

See http://c-faq.com/aryptr/aryptrequiv.html
So I would really like to have fit_data.c to return a pointer to a 2
dimensional array, data_space.

Have a look at http://c-faq.com/aryptr/dynmuldimary.html

Rather than pick over your code now, read that FAQ, and come back if you
have questions. You need to decide what sort of 2D array you want first.

[1] the exceptions are when the array is an operand of sizeof, when it is
the operand of & and when it is a string literal used to initialise an
array. E.g. given

char c[1000] = "hello";

it is unlikely that sizeof c == sizeof (char *). Also, the type of &c
is the rather odd-looking type char (*)[1000] and not char ** as it
would be if c were converted to a pointer to char as it normally is.
Lastly, the array denoted by the string "hello" is not converted to a
pointer to char, but is simply used to initialise the array.
 
K

Keith Thompson

Louise Hoffman said:
Dear readers,

Full source code with data file at (50k):
http://www.sendspace.com/file/32ghfp

My C book says that an array is the same as a pointer to the array.
[...]

Which C book is that?

If it says that, it's wrong, but you should also consider the
possibility that you've misunderstood what it says. Can you quote the
relevant passage from the book?

As others have suggested, section 6 of the comp.lang.c FAQ,
<http://www.c-faq.com>, does an excellent job of explaining this
stuff. The relationship between arrays and pointers in C can be
confusing, but it's not that bad once you understand the underlying
concepts.
 
T

Tomás Ó hÉilidhe

Firstly it might help to think of multi-dimensional arrays as "an
array of arrays", because that is exactly how the language treats
them.

So if you have an object as follows:

int array[5][3];

Then it's the same as:

typedef int IntArrayThree[3];

IntArrayThree array[5];

Secondly, as Ben Bacarisse has already mentioned, there are three
places in which an array stays as an array. In other places, the array
undergoes an implicit conversion to a pointer to its first element.

In the case of the array, "array", it's an array containing 5 elements
and each element is an "IntArrThree". Therefore, if you do something
like:

CallSomeFunction(array);

Then you're passing as an argument to that function a pointer to the
first element of "array". Since an element of "array" is of type
IntArrayThree, that means a pointer to the first element will be of
the type IntArrayThree*. If we take away the decorative typedef, the
type is:

int (*)[3]

If you were to dereference this pointer, such as:

CallSomeFunction(*array);

Then the argument would be of type:

int[3]

but of course, because it's an array, it will undergo an implicit
conversion to a pointer to its first element:

int*

That's pretty much all there is to it: Multi-dimensional arrays are
arrays of arrays, and arrays undergo an implicit conversion to a
pointer to their first element (in all but three cases).
 
T

Tomás Ó hÉilidhe

One more thing, I forgot to mention about taking the address of an
array. Using the address-of operator on an array (i.e. the & operator)
is one of the three cases in which an array stays as an array. So if
you have an object as follows:

int arr[5];

then the following expression:

&arr

is of type:

int (*)[5]

Another example: If you had:

int arr[5][6][7];

then the following expression:

&arr

would be of the type:

int (*)[5][6][7]

A pointer to an array gets the exact same treatment as any old
pointer, it won't undergo any funky implicit conversions. If you want
funky implicit conversions, then dereference it to yield an array
object. For instance:

*arr

would be of the type:

int [5][6][7]

But if you used this as an argument to a function, it would implicitly
convert to:

int (*)[6][7]
 
T

Tomás Ó hÉilidhe

A pointer to an array gets the exact same treatment as any old
pointer, it won't undergo any funky implicit conversions. If you want
funky implicit conversions, then dereference it to yield an array
object. For instance:

*arr


That's incorrect, I should have written:

*&arr

arr is an array
&arr is a pointer to an array
When you dereference the pointer, you get the array back
 
L

Louise Hoffman

Which C book is that?

My book is
K. N. King, C Programming - A modern approach, 2nd ed
If it says that, it's wrong, but you should also consider the
possibility that you've misunderstood what it says.  Can you quote the
relevant passage from the book?

With all the arguments you all have come with, doubt I have qouted the
book right =)

I was pretty sure it was in chapter 17, but I can't find it...
As others have suggested, section 6 of the comp.lang.c FAQ,
<http://www.c-faq.com>, does an excellent job of explaining this
stuff.  The relationship between arrays and pointers in C can be
confusing, but it's not that bad once you understand the underlying
concepts.

Yes, I didn't knew it, and I think I now have resolved the original
problem.

But I get a "segmentation fault". I am pretty sure it is either in
fit_data.c or in main.c from 50.

Can someone figure out what I am doing wrong?

Full source code with data file (~50kB) at:
http://www.sendspace.com/file/5ikqp7

With love,
Louise =)
 
K

Keith Thompson

I wrote the above. Please leave attribution lines in place (lines
knowing who said what makes it much easier to follow the discussion.

[...]
But I get a "segmentation fault". I am pretty sure it is either in
fit_data.c or in main.c from 50.

Can someone figure out what I am doing wrong?

Full source code with data file (~50kB) at:
http://www.sendspace.com/file/5ikqp7

Try narrowing it down to a minimal program that exhibits the problem.
It's likely that you'll solve the problem in the course of doing this;
if not, you'll at least end up with something small enough for people
to look at.
 
B

Ben Bacarisse

Can someone figure out what I am doing wrong?

Just typing make showed four warnings -- all of them serious. The
program can't work until you fix them. Don't be fooled by gcc's kind
tone "warning: passing argument 5 of ‘fit_data’ from incompatible
pointer type" can (and in the case, does) mean "serious error:
entirely unsuitable data is being passed to fit_data". Warnings may
be benign, but you need to know which are and which are not and when.

The third, "main.c:65: warning: assignment makes pointer from integer
without a cast", points to something that neeeds re-design. Let me
sketch the function:

char *fit_data(<lost of args>)
{
char *p, data_space[Y_BINS][X_BINS];

/* Fill array with white space */
for (p = &data_space[0][0]; p<=&data_space[Y_BINS-1][X_BINS-1]; p++) {
*p = ' ';
}


/***************************************************
* *
* remember to free p in main.c *
* *
***************************************************/
return p;
}

This doomed! You say "remember to free" but there is nothing
malloced. Even if you did, p points to the end of the space after the
initial loop. The array space returned by this function must either
be of static storage duration or allocated with malloc -- local
variables (AKA automatic storage) like data_space disappear when the
function returns.

BTW, know your library. That initial loop can be written:

memset(data_space, ' ', sizeof data_space);
 
L

Louise Hoffman

Just typing make showed four warnings -- all of them serious.  The
program can't work until you fix them.  Don't be fooled by gcc's kind
tone "warning: passing argument 5 of ‘fit_data’ from incompatible
pointer type" can (and in the case, does) mean "serious error:
entirely unsuitable data is being passed to fit_data".  Warnings may
be benign, but you need to know which are and which are not and when.

Glad you say so, because after I have redesigned the fit_data.c as you
point out in the next paragraf, I still get the error:

main.c:66: warning: assignment makes pointer from integer without a
cast

and that is the last line of:

p = *fit_data(start_x, slut_x, start_y, slut_y,
raw_s.x, raw_s.y, raw_s.n);

Isn't my raw_s.x and raw_y.y still pointers with type double? I assume
the problem is that it doesn't know that they are pointers to doubles?
The third, "main.c:65: warning: assignment makes pointer from integer
without a cast", points to something that neeeds re-design.  Let me
sketch the function: [snip]
This doomed!  You say "remember to free" but there is nothing
malloced.  Even if you did, p points to the end of the space after the
initial loop.  The array space returned by this function must either
be of static storage duration or allocated with malloc -- local
variables (AKA automatic storage) like data_space disappear when the
function returns.

I have now redesigned it to use malloc and got rid of the 2
dimensional array, as I assume I can't use 2-dimensional arrays to
access data in malloc'ed memory?

But I still this this
main.c:66: warning: assignment makes pointer from integer without a
cast

Can you tell where I should redesign now?
BTW, know your library.  That initial loop can be written:

  memset(data_space, ' ', sizeof data_space);

Cool trick! Thanks =)

Full source code her:
http://www.sendspace.com/file/g7ps41

Hugs,
Louise =)
 
L

Louise Hoffman

Reply to own post. I still have the same problems, but I just fixed
that one function had its arguments typed in in the wrong order.
 
J

jameskuyper

Louise Hoffman wrote:
....
I have now redesigned it to use malloc and got rid of the 2
dimensional array, as I assume I can't use 2-dimensional arrays to
access data in malloc'ed memory?

No, it's quite feasible to do something like the following:

#include <stdlib.h>
#define ROWS 3
#define COLS 5

int main(void)
{
int (*array)[COLS] = malloc(ROWS * sizeof *array);

if(array)
{
// Use array

free(array);
}
return 0;
}

Note: ROWS could be a variable, rather than a macro, if you need to be
able to change the number of rows in your array. In C99, COLS can be a
variable, too.
 
B

Ben Bacarisse

Louise Hoffman said:
Glad you say so, because after I have redesigned the fit_data.c as you
point out in the next paragraf, I still get the error:

main.c:66: warning: assignment makes pointer from integer without a
cast

and that is the last line of:

p = *fit_data(start_x, slut_x, start_y, slut_y,
raw_s.x, raw_s.y, raw_s.n);

The problem is the *. When I last saw the source, fit_data returns
char * so you just want p = fit_data(...);. The * turns the pointer
into the char it points to. The warning is that you can't assign an
integer type (char) to a pointer object with out a cast. Of course,
you don't want to ass a cast (you almost never do, BTW) but you do
need to change the code.
 
L

Louise Hoffman

No, it's quite feasible to do something like the following: [snip]
Note: ROWS could be a variable, rather than a macro, if you need to be
able to change the number of rows in your array. In C99, COLS can be a
variable, too.

That is cool! I haven't seen "(*array)[COLS]" before, so I will have
to check what it means =)
 
L

Louise Hoffman

The problem is the *.  When I last saw the source, fit_data returns
char * so you just want p = fit_data(...);.  The * turns the pointer
into the char it points to.  The warning is that you can't assign an
integer type (char) to a pointer object with out a cast.  Of course,
you don't want to ass a cast (you almost never do, BTW) but you do
need to change the code.

WOW! That was it! =) =) =)

It now works =)
http://www.sendspace.com/file/i0dzb2

The plot it wrong though, but I guess I most have made a math error
somewhere.

What my program is suppose to do is to:
* take the 4200 data points from the ovn.sim file
* throw some of the point away based on axis limits (start_x, slut_x,
start_y, slut_y)
* scale the rest of the points down so they fit in a 10x20 coordinate
system

The output should be a curve, but I don't quite get that =)

Have you experience on this field as well, where the problem could be?
 
A

Amandil

No, it's quite feasible to do something like the following:

#include <stdlib.h>
#define ROWS 3
#define COLS 5

int main(void)
{
    int (*array)[COLS] = malloc(ROWS * sizeof *array);

    if(array)
    {
        // Use array

        free(array);
    }
    return 0;

}

Note: ROWS could be a variable, rather than a macro, if you need to be
able to change the number of rows in your array. In C99, COLS can be a
variable, too.

As Louise Hoffman writes elsethread, "That's cool". I have wondered,
and considered asking, how (on DOS systems) to write to screen memory
directly, treating it as a 2 dimensional array of type screen char.
This method would also make it easier for me to implement a curses-
like library.

If I understand you correctly, I could use,
struct screen_char {
char c, attr;
} ;
struct screen_char (*screen)[80] = get_screen_base();
and each spot on the screen is accessible as
screen[13][42].c = 'A';
screen[13][42].attr = REG;

Previously I had thought to do it by hiding the screen addressing in a
function, for example
void main_program(struct screen_char[25][80]);
void *screen_base = get_screen_base;
main_program(screen_base);
which I found not optimal because I was lying to the compiler.

Remember that while this exact use my be platform specific, the ideas
herein are definitely not. (Which is why I hide the screen_base value
in a function.)

Thanks a bunch.

Marty Amandil (almost always prepared to learn something new)
 
J

jameskuyper

Amandil said:
No, it's quite feasible to do something like the following:

#include <stdlib.h>
#define ROWS 3
#define COLS 5

int main(void)
{
� � int (*array)[COLS] = malloc(ROWS * sizeof *array);

� � if(array)
� � {
� � � � // Use array

� � � � free(array);
� � }
� � return 0;

}

Note: ROWS could be a variable, rather than a macro, if you need to be
able to change the number of rows in your array. In C99, COLS can be a
variable, too.

As Louise Hoffman writes elsethread, "That's cool". I have wondered,
and considered asking, how (on DOS systems) to write to screen memory
directly, treating it as a 2 dimensional array of type screen char.
This method would also make it easier for me to implement a curses-
like library.

If I understand you correctly, I could use,
struct screen_char {
char c, attr;
} ;
struct screen_char (*screen)[80] = get_screen_base();
and each spot on the screen is accessible as
screen[13][42].c = 'A';
screen[13][42].attr = REG;

You're relying upon there being no padding between c and attr, nor any
additional padding at the end of the struct. The first assumption is a
very good one, though not guaranteed by the standard. The second
assumption is not so good: many systems pad struct types to a size
which is a multiple of some important number like 8 or 16 bytes.

You can get around that problem by using a 2-element array, rather
than a structure type:
struct screen_char (*screen)[80][2] = get_screen_base();

screen[13][42][0] = 'A';
screen[13][42][1] = REG;

If '0' and '1' don't feel sufficiently descriptive, you can define
macros with meaningful names whose values are 0 and 1, respectively.
Previously I had thought to do it by hiding the screen addressing in a
function, for example
void main_program(struct screen_char[25][80]);
void *screen_base = get_screen_base;
main_program(screen_base);
which I found not optimal because I was lying to the compiler.

However, if you managed to get code like that working, the compiler
you're using apparantly doesn't insert any padding. Still, I'd
recommend improving the portability of your code by not relying on
that fact.
 

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,772
Messages
2,569,593
Members
45,108
Latest member
AlbertEste
Top