Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accesing a 2D array using a single pointer

Tags:

arrays

c

pointers

There are tons of code like this one:

#include <stdio.h>

int main(void)
{
    int a[2][2] = {{0, 1}, {2, -1}};
    int *p = &a[0][0];

    while (*p != -1) {
        printf("%d\n", *p);
        p++;
    }
    return 0;
}

But based on this answer, the behavior is undefined.

N1570. 6.5.6 p8:

When an expression that has integer type is added to or subtracted from a pointer, the result has the type of the pointer operand. If the pointer operand points to an element of an array object, and the array is large enough, the result points to an element offset from the original element such that the difference of the subscripts of the resulting and original array elements equals the integer expression. In other words, if the expression P points to the i-th element of an array object, the expressions (P)+N (equivalently, N+(P)) and (P)-N (where N has the value n) point to, respectively, the i+n-th and i−n-th elements of the array object, provided they exist. Moreover, if the expression P points to the last element of an array object, the expression (P)+1 points one past the last element of the array object, and if the expression Q points one past the last element of an array object, the expression (Q)-1 points to the last element of the array object. If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined. If the result points one past the last element of the array object, it shall not be used as the operand of a unary * operator that is evaluated.

Can someone explain this in detail?

like image 887
David Ranieri Avatar asked Aug 14 '14 08:08

David Ranieri


People also ask

How do you access a two dimensional array using pointers?

Pointers and two dimensional Arrays: In a two dimensional array, we can access each element by using two subscripts, where first subscript represents the row number and second subscript represents the column number. The elements of 2-D array can be accessed with the help of pointer notation also.

How are the elements accessed in 2D arrays in C?

Accessing Elements of Two-Dimensional Arrays: Elements in Two-Dimensional arrays are accessed using the row indexes and column indexes. Example: int x[2][1]; The above example represents the element present in the third row and second column.

How are single and two dimensional arrays represented using pointers explain?

Using pointer to access the element of two- dimensional array, the key is to clear defined pointer points to the element or points to the one-dimensional array . The pointer which points to element is assigned a element address,the pointer which points to the one- dimensional array is assigned a row address.


Video Answer


3 Answers

The array who's base address (pointer to first element) p is assigned is of type int[2]. This means the address in p can legally be dereferenced only at locations *p and *(p+1), or if you prefer subscript notation, p[0] and p[1]. Furthermore, p+2 is guaranteed to be a legally evaluated as an address, and comparable to other addresses in that sequence, but can not be dereferenced. This is the one-past address.

The code you posted violates the one-past rule by dereferencing p once it passes the last element in the array in which it is homed. That the array in which it is homed is buttressed up against another array of similar dimension is not relevant to the formal definition cited.

That said, in practice it works, but as is often said. observed behavior is not, and should never be considered, defined behavior. Just because it works doesn't make it right.

like image 197
WhozCraig Avatar answered Oct 19 '22 17:10

WhozCraig


The object representation of pointers is opaque, in C. There is no prohibition against pointers having bounds information encoded. That's one possibility to keep in mind.

More practically, implementations are also able to achieve certain optimizations based on assumptions which are asserted by rules like these: Aliasing.

Then there's the protection of programmers from accidents.

Consider the following code, inside a function body:

struct {
    char c;
    int i;
  } foo;

char * cp1 = (char *) &foo;
char * cp2 = &foo.c;

Given this, cp1 and cp2 will compare as equal, but their bounds are nonetheless different. cp1 can point to any byte of foo and even to "one past" foo, but cp2 can only point to "one past" foo.c, at most, if we wish to maintain defined behaviour.

In this example, there might be padding between the foo.c and foo.i members. While the first byte of that padding co-incides with "one past" the foo.c member, cp2 + 2 might point into the other padding. The implementation can notice this during translation and instead of producing a program, it can advise you that you might be doing something you didn't think you were doing.

By contrast, if you read the initializer for the cp1 pointer, it intuitively suggests that it can access any byte of the foo structure, including padding.

In summary, this can produce undefined behaviour during translation (a warning or error) or during program execution (by encoding bounds information); there's no difference, standard-wise: The behaviour is undefined.

like image 41
Shao Avatar answered Oct 19 '22 16:10

Shao


You can cast your pointer into a pointer to a pointer to array to ensure the correct array semantics.

This code is indeed not defined but provided as a C extension in every compiler in common usage today.

However the correct way of doing it would be to cast the pointer into a pointer to array as so:

((int (*)[2])p)[0][0]

to get the zeroth element or say:

((int (*)[2])p)[1][1]

to get the last.

To be strict, he reason I think this is illegal is that you are breaking strict aliasing, pointers to different types may not point to the same address (variable).

In this case you are creating a pointer to an array of ints and a pointer to an int and pointing them to the same value, this is not allowed by the standard as the only type that may alias another pointer is a char * and even this is rarely used properly.

like image 36
Vality Avatar answered Oct 19 '22 16:10

Vality