1
votes

I'm getting two types of errors:

Compiler's complaints

pr.c: In function ‘main’:

pr.c:20:2: warning: passing argument 1 of ‘printMatrix’ from incompatible pointer type [enabled by default]

pr.c:9:6: note: expected ‘const int (*)[80]’ but argument is of type ‘int (*)[80]’

pr.c:22:2: warning: passing argument 1 of ‘lights’ from incompatible pointer type [enabled by default]

pr.c:10:6: note: expected ‘const int (*)[80]’ but argument is of type ‘int (*)[80]’

It seems the compiler complains about receiving a non-const in a function that takes a const, but I was told this was the correct way of using const...

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

#define MAXCOL  80
#define MAXROW  20
#define randNormalize() srand(time(0))

void fillMatrix(int m[][MAXCOL], size_t rows, size_t cols);
void printMatrix(const int m[][MAXCOL], size_t rows, size_t cols);
void lights(const int m[][MAXCOL], size_t rows, size_t cols);
int star(const int m[][MAXCOL], int row, int col);

int main()
{
    int m[MAXROW][MAXCOL];

    randNormalize();

    fillMatrix(m, 5, 5);
    printMatrix(m, 5, 5);

    lights(m, 5, 5);

    return 0;
}

void fillMatrix(int m[][MAXCOL], size_t rows, size_t cols)
{
    int i, j;

    for(i = 0; i < rows; i++)
        for(j = 0; j < cols; j++)
            m[i][j] = rand()%21;

}

void printMatrix(const int m[][MAXCOL], size_t rows, size_t cols)
{
    int i, j;

    for(i = 0; i < rows; i++)
    {
        printf("\n");

        for(j = 0; j < cols; j++)
            printf("%d ", m[i][j]);
    }

    printf("\n");
}


void lights(const int m[][MAXCOL], size_t rows, size_t cols)
{
    int i, j;

    for(i = 1; i < rows - 1; i++)
    {
        printf("\n");

        for(j = 1; j < cols - 1; j++)
        {
            if( star(m, i, j) )
                printf("*");
            else
                printf(" ");
        }
    }

    printf("\n");
}



int star(const int m[][MAXCOL], int row, int col)
{
    int i, j;
    int sum = 0;

    for(i = row - 1; i <= row + 1; i++)
        for(j = col - 1 ; j <= col + 1; j++ )
            sum += m[i][j];

    return (sum/9 > 10);
}

I'm looking for the best solution that doesn't use pointers, as this was from an exercise of a course in which we have not yet covered them (although I have studied them).

2
The const-thing is fine, but fillMatrix expects a pointer to array of 80 ints, while you pass a pointer to an array of 5. Those are not compatible. This question might explain more.Kninnug
Why is the const thing fine? I want to get rid of it :~(.YoTengoUnLCD
FYI, the messages are not two seperate warnings. They are part of the same warning.kaylum
How come? They're messages about different lines.YoTengoUnLCD
Oh, ok I now see you have provided half the message for two warnings. What I meant was that each warning should tell you the warning type and then the warning note. Which is what you have shown respectively but for different warning lines. So you should actually post all 4 lines from the warnings output or at least post consistently the same part of the warning messages for each line.kaylum

2 Answers

2
votes

Unfortunately, in C there is no implicit conversion from int[X][Y] to const int[X][Y]. Nor is there implicit conversion from int (*)[Y] to const int (*)[Y].

This is a defect in the language; there's no technical reason why such a conversion should not be allowed. (C++ does allow this conversion).

You have two options, both unappealing:

  1. Have the functions accept int instead of const int
  2. Write a cast when calling the const int functions, e.g. printMatrix((const int (*)[MAXCOL])m, 5, 5);

Normally option 1 would be used and we just have to do without const-correctness for multi-dimensional arrays.

1
votes

Problem

When you use m in a function call like you do, it decays to a pointer. The type of pointer that m decays to is int (*)[5] -- a pointer to "an array of 5 ints".

Given the declaration of fillMatrix, the expected argument type is int (*)[MAXCOL], i.e. int (*)[80] -- a pointer to "an array of 80 ints".

These two types are not compatible.

The reason why these two types are not compatible can be understood by looking at how the compiler expects the pointers to behave, and the expected memory layout.

For explanation, I am going to change the sizes of the array to 2 and 4.

For

int m1[2][2];

the memory layout looks like:

m1[0][0]    m1[1][1]
|           |
+---+---+---+---+
|   |   |   |   |
+---+---+---+---+

If you declare a pointer that array,

int (*p1)[2] = m1;

The memory layout from the pointer perspective looks like:

p1      p1+1    p1+2
|       |       |
v       v       v
+---+---+---+---+
|   |   |   |   |
+---+---+---+---+

Now, let's create an array with size 4 x 4 and examine how the memory layout looks from a pointer point of view.

int m2[4][4];

Memory layout:

m2[0][0]                                                    m2[3][3]
|                                                           |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

Pointer:

int (*p2)[4] = m1;

The memory layout from the pointer perspective looks like:

p2              p2+1            p2+2            p2+3            p2+3
|               |               |               |               |
v               v               v               v               v
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

If you compare how the pointer arithmetic is used for the two different pointer types, you will see that:

p1[1][0] is quite different p2[1][0] in terms of how the values of those pointers are offset before performing the pointer -> int dereferencing.

If p1 is treated like it is of the same type as the type of p2, the pointer arithmetic will very easily lead to accessing the array out of bounds and will cause undefined behavior.

That is why a compiler will not use a pointer of type int (*)[4] when the expected argument type is int (*)[80].

Solution

There are many ways to solve the problem. The simplest solution is to use

int m[2][MAXCOL];