pointer to an array -Is there an out of bound access?

K

Kavya

int main (){

int a[][3]={{1,2,3},{4,5,6}};
int (*ptr)[3]=a;

/* This should be fine and give 3 as output*/
printf("%d\n",(*ptr)[2]);

++ptr;

/* This should be fine and give 6 as output*/
printf("%d\n",(*ptr)[2]);

return 0;
}

But what if I do something like this

int main (){

int a[][3]={{1,2,3},{4,5,6}};
int (*ptr)[3]=a;

/* This should be fine and give 3 as output*/
printf("%d\n",(*ptr)[2]);

/* Will this be fine??
printf("%d\n",(*ptr)[5]);
return 0;
}

Is the second printf call in second code fine? or is it out of bound
access?
I tried both the codes with gcc 3.4.5 with -Wall and -pedantic. There
was no warning and got the correct output as 3 and 6 in both codes.
 
C

Cong Wang

int main (){

int a[][3]={{1,2,3},{4,5,6}};
int (*ptr)[3]=a;

/* This should be fine and give 3 as output*/
printf("%d\n",(*ptr)[2]);

++ptr;

/* This should be fine and give 6 as output*/
printf("%d\n",(*ptr)[2]);

return 0;

}But what if I do something like this

int main (){

int a[][3]={{1,2,3},{4,5,6}};
int (*ptr)[3]=a;

/* This should be fine and give 3 as output*/
printf("%d\n",(*ptr)[2]);

/* Will this be fine??
printf("%d\n",(*ptr)[5]);
return 0;

}Is the second printf call in second code fine? or is it out of bound
access?
I tried both the codes with gcc 3.4.5 with -Wall and -pedantic. There
was no warning and got the correct output as 3 and 6 in both codes.

It's out.
I also use gcc and got the following:

3
-1074239864

.. In fact, 'ptr' is NOT a common int pointer. See the following:

int *p = (int*)a;
printf("%d\n", p[5]);

That's OK, because 'p' is a common int pointer.
 
F

Frederick Gotham

Kavya:
int main (){

int a[][3]={{1,2,3},{4,5,6}};


Equivalent to:

int a[2][3] = {{1,2,3},{4,5,6}};

int (*ptr)[3]=a;


Equivalent to:

int (*ptr)[3] = &a[0];

/* This should be fine and give 3 as output*/
printf("%d\n",(*ptr)[2]);


Equivalent to:

printf("%d\n", a[0][2]);



Equivalent to:

ptr = &a[1];

/* This should be fine and give 6 as output*/
printf("%d\n",(*ptr)[2]);


Equivalent to:

printf("%d\n", a[1][2]);

return 0;
}


That works fine.

But what if I do something like this

