q.Then
q.Then

Reputation: 2753

Contradictory pointers in an array

I came across some "weird" behaviour in C, at least something I did not expect. Consider this following line of code:

int arrayB[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
printf("%d\n", (arrayB+1)[1]);

This works fine as (arrayB + 1)[1] translates to *(arrayB+1 +1) which is the third element of arrayB (thus it prints 3 as expected)

However, consider the below code where I initialize a 2D array called arrayA (with 5 arrays of 10 ints, each {0, 1, 2, 3, 4, 5, 6, 7, 8, 9})

int ** arrayA = malloc(sizeof(int *) * 5);
for (int i = 0; i < 5; i++) {
    arrayA[i] = malloc(sizeof(int) * 10);
    for (int j = 0; j < 10; j++) {
        arrayA[i][j] = j;
    }
}

printf("%d\n", *(arrayA+1)[2]);

This code does not raise a warning, and it claims that *(arrayA+1)[2] is an int type. However.. it seems that it should be (since array subscript has higher priority over dereference operator), *(arrayA+1)[2] translates to *(arrayA+3) which is of type int *, since arrayA is a int ** that Even further is that this is allowed after:

*(arrayA+1)[2] = 1;

But how is this possible? It seems as if I am assigning a int to a int *... which typically raises a warning.

Upvotes: 0

Views: 60

Answers (3)

David C. Rankin
David C. Rankin

Reputation: 84521

In addition to the pointer correction, while you are free to declare a pointer-to-pointer-to-int (e.g. int **arrayA;), and allocate pointers, then allocate memory and assign the block to each individual pointer, there is no need to do so when dealing with a fixed number of elements in each row. You can simply declare a pointer-to-array-of-type-no. (e.g. int (*arrayA)[10]; and allocate once. For example:

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

#define ASZ 5

void *xmalloc (size_t s) {
    void *memptr = malloc (s);
    if (memptr == 0) {
        fprintf (stderr, "xmalloc() error: virtual memory exhausted.\n");
        exit (EXIT_FAILURE);
    }
    return memptr;
}

int main (void) {

    int i, j;
    int (*arrayA)[ASZ] = xmalloc (sizeof *arrayA * ASZ);

    for (i = 0; i < ASZ; i++) {
        for (j = 0; j < ASZ; j++) {
            arrayA[i][j] = j+i;
        }
    }

    for (i = 0; i < ASZ; i++) {
        if (!i) putchar ('\n');
        for (j = 0; j < ASZ; j++) {
            printf (" %2d", arrayA[i][j]);
        }
        putchar ('\n');
    }

    printf("\n *(arrayA+1)[2] = %d\n\n", *(arrayA+1)[2]);

    free (arrayA);

    return 0;
}

(don't forget to validate your memory allocation. using a helper like xmalloc can make life easier)

Example Use/Output

$ ./bin/arr2dderef

  0  1  2  3  4
  1  2  3  4  5
  2  3  4  5  6
  3  4  5  6  7
  4  5  6  7  8

 *(arrayA+1)[2] = 3

Don't forget to validate your memory use with a memory error checking program such as valgrind on Linux, and don't forget to free the memory when it is no longer needed. (yes it is freed automatically on exit, but get in the habit now of accounting for each byte, and you will save yourself a lot of grief later)

$ valgrind ./bin/arr2dderef
==28392== Memcheck, a memory error detector
==28392== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==28392== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==28392== Command: ./bin/arr2dderef
==28392==

  0  1  2  3  4
  1  2  3  4  5
  2  3  4  5  6
  3  4  5  6  7
  4  5  6  7  8

 *(arrayA+1)[2] = 3

==28392==
==28392== HEAP SUMMARY:
==28392==     in use at exit: 0 bytes in 0 blocks
==28392==   total heap usage: 1 allocs, 1 frees, 100 bytes allocated
==28392==
==28392== All heap blocks were freed -- no leaks are possible
==28392==
==28392== For counts of detected and suppressed errors, rerun with: -v
==28392== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)

Upvotes: -1

G. Emadi
G. Emadi

Reputation: 230

We discuss about the int ** arrayA (2D).

arrayA is int ** , so arrayA+x is int ** too.

*arrayA or arrayA[i] is int *, and is a linear array(1D).

So, *(arrayA+1)[2], with any priority is a value not address.

Upvotes: 0

user2357112
user2357112

Reputation: 280181

it seems that ... *(arrayA+1)[2] translates to *(arrayA+3)

No, you dropped a *. *(arrayA+1)[2] translates to **(arrayA+3), which is indeed an int.

Upvotes: 3

Related Questions