2

I am learning C and as I got stuck at the pointer chapter. Particullary at the pointer to pointer ( **p ) part, used with processing 2 dimensional arrays.

The following function generates a 2 dimmensional array with all its elements equal to 0 .

double** example(int rows, int cols){
    static double tr[rows][cols];
    for(int i = 0; i < rows; i++)
       for(int j = 0; j < cols; j++)
            tr[j][i] = 0;
    return tr;
    }
int main(void){

    double **tr;
    int m = 2 ;
    int n = 2 ;

    tr = transpose(m, n);
    return 0;}

Is the tr returned by the function a "double pointer" to the first element of the tr array ? Or to its value ? Also, now (after the the function was called) how do I access the elements of the array at the part of memory where **tr points to ?

Can someone explain me how this all works ? Or suggest me an article or a chapter in a book that does ?

Joshua
  • 40,822
  • 8
  • 72
  • 132
ROBlackSnail
  • 111
  • 1
  • 3
  • 14
  • `example` returns a double pointer to the first element. You can access it's elements in the same way seen in the function, i.e. `tr[0][1]` <- row 0, col 1. – Geoff Mar 31 '17 at 15:56
  • Throw away this book, it is teaching you *garbage*. See Vlad's answer below. `double **` is *not* the correct type to return for a 2D array. – John Bode Mar 31 '17 at 17:51

3 Answers3

4

In C, functions will have to return a value/variable of the same type as the function. In this example, tr would be a double pointer, which is pointing to the first row.

When using double pointers to create 2D arrays, you're actually creating an pointer that points to an array of pointers, with the pointers within the array pointing to strings/values stored in each row. The double pointer would be pointing to the first pointer in the pointer array, which will point to the first element in the first row.

You can access values in 2D arrays using bracket notation such as tr[row][column], or as *(*(tr + row) + column). For the second notation, *(tr + row) will access the pointer pointing to the row you want, which you can then reference the value by using *(found_row + column), giving you the element in the column you want.

1

The presented program is totally incorrect. A variable length array may not have static storage duration/ According to the C Standard (6.7.6.2 Array declarators)

2 If an identifier is declared as having a variably modified type, it shall be an ordinary identifier (as defined in 6.2.3), have no linkage, and have either block scope or function prototype scope. If an identifier is declared to be an object with static or thread storage duration, it shall not have a variable length array type.

Moreover the pointer types double ** and double ( * )[cols] (to which the array provided that it was declared correctly is converted in expressions) are not compatible. So the function is wrong.

double** example(int rows, int cols){
    static double tr[rows][cols];
    for(int i = 0; i < rows; i++)
       for(int j = 0; j < cols; j++)
            tr[j][i] = 0;
    return tr;
    }

Here is a demonstrative program that shows how to deal with variable length arrays.

#include <stdio.h>

void init( size_t rows, size_t cols, double a[rows][cols] )
//  or
//  void init( size_t rows, size_t cols, double a[][cols] )
//  or
//  void init( size_t rows, size_t cols, double ( *a )[cols] )
{
    for ( size_t i = 0; i < rows; i++ )
    {
        for ( size_t j = 0; j < cols; j++ ) a[i][j] = 0.0;
    }
}

void display( size_t rows, size_t cols, double a[rows][cols] )
//  or
//  void display( size_t rows, size_t cols, double a[][cols] )
//  or
//  void display( size_t rows, size_t cols, double ( *a )[cols] )
{
    for ( size_t i = 0; i < rows; i++ )
    {
        for ( size_t j = 0; j < cols; j++ ) printf( "%lf", a[i][j] );
        putchar( '\n' );
    }
}

int main(void) 
{
    while ( 1 )
    {
        size_t m, n;

        printf( "Enter numbers of rows and columns (0 - exit): " );

        if ( scanf( "%zu%zu", &m, &n ) != 2 || m == 0 || n == 0 ) break;

        double a[m][n];

        putchar( '\n' );

        init( m, n, a );
        display( m, n, a );

        putchar( '\n' );
    }

    return 0;
}