int main (){

int a[][3]={{1,2,3},{4,5,6}};
int (*ptr)[3]=a;

/* This should be fine and give 3 as output*/
printf("%d\n",(*ptr)[2]);

/* Will this be fine??
printf("%d\n",(*ptr)[5]);


Basically you're asking if the Standard necessitates that multi-dimensional
arrays be lain out in a particular way in memory, specifically:

a[0][0] a[0][1] a[0][2] a[1][0] a[1][1] a[1][2]

I'm pretty sure that things have to be like this, but maybe someone can
give a quote from the Standard proving or disproving the necessity.
 
F

Frederick Gotham

Frederick Gotham:
Basically you're asking if the Standard necessitates that
multi-dimensional arrays be lain out in a particular way in memory,
specifically:

a[0][0] a[0][1] a[0][2] a[1][0] a[1][1] a[1][2]

I'm pretty sure that things have to be like this, but maybe someone can
give a quote from the Standard proving or disproving the necessity.


I actually, if I put a tad bit of thought into it, I don't need to consult
the Standard.

We know that:

(1) There cannot be padding between array elements or after.
(2) Each consecutive array element must have ascending addresses.

Also, there's no such thing as multi-dimensional arrays in C, but rather
arrays of arrays. Therefore, looking at the first array:

int (a[2])[3];

We know that the three elements in this array must not have padding between
them, and that their addresses must be ascending. Now, if we look at the
array type, we see that each element is an int[2]. We know that this inner
array must also have no padding between elements, and also that the
addresses must be ascending.

Therefore, the memory layout I showed above is necessitated by the
Standard.
 
R

Richard Heathfield

Frederick Gotham said:

Also, there's no such thing as multi-dimensional arrays in C

Utter bilge.


From C89's 3.3.2.1 Array subscripting:

Successive subscript operators designate a member of a multi-dimensional
array object.

From C99's 6.5.2.1 Array subscripting:

Successive subscript operators designate an element of a multidimensional
array object.


If the Standard says C has multi-dimensional arrays, C has multi-dimensional
arrays.
 
F

Frederick Gotham

Richard Heathfield:
Utter bilge.


Yes, you're right.

From C89's 3.3.2.1 Array subscripting:

Successive subscript operators designate a member of a multi-dimensional
array object.


Indeed they do.

From C99's 6.5.2.1 Array subscripting:

Successive subscript operators designate an element of a
multidimensional array object.


Again, you're right.

If the Standard says C has multi-dimensional arrays, C has
multi-dimensional arrays.


Yes you're right. But how exactly do these multi-dimensional arrays work?

Do you think it's right to say that, other than the proton and the
electron, that there's a third kind of particle within the atom? Some would
say yes, the neutron. Others would say no, because a neutron is little more
than an electron and a proton bound together.

So, I say that a multi-dimensional array is merely an array of arrays. And
guess what, that way of thinking works well for me.

If the Standard, or you for that matter, would like to say that there are
multi-dimensional arrays in C, then go ahead, you're right; but as far as
the grammar of the C programming language goes, they work like an array of
arrays.
 
B

Barry Schwarz

Frederick Gotham:
Basically you're asking if the Standard necessitates that
multi-dimensional arrays be lain out in a particular way in memory,
specifically:

a[0][0] a[0][1] a[0][2] a[1][0] a[1][1] a[1][2]

I'm pretty sure that things have to be like this, but maybe someone can
give a quote from the Standard proving or disproving the necessity.


I actually, if I put a tad bit of thought into it, I don't need to consult
the Standard.

We know that:

(1) There cannot be padding between array elements or after.
(2) Each consecutive array element must have ascending addresses.

Also, there's no such thing as multi-dimensional arrays in C, but rather
arrays of arrays. Therefore, looking at the first array:

int (a[2])[3];

We know that the three elements in this array must not have padding between
them, and that their addresses must be ascending. Now, if we look at the
array type, we see that each element is an int[2]. We know that this inner
array must also have no padding between elements, and also that the
addresses must be ascending.

I agree with your intent but I think you would have been closer to the
point you were trying to make to say the first array has two elements,
each of which is an int[3] and then proceed to discuss the absence of
padding between a[0] and a[1] followed by the combined effects of
aligning a and a[0] and the absence of padding between a[j]
and a[j+1].
Therefore, the memory layout I showed above is necessitated by the
Standard.

But if the compiler performs range checking on constant subscripts or
inserts range checking into the executable code, it would compliant to
issue a diagnostic and reject the translation unit or terminate the
execution of the program for a serious run time error, respectively.


Remove del for email
 
K

Keith Thompson

Richard Heathfield said:
From C89's 3.3.2.1 Array subscripting:

Successive subscript operators designate a member of a multi-dimensional
array object.

From C99's 6.5.2.1 Array subscripting:

Successive subscript operators designate an element of a multidimensional
array object.


If the Standard says C has multi-dimensional arrays, C has
multi-dimensional arrays.

Yes, but it's really just a matter of terminology. If the C standard
didn't mention multidimensional arrays, or even if it explicitly
stated "The C language does not have multidimensional arrays, but it
does have arrays of arrays", then it would still describe exactly the
same language.

You can declare an array of arrays. The question of whether you can
call that a multidimensional array has a clear answer (yes, because
the standard says so), but it's not a very important question.

Note that some languages do support multidimensional arrays as a
distinct concept; arr1[x][y] indexes into an array of arrays, and
arr2[x,y] indexes into a two-dimensional array. C, of course, doesn't
do this (and the existence of the comma operator makes arr2[x,y] a
trap for the unwary).

Digression follows.

Similarly, we say that C *doesn't* have pass-by-reference because the
standard doesn't use that term. But if the standard referred to
passing a pointer-to-foo as passing the foo by reference, then we'd
have to say that C *does* support pass-by-reference. In fact, C does
support pass-by-reference *as a programming technique*, but not as a
language construct.
 
R

Richard Heathfield

Keith Thompson said:

Digression follows.

Similarly, we say that C *doesn't* have pass-by-reference because the
standard doesn't use that term. But if the standard referred to
passing a pointer-to-foo as passing the foo by reference, then we'd
have to say that C *does* support pass-by-reference. In fact, C does
support pass-by-reference *as a programming technique*, but not as a
language construct.

As you probably recall, I think it is easier to explain parameter-passing to
a newbie if one sticks strictly to the "pass-by-value" terminology, even
when pointers are being passed. There is no need to make an exception for
pointers, and to do so leads to confusion, as we occasionally see in
questions on this newsgroup.
 
K

Keith Thompson

Richard Heathfield said:
Keith Thompson said:


As you probably recall, I think it is easier to explain parameter-passing to
a newbie if one sticks strictly to the "pass-by-value" terminology, even
when pointers are being passed. There is no need to make an exception for
pointers, and to do so leads to confusion, as we occasionally see in
questions on this newsgroup.

I think it depends on the newbie. For someone who's learning to
program for the first time, it probably makes sense to stick to the
idea of "pass-by-value", and later introduce the idea that passing
pointers by value can be a useful technique if you want to modify the
pointed-to object.

On the other hand, someone who's new to C, but has experience in some
other language that *does* directly support pass-by-reference, will
likely find it useful to know that passing a pointer is the way to
implement the equivalent of pass-by-reference in C. (When I first
learned C, most of my programming experience was in Pascal.)

I might also argue that pass-by-reference is a useful concept in
computer science in general, independent of any particular language,
and it's useful to see how that concept is mapped onto the constructs
of a particular language.

C doesn't "have" linked lists, binary trees, or pass-by-reference.
You can implement them all using pointers. If I present a chunk of C
code that implements a linked list and say "This is a linked list",
nobody is going to jump up and say "No, C doesn't have linked lists;
that's just a structure with a pointer in it and some functions that
operate on it".

But that's not a perfect analogy, and I can certainly see your point.
 
F

Frederick Gotham

Barry Schwarz:
But if the compiler performs range checking on constant subscripts or
inserts range checking into the executable code, it would compliant to
issue a diagnostic and reject the translation unit or terminate the
execution of the program for a serious run time error, respectively.


Wha. . . ? Sounds a bit drastic. Are you sure the compiler is entitled to
do that even when you definitely should be able to do what you're trying to
do? Consider the following code for instance; it's not exactly politically
correct, but there shouldn't be a problem with it.

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>

int main(void)
{
assert( sizeof(double) > sizeof(int) );

{ /* Start */

double *p;
int *q;
char unsigned const *pover;
char unsigned const *ptr;

p = malloc(5 * sizeof*p);
q = (int*)p++;
pover = (char unsigned*)(p+4);
ptr = (char unsigned*)p;
p[3] = 2423.234;
*q++ = -9;

do printf("%d",(int)*ptr++);
while (pover != ptr);

return 0;

} /* End */
}


