Home > Enterprise >  Pointer initialization/dereferencing
Pointer initialization/dereferencing

Time:11-22

What is the difference between

int(*a)[5];
int b[5] = { 1, 2, 3, 4, 5 };
i = 0;
a = &b;

for (i = 0; i < 5; i  )
    printf("%d\n", *(*a i));

and

int b[5] = { 1, 2, 3, 4, 5 };
int *y = b;

for (i = 0; i < 5; i  )
    printf("%d\n", *y i);

Both snippets produce same output but I dont understand what is the difference in initialization? How is

int *y = b;

different to

int(*a)[5];

How to explain this kind of dereferencing?

printf("%d\n", *(*a i));

CodePudding user response:

Almost a side note, but some of the code is incorrect.

Referring to the code below ...

Note that the first snippet, which I'll call foo is correct.

But, the second snippet which I'll call bar is incorrect. It only works [serendipitously] because the b array is 1, 2, 3, 4, 5 (i.e. in sorted order).

If the b array is not sorted, then bar will not print the correct order. This is because *y i must be *(y i)

For *y i, the value fetched for all loop iterations is b[0] to which we add the value of i. To use array/index syntax, we are doing: y[0] i

What we want is to fetch: b[0] b[1] ... b[4]

The corrected version is in baz. So, we use *(y i). In array/index syntax, this is y[i].


Here is the code that illustrates this:

#include <stdio.h>

#if SORTED
int b[5] = { 1, 2, 3, 4, 5 };
#else
int b[5] = { 5, 4, 1, 2, 3 };
#endif

void
foo(void)
{
    int (*a)[5];
    a = &b;

    printf("\n");

    for (int i = 0; i < 5; i  )
        printf("foo: %d\n", *(*a   i));
}

void
bar(void)
{
    int *y = b;

    printf("\n");

// NOTE/BUG: "*y   i" is incorrect -- see baz
    for (int i = 0; i < 5; i  )
        printf("bar: %d\n", *y   i);
}

void
baz(void)
{
    int *y = b;

    printf("\n");

// NOTE/FIX: correct fix for bar
    for (int i = 0; i < 5; i  )
        printf("baz: %d\n", *(y   i));
}

int
main(void)
{

    foo();
    bar();
    baz();

    return 0;
}

Here is the program output:


foo: 5
foo: 4
foo: 1
foo: 2
foo: 3

bar: 5
bar: 6
bar: 7
bar: 8
bar: 9

baz: 5
baz: 4
baz: 1
baz: 2
baz: 3

CodePudding user response:

From the C Standard (6.3.2.1 Lvalues, arrays, and function designators)

3 Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.

In your second code snippet

int b[5] = { 1, 2, 3, 4, 5 };
int *y = b;

in the declaration of the pointer y the array designator b used as an initializer is implicitly converted to pointer to its first element. The above declaration is equivalent to

int *y = &b[0];

Dereferencing the pointer y you will get the first element of the array of the type int.

Try the following demonstration program.

#include <stdio.h>

int main( void )
{
    int b[5] = { 1, 2, 3, 4, 5 };
    int *y = b;

    printf( "sizeof( *y ) = %zu, *y = %d\n", sizeof( *y ), *y );
}

Its output might look like

sizeof( *y ) = 4, *y = 1

Thus in this for loop

for (i = 0; i < 5; i  )
    printf("%d\n", *y i);

you just outputting the value of the first element of the array b plus the value of the variable i. That is actually the loop does not output elements of the array b because they even are not accessed except the first element of the array..

If you want to output elements of the array you need to write

for (i = 0; i < 5; i  )
    printf("%d\n", *( y i ));

It is the same as to write

for (i = 0; i < 5; i  )
    printf("%d\n", y[i] );

because the subscript operator is evaluated like (the C Standard, 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).

In your first code snippet

int(*a)[5];
int b[5] = { 1, 2, 3, 4, 5 };
i = 0;
a = &b;

for (i = 0; i < 5; i  )
    printf("%d\n", *(*a i));

the pointer a is declared as a pointer to array of the type int[5]. So dereferencing the pointer you get an array of the type int[5].

Consider the following demonstration program,

#include <stdio.h>

int main( void )
{
     int(*a)[5];
     int b[5] = { 1, 2, 3, 4, 5 };

    printf( "sizeof( *a ) = %zu\n", sizeof( *a ) );
}

Its output might look like

sizeof( *a ) = 20

In this for loop

for (i = 0; i < 5; i  )
    printf("%d\n", *(*a i));

the expression *a yields the pointed array b that used in the expression *a i is converted to pointer to its first element. To make it clear you could rewrite the for loop the following way

for (i = 0; i < 5; i  )
{
    int *p = *a;
    printf("%d\n", *(p   i));
}

That is in this declaration

    int *p = *a;

the array obtained by dereferencing the pointer a is implicitly converted to pointer to its first element according to the first provided quote from the C Standard..

Consider one more example. Let's assume that you have a two-dimensional array like for example

int b[2][5] =
{
    { 1, 2, 3, 4,  5 },
    { 6, 7, 8, 9, 10 } 
};

As it was pointed out above the array used in expressions with rare exception is converted to pointer to its initial (first) element. The type of elements of the array is int[5]. So you can declare a pointer like

int ( *a )[5] = b;

In this case to output the array you can write for example

for ( int i = 0; i < 2; i   )
{
    for ( int j = 0; j < 5; j   )
    {
        printf( "%d\n", *( *a   j ) );
    }

      a;
}

After this statement

  a;

the pointer a will point to the second element of the array b that is to its second "row" of the type int[5] due to the pointer arithmetic..

And again this declaration

int ( *a )[5] = b;

is equivalent to

int ( *a )[5] = &b[0];
  • Related