Some doubts on variable-length array

L

lovecreatesbeauty

Hello experts,

I have seen following the code snippet given by Marc Boyer (with slight
changes by me for a better format), and have doubts on it. I am so
grateful if you can give me your kindly help and hints on this problem.

1. Does the function call `foo(3, 3, tab);' refer to the data outside
the array `int tab[9];'. The available subscription for a 3X3 2-D array
should be 0..2 X 0..2, I think.

2. For the available function call `foo(2, 2, tab);' (I do not think
the second one is unavailable, just not sure.), which element in the
array `int tab[9];' does tab[0][0] refer to inside the body of the
function `foo'. I want to know the exact one int the format `tab[0] ..
tab [8]'.

And which element in `int tab[9];' does tab[1][1] refer to.

I can not figure it out for some while on it. The GCC4.1 says it does
not support the variable-length array of C99, but I can compile this
program on even GCC3.3.5, and also on GCC4.1 after I installed this
newest one. I also get the same result of the program as Marc Boyer.

Sincerely,

lovecreatesbeauty



/* sample by Marc Boyer */

#include <stdio.h>

void
foo(int size_x, int size_y, int tab[size_x][size_y])
{
printf("tab[1][1] == %d\n", tab[1][1]);
}

int
main()
{
int tab[9] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
foo(2, 2, tab);
foo(3, 3, tab);

return 0;
}

news> ./a.out
tab[1][1] == 3
tab[1][1] == 4
 
L

lovecreatesbeauty

I can understand the behaviour of these statements marked `/* A. don't
understand */' in following code, but do not understand the statements
marked `/* B. understand */' .

The behaviour of the following statements of A kind is different to B
kind.

Thank you.

#include <stdio.h>

void foo(int size_x, int size_y, int tab[size_x][size_y])
{
printf("tab[1][1] == %d\n", tab[1][1]);
}

int main()
{
/* int tab[9] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; */
int tab[3][3] = {0, 1, 2, 3, 4, 5, 6, 7, 8};

foo(2, 2, tab); /* A. don't understand */
printf("%i\n", tab[2][2]); /* B. understand */
printf("%i\n", *(*(tab+2)+2)); /* B. understand */
printf("\n\n");

foo(3, 3, tab); /* A. don't understand */
printf("%i\n", tab[3][3]); /* B. understand */
printf("%i\n", *(*(tab+3)+3)); /* B. understand */
printf("\n\n");

return 0;
}
 
F

Fred Kleinschmidt

lovecreatesbeauty said:
I can understand the behaviour of these statements marked `/* A. don't
understand */' in following code, but do not understand the statements
marked `/* B. understand */' .

The behaviour of the following statements of A kind is different to B
kind.

Thank you.

#include <stdio.h>

void foo(int size_x, int size_y, int tab[size_x][size_y])
Here foo is being told that tab is an array with dimensions
[size_x][size_y].
It will assume that this is actually the case, regardless of the true size
of the array passed to it in the calling function.
{
printf("tab[1][1] == %d\n", tab[1][1]);
}

if this were void foo( int **tab),
foo would not know the rank of the array
int main()
{
/* int tab[9] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; */
int tab[3][3] = {0, 1, 2, 3, 4, 5, 6, 7, 8};

foo(2, 2, tab); /* A. don't understand */
Call function foo, and tell foo that tab is a 2x2 array
printf("%i\n", tab[2][2]); /* B. understand */
printf("%i\n", *(*(tab+2)+2)); /* B. understand */
printf("\n\n");

foo(3, 3, tab); /* A. don't understand */
Call function foo, and tell foo that tab is a 3x3 array
printf("%i\n", tab[3][3]); /* B. understand */ Error - index out of bounds
printf("%i\n", *(*(tab+3)+3)); /* B. understand */ Error - index out of bounds
printf("\n\n");

return 0;
}
 
B

Ben Bacarisse

I can understand the behaviour of these statements marked `/* A. don't
understand */' in following code, but do not understand the statements
marked `/* B. understand */' .

Did you mean it that way round? I've commented on both just in case.
#include <stdio.h>

void foo(int size_x, int size_y, int tab[size_x][size_y])
{
printf("tab[1][1] == %d\n", tab[1][1]);

prints the 2nd element of the 2nd element of tab.
}

int main()
{
/* int tab[9] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; */
int tab[3][3] = {0, 1, 2, 3, 4, 5, 6, 7, 8};

My compiler (gcc 4.0.1) warns me I should write:

int tab[3][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}};

although it accepts the flat list as well.
foo(2, 2, tab); /* A. don't understand */

This calls "lies" to foo telling it that the array is 2x2 so foo sees it
as if it were "int tab[2][2] = {{0, 1}, {2, 3}};". Element [1][1] is 3
and this is what is printed.
printf("%i\n", tab[2][2]); /* B. understand */

The declaration of tab is in scope here so there should be no confusion.
The third element of the third element of tab is 8.
printf("%i\n", *(*(tab+2)+2)); /* B. understand */

This is another way to write the same thing. The array name tab is
treated as a pointer to its first element (an array of three ints).
Adding 2 to this pointer (tab + 2) moves it by two "strides" to make a
pointer that points to the third element (another array of three ints).
*(tab + 2) is this array, but again, the array is treated as a pointer to
its first element (an int). Adding 2 to that gives a pointer that points
to the number 8. The final * de-references that pointer to give the value
8.
printf("\n\n");

foo(3, 3, tab); /* A. don't understand */

This call does not "lie". So inside foo it is seen as it was defined and
element [1][1] is the number 4.
printf("%i\n", tab[3][3]); /* B. understand */
printf("%i\n", *(*(tab+3)+3)); /* B. understand */

These two are exactly as above, but the array is being index out of bounds
so you get undefined behaviour (probably a segmentation fault, or maybe
just garbage being printed).
 
L

lovecreatesbeauty

Ben said:
foo(2, 2, tab); /* A. don't understand */

This calls "lies" to foo telling it that the array is 2x2 so foo sees it
as if it were "int tab[2][2] = {{0, 1}, {2, 3}};". Element [1][1] is 3
and this is what is printed.

I know it before that functions regard an array argument same as a
pointer. So how can the layout/dimension of an actual array argument be
known inside the function body? Are new meanings/semantics given to
array arguments when they are variable-length array?
foo(3, 3, tab); /* A. don't understand */

This call does not "lie". So inside foo it is seen as it was defined and
element [1][1] is the number 4.

The original array is: /* int tab[9] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; */
, if it is used instead, then foo(3, 3, tab); lies again, right?

I can understand the original sample code in your way, but get more
anxious on how a function knows the layout/dimension of actual array
arguments.
 
B

Ben Bacarisse

Ben said:
foo(2, 2, tab); /* A. don't understand */

This calls "lies" to foo telling it that the array is 2x2 so foo sees it
as if it were "int tab[2][2] = {{0, 1}, {2, 3}};". Element [1][1] is 3
and this is what is printed.

I know it before that functions regard an array argument same as a
pointer. So how can the layout/dimension of an actual array argument be
known inside the function body? Are new meanings/semantics given to
array arguments when they are variable-length array?

Yes. The special syntax of these arguments lets the compiler know how big
each row of the parameter array is.
foo(3, 3, tab); /* A. don't understand */

This call does not "lie". So inside foo it is seen as it was defined and
element [1][1] is the number 4.

The original array is: /* int tab[9] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; */
, if it is used instead, then foo(3, 3, tab); lies again, right?

Yes and this is a bigger lie. But I put "lie" in quotes, because it may
be OK to do it. Telling a function the "shape" of the data array is fine
so long as you know what will happen when you do that.

If you want to treat a flat array as a collection of rows, then you are
free to do so but you will need to persuade the compiler with a cast in
the call to foo: foo(3, 3, (int (*)[3])tab) and you will not be able to
write tab[2][2] within scope of the flat definition. Nor will the mess
with all the pointer arithmetic work. In short, you need a very good
reason to go messing about like that. If you have 2D array, then just
declare it and use it. Pass at least the "row size" to any functions that
use the array and all will be fine. Passing both sizes makes sense for
many applications and helps to document the code.
 
L

lovecreatesbeauty

Ben said:
Ben said:
foo(2, 2, tab); /* A. don't understand */

This calls "lies" to foo telling it that the array is 2x2 so foo sees it
as if it were "int tab[2][2] = {{0, 1}, {2, 3}};". Element [1][1] is 3
and this is what is printed.

I know it before that functions regard an array argument same as a
pointer. So how can the layout/dimension of an actual array argument be
known inside the function body? Are new meanings/semantics given to
array arguments when they are variable-length array?

Yes. The special syntax of these arguments lets the compiler know how big
each row of the parameter array is.
foo(3, 3, tab); /* A. don't understand */

This call does not "lie". So inside foo it is seen as it was defined and
element [1][1] is the number 4.

The original array is: /* int tab[9] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; */
, if it is used instead, then foo(3, 3, tab); lies again, right?

Yes and this is a bigger lie. But I put "lie" in quotes, because it may
be OK to do it. Telling a function the "shape" of the data array is fine
so long as you know what will happen when you do that.

If you want to treat a flat array as a collection of rows, then you are
free to do so but you will need to persuade the compiler with a cast in
the call to foo: foo(3, 3, (int (*)[3])tab) and you will not be able to
write tab[2][2] within scope of the flat definition. Nor will the mess
with all the pointer arithmetic work. In short, you need a very good
reason to go messing about like that. If you have 2D array, then just
declare it and use it. Pass at least the "row size" to any functions that
use the array and all will be fine. Passing both sizes makes sense for
many applications and helps to document the code.



Thank you.

How is it going whan I add `foo(0, 0, tab)' and `foo(1, 1, tab)' in the
code snippet as following?