The code might look a bit weird, but that shouldn't entitle a compiler to
do whatever it wants with it.
 
O

Old Wolf

Frederick said:
Do you think it's right to say that, other than the proton and the
electron, that there's a third kind of particle within the atom? Some would
say yes, the neutron. Others would say no, because a neutron is little more
than an electron and a proton bound together.

A neutron is nothing at all like an electron and a proton bound
together (whatever that means). What have you been smoking?
 
R

Richard Heathfield

Frederick Gotham said:
Barry Schwarz:



Wha. . . ? Sounds a bit drastic. Are you sure the compiler is entitled to
do that even when you definitely should be able to do what you're trying
to do?

Yes, the compiler is entitled to do that, and exceeding bounds is not
something you should definitely be able to do.

Consider the following code for instance; it's not exactly
politically correct, but there shouldn't be a problem with it.

If you want help with your code, don't obfuscate it, and don't hurl insults
at the very people who can help you.
 
P

pete

Kavya wrote:
int main (){

int a[][3]={{1,2,3},{4,5,6}};
int (*ptr)[3]=a;

/* This should be fine and give 3 as output*/
printf("%d\n",(*ptr)[2]);

/* Will this be fine??
printf("%d\n",(*ptr)[5]);
return 0;
}

Is the second printf call in second code fine?
or is it out of bound access?

It's an out of bound access.
I tried both the codes with gcc 3.4.5 with -Wall and -pedantic. There
was no warning and got the correct output as 3 and 6 in both codes.

That's the point of programmers learning the language better.
 
J

james of tucson

Kavya said:
I tried both the codes with gcc 3.4.5 with -Wall and -pedantic. There
was no warning and got the correct output as 3 and 6 in both codes.

Sometimes you can get lucky with undefined behavior. Nothing to see
here, folks. Move along.
 
A

Andrey Tarasevich

Frederick said:
...
I actually, if I put a tad bit of thought into it, I don't need to consult
the Standard.

We know that:

(1) There cannot be padding between array elements or after.
(2) Each consecutive array element must have ascending addresses.

Also, there's no such thing as multi-dimensional arrays in C, but rather
arrays of arrays. Therefore, looking at the first array:

int (a[2])[3];

We know that the three elements in this array must not have padding between
them, and that their addresses must be ascending. Now, if we look at the
array type, we see that each element is an int[2]. We know that this inner
array must also have no padding between elements, and also that the
addresses must be ascending.

Therefore, the memory layout I showed above is necessitated by the
Standard.
...

The memory layout is indeed necessitated by the standard, but the issue here is
not limited to the memory layout. The formal issue here is in fact the same as
with the recently-discussed "struct hack". It, once again, depends on the
intended meaning of the pointer arithmetic rules. When applied to indexing an
array object, were they supposed to refer to the declared size of the array
object or the actual size of the underlying memory block? The standard itself
does not answer that question explicitly (neither C89/90 nor C99). (Several
people here insisted that it does, but no one was able to produce a convincing
quote.) However, it is informally known that the intent was to limit the pointer
arithmetic for such cases with the _declared_ size of the array object.

Applied to the OP's example, this means that accessing, say, '(*ptr)[5]' when
'ptr' is declared as 'int (*ptr)[3]' is illegal. Attempts to tie this formal
requirement to practice usually insist that implementation is allowed to
represent the '*ptr' value is a way that only allows access to elements in 0-2
range (exactly the same reasoning as with the "struct hack"). Once again, this
is based on the aforementioned "informally canonical" interpretation of the
address arithmetic rules.

As an interesting side note, IIRC, Stroustrup's C++ book includes (-ed?) an
example where a multi-dimensional array is actually accessed in that "illegal"
fashion, even though C++'s address arithmetic rules are no different from C's.
 
F

Frederick Gotham

Andrey Tarasevich:
Applied to the OP's example, this means that accessing, say, '(*ptr)[5]'
when 'ptr' is declared as 'int (*ptr)[3]' is illegal. Attempts to tie
this formal requirement to practice usually insist that implementation
is allowed to represent the '*ptr' value is a way that only allows
access to elements in 0-2 range (exactly the same reasoning as with the
"struct hack"). Once again, this is based on the aforementioned
"informally canonical" interpretation of the address arithmetic rules.


What about the following, is it OK?

int arr[2][2];

int *p = *arr;

p[3] = 8;

If it is OK, then the following should be OK aswell:

arr[0][3] = 8;

, because the compiler must treat it as:

*( *(arr+0) + 3)

arr decays to a pointer to its first element, an R-value of type int(*)[2]
It then has zero added to it, which has no effect.
It is then dereferenced, yielding an L-value of type int[2]
It then decays to a pointer to its first element, an R-value of type int*
It then has 3 added to it
It is then dereference, yielding an L-value of type int.

Even when we use "arr" directly, I don't see the problem.
 
R

Richard Heathfield

Frederick Gotham said:
What about the following, is it OK?

int arr[2][2];

int *p = *arr;

p[3] = 8;

No, for reasons that have already been explained ad nauseam.
 
F

Frederick Gotham

Richard Heathfield:
Frederick Gotham said:
What about the following, is it OK?

int arr[2][2];

int *p = *arr;

p[3] = 8;

No, for reasons that have already been explained ad nauseam.


Then how can we treat any chunk of memory as if it's an array of unsigned
char?
 

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,743
Messages
2,569,478
Members
44,898
Latest member
BlairH7607

Latest Threads

Top