2

I tried the following code

    char arr[5] = {'A', 'E', 'I', 'O', 'U'};
    char (*p_arr)[1] = &arr;
    printf("%c\n", p_arr[0][4]); //returns 'U'

On this stackoverflow thread,

    char (*p_arr2D)[m] = &arr2D //or arr2D

is also a syntax for the decay of a n*m 2D-array arr2D, returning a pointer to its first element, an array of m elements.

p_arr[0] seems to be the "reverse" array decay, retrieving a 2D array from a pointer to an array. How can that happen ? In the code snippet above, is p_arr interpreted as a 1*5 array (from the original 1D-array with 5 elements) ?

Lundin
  • 195,001
  • 40
  • 254
  • 396
Y.GP
  • 41
  • 6
  • My bad, I split my original question in two halves and forgot to change `char (*p_arr)[1] = &arr;` to `char (*p_arr)[5] = &arr;` in this part. However, `char (*p_arr)[1] = &arr;` doesn't create any compilation error (see my [other question](https://stackoverflow.com/questions/46687759/pointer-to-2d-array-with-incompatible-row-length)), and i don't understand why. – Y.GP Oct 11 '17 at 12:07
  • (The C compiler does not prevent what you want to do.) Compile it with `-Wall` option. – BLUEPIXY Oct 11 '17 at 12:16
  • I did a rollback of code changes. Please don't fix things your code once answers have been posted. By fixing it, you rendered most answers irrelevant. Instead, if you have a follow-up question, ask a new question (like you did). – Lundin Oct 11 '17 at 15:03

4 Answers4

1

To begin with, the line char (*p_arr)[1] = &arr; is not valid C. Your compiler must give a diagnostic message. p_arr is an array pointer to an array of 1 char, but you assign it to point at an array of 5 char.

As for why it seems like you have a 2D array - you don't have one - is because p_arr[0] works like any other pointer arithmetic, giving you pointed-at item "offset + 0". Which is the array. And then you access item 4 in the array.

A simple example of the very same pointer arithmetic principle would be this:

int a;
int* ptr = &a;
ptr[0] = whatever; // perfectly valid C

ptr[0] is guaranteed to be equivalent to *(ptr+0).

Correct use of the array pointer should be printf("%c\n", (*p_arr)[4]). In this case it happened to work, but in other cases you might invoke undefined behavior, since p_arr is not allowed to point beyond the allocated memory.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • My bad, I split my original question in two halves and forgot to change char (*p_arr)[1] = &arr; to char (*p_arr)[5] = &arr; in this part (done now). However, char (*p_arr)[1] = &arr; doesn't create any compilation error (see my other question), and i don't understand why. I already put this comment under my question, tell me if it's a good or bad practice to duplicate it here. I'll remove it if needed – Y.GP Oct 11 '17 at 12:36
1

a 2D array is stored in memory like a 1D array How are multi-dimensional arrays formatted in memory?

the char (*p_arr)[1] = &arr; creates a pointer to an array of char pointers with one element ( (*p_arr)[1] ) and by the assignment &arr this pointer gets the address of an already existing array with 5 elements

so printf("%c\n", p_arr[0][4]); prints the 5th element ( U ) because by p_arr[0][4] adresses the 5th element in line 1

printf("%c\n", p_arr[1][4]); would give an error

so the reason why this code works is that a 2D array is stored in memory like a 1D array

ralf htp
  • 9,149
  • 4
  • 22
  • 34
  • "and by the assignment &arr this pointer gets the address of an already existing array" No, because that part is not valid C. It is not a valid assignment. – Lundin Oct 11 '17 at 15:01
1

This declaration

char (*p_arr)[1] = &arr;

is incorrect because the declared pointer and the initializer have incompatible types. There is no implicit conversion from the type char ( * )[5], the type of the initializer, to the type char ( * )[1], the ty[e of the declared pointer. So you need an explicit casting to reinterpret a pointer of one type as a pointer of another type as for example

char (*p_arr)[1] = ( char ( * )[1] )&arr;

As for your question then if you have an object of type T where T is some type

T obj;

and a pointer to the object like

T *ptr = &obj;

then expression *ptr or ptr[0] yields the referenced object of the type T.

So let's assume that you have the following declarations

char arr[5] = {'A', 'E', 'I', 'O', 'U'};
char (*p_arr)[5] = &arr;

For the type char[5] that is the type of the variable arr you can introduce a typedef name.

typedef char T[5];

Then the declarations will look like

T arr = {'A', 'E', 'I', 'O', 'U'};
T *p_arr = &arr;

As it was mentioned above the expression *p_arr or p_arr[0] yields the referenced object of the type T that is of the type char[5].

From the C Standard (6.5.3.2 Address and indirection operators)

4 The unary * operator denotes indirection. If the operand points to a function, the result is a function designator; if it points to an object, the result is an lvalue designating the object. If the operand has type ‘‘pointer to type’’, the result has type ‘‘type’’. If an invalid value has been assigned to the pointer, the behavior of the unary * operator is undefined.

And (6.5.2.1 Array subscripting)

2 A postfix expression followed by an expression in square brackets [] is a subscripted designation of an element of an array object. The definition of the subscript operator [] is that E1[E2] is identical to (*((E1)+(E2))). Because of the conversion rules that apply to the binary + operator, if E1 is an array object (equivalently, a pointer to the initial element of an array object) and E2 is an integer, E1[E2] designates the E2-th element of E1 (counting from zero).

and at last (6.5.6 Additive operators)

7 For the purposes of these operators, a pointer to an object that is not an element of an array behaves the same as a pointer to the first element of an array of length one with the type of the object as its element type.

So in this declaration

char arr[5] = {'A', 'E', 'I', 'O', 'U'};
char (*p_arr)[5] = &arr;

the pointer p_arr does not point to an array element. However it can behaves as it points to the first element of an array of length 1. You can imagine it the following way

char arr[1][5] = { {'A', 'E', 'I', 'O', 'U'} };
char (*p_arr)[5] = arr;

So the expression p_arr[0] gives the first element of the imagined two-dimensional array that is an element of the type char[5].

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • My bad, I split my original question in two halves and forgot to change char (*p_arr)[1] = &arr; to char (*p_arr)[5] = &arr; in this part (done now). However, char (*p_arr)[1] = &arr; doesn't create any compilation error (see my other question), and i don't understand why. I already put this comment under my question, tell me if it's a good or bad practice to duplicate it here. I'll remove it if needed – Y.GP Oct 11 '17 at 12:37
0

You are declaring p_arr to be a pointer to an array of chars (and that array is of length one which is a sure sign of a problem).

You may want the much simpler syntax:

char *p_arr = arr;

or maybe:

#include <stdio.h>

int main() {
    char arr[] = {"Hello world"};
    char *p[3] = {NULL, arr, NULL};

    printf("%s %c\n", p[1], p[1][3]);
}

John

JohnRowe
  • 49
  • 2
  • My bad, I split my original question in two halves and forgot to change char (*p_arr)[1] = &arr; to char (*p_arr)[5] = &arr; in this part (done now). However, char (*p_arr)[1] = &arr; doesn't create any compilation error (see my other question), and i don't understand why. I already put this comment under my question, tell me if it's a good or bad practice to duplicate it here. I'll remove it if needed – Y.GP Oct 11 '17 at 12:36