Can I think each function call refers to the array element as following
(I get inspired on it form another people goodluckyxl):

foo(0, 0, tab) refers to: tab[1+sizeof(int[0])/sizeof(int)] or
tab[1+0/sizeof(int)]

foo(0, 0, tab) refers to: tab[1+sizeof(int[1])/sizeof(int)]

foo(0, 0, tab) refers to: tab[1+sizeof(int[2])/sizeof(int)]

foo(0, 0, tab) refers to: tab[1+sizeof(int[3])/sizeof(int)]



#include <stdio.h>

void
foo(int size_x, int size_y, int tab[size_x][size_y])
{
printf("tab[1][1] == %d\n", tab[1][1]);
}

int
main()
{
int tab[9] = {0, 1, 2, 3, 4, 5, 6, 7, 8};

foo(0, 0, (int(*)[0])tab); /* foo(0, 0, tab); */
foo(1, 1, (int(*)[1])tab); /* foo(1, 1, tab); */

foo(2, 2, (int(*)[2])tab);
foo(3, 3, (int(*)[3])tab);

return 0;
}
 
L

lovecreatesbeauty

Ben said:
Ben said:
foo(2, 2, tab); /* A. don't understand */

This calls "lies" to foo telling it that the array is 2x2 so foo sees it
as if it were "int tab[2][2] = {{0, 1}, {2, 3}};". Element [1][1] is 3
and this is what is printed.

