Lucy The Brazen
Lucy The Brazen

Reputation: 137

Pointers, Arrays and Functions

I programmed a function which can rotate 4 x 4 arrays, and now i want to output it... But I get a bunch warnings, but it compiles and works, so I guess i only did some "minor/formal" mistakes.

#include <stdio.h>

//-----------------------------------------------------------------------------
///
/// This function prints four 4x4 matrix
///
/// @param m1, m2, m3, m4 input matrix you want to print.
///
/// @return 0
//-----------------------------------------------------------------------------

void *print_matrix(char m1[4][4], char m2[4][4], char m3[4][4], char m4[4][4])
    {
        for(int i = 0; i < 4; i++)
        {
            for(int j = 0; j < 4; j++)
            {
                printf("%c ", m1[i][j]);
            }
            printf(" ");
            for(int j = 0; j < 4; j++)
            {
                printf("%c ", m2[i][j]);
            }
            printf(" ");
            for(int j = 0; j < 4; j++)
            {
                printf("%c ", m3[i][j]);
            }
            printf(" ");
            for(int j = 0; j < 4; j++)
            {
                printf("%c ", m4[i][j]);
            }
            printf("\n");
        }
        printf("\n");
        return 0;
    }

//-----------------------------------------------------------------------------
///
/// This function rotates a 4x4 matrix 
/// (and collects tears from overworked students @2am, because wtf is  )
///
/// @param m, the matrix you want to rotate
///
/// @return m_r the rotated matrix
//-----------------------------------------------------------------------------

char *rotate_matrix(char m[4][4])
{
    static char m_r[4][4];
    for(int i = 0; i < 4; i++)
    {
        for(int j = 0; j < 4; j++)
        {
            m_r[i][j] = m[3-j][i];
        }
    }
    return *m_r;
}

int main(void)
{
char matrix_t [4][4] = {{ '-', '-', '-', '-' },
                        { '-', 'O', '-', '-' },
                        { 'O', 'O', 'O', '-' },
                        { '-', '-', '-', '-' }
                       };
char matrix_z [4][4] = {{   '-', '-', '-', '-' },
                        { '-', 'O', 'O', '-' },
                        { 'O', 'O', '-', '-' },
                        { '-', '-', '-', '-' }
                       };
char matrix_l [4][4] = {{   '-', '-', '-', '-' },
                        { '-', 'O', '-', '-' },
                        { '-', 'O', '-', '-' },
                        { '-', 'O', 'O', '-' }
                       };
char matrix_i [4][4] = {{   '-', '-', 'O', '-' },
                        { '-', '-', 'O', '-' },
                        { '-', '-', 'O', '-' },
                        { '-', '-', 'O', '-' }
                       };
char (*array)[4][4];

  print_matrix(matrix_t, matrix_z, matrix_l, matrix_i);
  array = rotate_matrix(matrix_t);
  print_matrix(array, matrix_z, matrix_l, matrix_i);
    return 0;
}

This are the errors i get:

102:9: warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]


array = rotate_matrix(matrix_t);

,

103:16: warning: passing argument 1 of ‘print_matrix’ from incompatible pointer type [-Wincompatible-pointer-types]


print_matrix(array, matrix_z, matrix_l, matrix_i);

and

note: expected ‘char (*)[4]’ but argument is of type ‘char (*)[4][4]’


void *print_matrix(char m1[4][4], char m2[4][4], char m3[4][4], char m4[4][4])

And to be honest, i don't get these errors, because the code seems to work, I'm never out of boundaries and obviously I'm not doing any memory violations...

Upvotes: 0

Views: 83

Answers (1)

ruakh
ruakh

Reputation: 183251

The warnings are because these:

char *            // pointer to char         or to first element of char[]
char (*)[4]       // pointer to char[4]      or to first element of char[][4]
char (*)[4][4]    // pointer to char[4][4]   or to first element of char[][4][4]

are different types. A pointer doesn't just denote a memory location, but also (via its type) how that memory location should be interpreted.

One tricky bit is that an array name (or any other expression that evaluates to an array) decays to a pointer. For example, this is perfectly valid:

char abc[4] = "abc";    // abc is an array of four characters
char a = *abc;          // a is 'a', the first character of abc

. . . and conversely, you can use array notation on a pointer (to perform pointer arithmetic and immediately dereference the result):

char * abc = "abc";     // abc is a pointer (into static memory)
char c = abc[2];        // c is 'c', the third character of abc

So char * is interconvertible with char ()[], char (*)[4] is interconvertible with char ()[][4], and char (*)[4][4] is interconvertible with char ()[][4][4].

And what makes it extra tricky is that C doesn't quite support passing an array as a function parameter (though if you really want that, you can achieve the same effect by wrapping the array in a struct and passing that). It does let you declare a function parameter as having an array type, but that is actually syntactic sugar for a function parameter with a pointer type. So these three signatures are equivalent:

void *print_matrix(char m1[4][4], char m2[4][4], char m3[4][4], char m4[4][4])
void *print_matrix(char m1[][4], char m2[][4], char m3[][4], char m4[][4])
void *print_matrix(char (*m1)[4], char (*m2)[4], char (*m3)[4], char (*m4)[4])

Your code works because your various warnings all sort of cancel out:

  • rotate_matrix should return either a char (*)[4] (a pointer to the first row of a char[][4]) or a char (*)[4][4] (a pointer to a complete char[4][4]). Instead, you have it return char *, and to accomplish that, you dereference the desired pointer, thereby getting (after array-to-pointer decay) a pointer to the first element of the first row of the matrix. Of course, all of these pointers point to the same memory location, just with different types.
    • To declare it with return-type char (*)[4], you can write char (*rotate_matrix(char m[4][4]))[4] . . . but it's probably better to declare a row type, typedef char row[4], and then write row * rotate_matrix(char m[4][4]). In this case you would return m_r at the end, which decays to a pointer to the first row of m_r.
    • To declare it with return-type char (*)[4][4], same thing, just with extra [4]'s (and a name like matrix rather than row). In this case you would return &m_r at the end, meaning a pointer to the entirety of m_r.
    • Either of the above approaches is fine IMHO, though of course you'll have to adjust the calling sites to match. For the rest of this answer, I'm going to assume we're doing the second version, the one with return-type char (*)[4][4].
  • array has type char (*)[4][4], which is fine. The code warning in array = rotate_matrix(matrix_t) is because of the mis-declaration of rotate_matrix. But the actual value of the returned pointer was already fine, at least on typical platforms.
    • That said, if you're going to use this type for array, then you should probably call it array_ptr, since it's a pointer to your usual array type.
  • But then you pass array as an argument to print_matrix, which takes a parameter of type char [4][4], meaning char (*)[4]. You can't convert a char (*)[4][4] (pointer to array of 4 rows of 4 chars) to a char (*)[4] (pointer to array of 4 chars). To fix this, you just need to dereference array, by passing *array instead.

(Note: I'm not sure that all of the above implicit conversions are actually guaranteed to work; pointers to different types of objects might not all be interconvertible on all platforms. And even on typical modern platforms, where they are all interconvertible, you might run afoul of strict aliasing rules, which means that your compiler might end up performing optimizations that totally break your code. But it will usually work.)

Upvotes: 1

Related Questions