Its output might look like

Enter numbers of rows and columns (0 - exit): 2 3

0.0000000.0000000.000000
0.0000000.0000000.000000

Enter numbers of rows and columns (0 - exit): 3 4

0.0000000.0000000.0000000.000000
0.0000000.0000000.0000000.000000
0.0000000.0000000.0000000.000000

Enter numbers of rows and columns (0 - exit): 0 0

Within the both functions the third parameter is adjusted to the pointer type double ( *a )[cols]. It is not the same as double **a.

If you will write for example the following program

#include <stdio.h>

#define M   2
#define N   3

int main(void) 
{
    int a[M][N] =
    {
        { 1, 2, 3 },
        { 4, 5, 6 }
    };

    int **p = ( int ** )a;

    p[0][0] = 10;

    return 0;
}

then it will have undefined behavior because p[0] considers the value stored in the element a[0][0] (or the combined value stored in elements a[0][0] and a[0][1] depending on the size of the pointer) that is the value 1 as a pointer value and try to access the memory at address 1 in the expression p[0][0].

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
0

Throw away the book that contains this code - it doesn't even compile properly. I get the following errors:

[fbgo448@n9dvap997]~/prototypes/buf: gcc -o stack -std=c99 -pedantic-errors -Wall stack.c
stack.c: In function âexampleâ:
stack.c:2: error: storage size of âtrâ isnât constant
stack.c:6: error: return from incompatible pointer type

You cannot create a variable-length array with static storage duration. Objects with static storage duration are allocated at program startup, so their sizes need to be known at compile time. A VLA cannot be instantiated until you know the size of its dimensions at runtime.

Except when it is the operand of the sizeof or unary & operators, or is a string literal used to initialize a character array in a declaration, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T", and the value of the expression will be the address of the first element of the array.

The expression tr has type "rows-element array of cols-element array of double"; in the return statement, this expression will "decay" to type "pointer to cols-element array of double", or double (*)[cols]. This is a completely different type from double **.

Multiple indirection shows up a lot, but this isn't a valid example.

You would normally see multiple indirection when you want a function to write to a parameter of pointer type:

void bar( T **p ) // for any type T
{
  *p = new_pointer_value();  // write a new value to the thing p points to
}

void foo( void )
{
   T *ptr;

   bar( &ptr ); // bar writes a value to ptr
}

You can use multiple indirection to create a structure that kinda-sorta looks and behaves like a 2D array, but it's not the same thing as a 2D array:

double **arr;
size_t rows = some_number_of_rows();
size_t cols = sime_number_of_cols();

arr = malloc( sizeof *arr * rows ); // allocates an array of pointers to double
if ( arr )
{
  for ( size_t i = 0; i < rows; i++ )
  {
    arr[i] = malloc( sizeof *arr[i] * cols ); // allocates an array of double
  }
}

When you're done, you have a structure that looks like this:

   +---+       +---+      +---+---+---+     +---+
a: |   |  ---> |   | ---> |   |   |   | ... |   |
   +---+       +---+      +---+---+---+     +---+
               |   | -+
               +---+  |   +---+---+---+     +---+
                ...   +-> |   |   |   | ... |   |
               +---+      +---+---+---+     +---+
               |   | -+
               +---+  |   +---+---+---+     +---+
                      +-> |   |   |   | ... |   |
                          +---+---+---+     +---+

You can access elements using regular subscript notation (a[i][j]) like a 2D array, but the memory for this structure is not allocated contiguously like it would be for a 2D array:

   +---+---+---+---+---+ 
a: |   |   |   |   |   |
   +---+---+---+---+---+
   |   |   |   |   |   |
   +---+---+---+---+---+
   |   |   |   |   |   |
   +---+---+---+---+---+
   ...  
John Bode
  • 119,563
  • 19
  • 122
  • 198