I know it before that functions regard an array argument same as a
pointer. So how can the layout/dimension of an actual array argument be
known inside the function body? Are new meanings/semantics given to
array arguments when they are variable-length array?

Yes. The special syntax of these arguments lets the compiler know how big
each row of the parameter array is.
foo(3, 3, tab); /* A. don't understand */

This call does not "lie". So inside foo it is seen as it was defined and
element [1][1] is the number 4.

The original array is: /* int tab[9] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; */
, if it is used instead, then foo(3, 3, tab); lies again, right?

Yes and this is a bigger lie. But I put "lie" in quotes, because it may
be OK to do it. Telling a function the "shape" of the data array is fine
so long as you know what will happen when you do that.

If you want to treat a flat array as a collection of rows, then you are
free to do so but you will need to persuade the compiler with a cast in
the call to foo: foo(3, 3, (int (*)[3])tab) and you will not be able to
write tab[2][2] within scope of the flat definition. Nor will the mess
with all the pointer arithmetic work. In short, you need a very good
reason to go messing about like that. If you have 2D array, then just
declare it and use it. Pass at least the "row size" to any functions that
use the array and all will be fine. Passing both sizes makes sense for
many applications and helps to document the code.



Thank you.

How is it going whan I add `foo(0, 0, tab)' and `foo(1, 1, tab)' in the
code snippet as following?

Can I think each function call refers to the array element as following
(I get inspired on it form another people goodluckyxl): (sorry to make
mistakes on following descriptions in my previous post)

foo(0, 0, tab) refers to: tab[1+sizeof(int[0])/sizeof(int)] or
tab[1+0/sizeof(int)]

foo(1, 1, tab) refers to: tab[1+sizeof(int[1])/sizeof(int)]

foo(2, 2, tab) refers to: tab[1+sizeof(int[2])/sizeof(int)]

foo(3, 3, tab) refers to: tab[1+sizeof(int[3])/sizeof(int)]



#include <stdio.h>

void
foo(int size_x, int size_y, int tab[size_x][size_y])
{
printf("tab[1][1] == %d\n", tab[1][1]);
}

int
main()
{
int tab[9] = {0, 1, 2, 3, 4, 5, 6, 7, 8};

foo(0, 0, (int(*)[0])tab); /* foo(0, 0, tab); */
foo(1, 1, (int(*)[1])tab); /* foo(1, 1, tab); */

foo(2, 2, (int(*)[2])tab);
foo(3, 3, (int(*)[3])tab);

return 0;
}
 
S

stathis gotsis

Ben Bacarisse said:
#include <stdio.h>

void foo(int size_x, int size_y, int tab[size_x][size_y])
{
printf("tab[1][1] == %d\n", tab[1][1]);

prints the 2nd element of the 2nd element of tab.
}

int main()
{
/* int tab[9] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; */
int tab[3][3] = {0, 1, 2, 3, 4, 5, 6, 7, 8};

My compiler (gcc 4.0.1) warns me I should write:

int tab[3][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}};

although it accepts the flat list as well.
foo(2, 2, tab); /* A. don't understand */

This calls "lies" to foo telling it that the array is 2x2 so foo sees it
as if it were "int tab[2][2] = {{0, 1}, {2, 3}};". Element [1][1] is 3
and this is what is printed.

This lie makes my compiler produce a warning. That is reasonable because the
types are incompatible. So that makes me wonder: is this guaranteed to work;
always produce the same result? I would think yes but i would like someone
to confirm it.
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,577
Members
45,054
Latest member
LucyCarper

Latest Threads

Top