1

Edited to add the entire assignment and expected output.

You have been hired to assist firefighters locate wildfires in a large geographic area. The area is divided into smaller zones. Each zone is scanned via satellite for its average temperature. If a zone has an average temperature strictly greater than 1000°F, we assume there is a fire in that zone. If the temperature is between 100 degrees (included) and 1000 degrees (included), we have to further investigate, so it becomes a "zone to watch."

The large geographic area you are watching is a rectangle with a certain length and width, each given in terms of zones. For example, if the area to be scanned has a length of 6 and width of 9 then it will be divided into 6*9 zones:

Because your program will be used for a variety of geographic areas (each with its own length and width) your program needs to dynamically allocate the memory for the number of zones it is to handle (vertically and horizontally).

To do so, you must use the two following functions without changing the code in them:

int ** allocateIntStarArray(int num){
int ** ptr = (int **) malloc(num * sizeof(int *));
return ptr;

}

int * allocateIntArray(int num){
int * ptr = (int *) malloc(num * sizeof(int));
return ptr;

}


The function `allocateIntArray()` will be used to allocate the space required to store the average temperatures in one row of zones, that is, an array of integers. The function therefore returns a pointer to such an array of integers.

The function `allocateIntStarArray()` will be used to allocate an array of pointers, each of which will store a pointer to a row of integers (temperatures of zones). That is, the function returns a pointer to an array of pointers. Each cell of this array will point to an array of integers containing the temperature values for the zones.

The inputs of the program are first the length, then the width of an area, then the average temperatures of all zones, row by row.

Please remember to free the memory you have allocated.

The output should pinpoint the possible zones with fires with [X] and the watch zone with a [*], the other zone are displayed with [ ].

    Input:

6
9
70   71   70   72   70   69
71   73   68   71   73   72
70   71   70   76   1900 78
69   71   100  800  75   71
70   70   71   79   70   69
70   71   112  1005 75   72
70   71   70   900  70   70
72   70   70   72   70   69
73   74   73   72   70   70


    Output:

[ ][ ][ ][ ][ ][ ]
[ ][ ][ ][ ][ ][ ]
[ ][ ][ ][ ][X][ ]
[ ][ ][*][*][ ][ ]
[ ][ ][ ][ ][ ][ ]
[ ][ ][*][X][ ][ ]
[ ][ ][ ][*][ ][ ]
[ ][ ][ ][ ][ ][ ]
[ ][ ][ ][ ][ ][ ]
----

The code I'm working on is to create a matrix based off user input. I'm having an issue getting my variable **mat back to the first array element so that it will print the rectangle correctly. Could someone enlighten me on how to do this? What I have so far:


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

int **allocateIntStarArray(int);
int *allocateIntArray(int);
int **allocateMatrix(int, int);

void readValues(int **, int, int);
void print(int **, int, int);

int main(void) {
    int rows, cols;

scanf("%d %d", &rows, &cols);
    int **mat = allocateMatrix(rows, cols);

    readValues(mat, cols, rows);
    print(mat, cols, rows);

    /* free your memory */
    for (int r = 0; r < rows; r++){
        free(mat[r]);
    }
    free(mat);
    return 0;
}

void readValues(int **mat, int ncols, int nrows) {
    for (int r = 0; r < nrows; r++) {
        for (int c = 0; c < ncols; c++) {
            scanf("%d", &mat[r][c]);

            
        }
    }
}

void print(int **mat, int ncols, int nrows) {
    for (int r = 0; r < nrows; r++) {
        for (int c = 0; c < ncols; c++) {
            int value = mat[r][c];

            if (value > 1000){
                printf("[X]");
            }
            else if (value >= 100){
                printf("[*]");
            }
            else{
                printf("[ ]");
            }
        
        }
        printf("\n");
    }
}

int **allocateMatrix(int nrows, int ncols) {
    
    int **mat = allocateIntStarArray(nrows);


    for (int row = 0; row <= nrows; ++row) {
        mat[row] = allocateIntArray(ncols);
    }

    return mat;
}

/* Provided functions, do not edit */
int **allocateIntStarArray(int num) {
    int **ptr = (int **) malloc(num * sizeof(int *));
    return ptr;
}

int *allocateIntArray(int num) {
    int *ptr = (int *) malloc(num * sizeof(int));
    return ptr;
}
  • "*I'm having an issue*" is not very precise. Can you please give the exact input, expected result and actual result? – kaylum Jan 16 '22 at 21:45
  • `scanf("%d", &**mat);` change that to be `scanf("%d", &mat[rows][cols]);` – kaylum Jan 16 '22 at 21:47
  • `**mat = **(mat-num);` I'm not sure what that is supposed to do. Suggest you ditch the double pointers. The way you use it is wrong. And even if used correctly it is still more confusing than it needs to be. Use simpler/clearer array notation: `mat[row][col]`. – kaylum Jan 16 '22 at 21:50
  • Ive edited my post to more accurately reflect what Im trying to do. Hope that helps. – Mary Layman Jan 16 '22 at 22:26

3 Answers3

0

I would not use double pointers instead of 2D arrays.

void *allocateMatrix(size_t nrows, size_t ncols, int (**array)[ncols]) 
{
    int (*arrptr)[ncols]  = malloc(nrows * sizeof(*arrptr));
    if(array) *array = arrptr;
    return arrptr;
}

int main(void)
{
    size_t cols = 30, rows = 40;
    int (*matrix)[cols] = allocateMatrix(rows, cols, NULL);

    if(matrix)
    {
        matrix[5][4] = 97;
        matrix[4][2] = matrix[5][4] * 4;
        printf("%d %d\n", matrix[5][4], matrix[4][2]);
    }
    free(matrix);
}

Easier, only one allocation / free, usage same as arrays and more efficient as you remove one level of indirection.

Also use the correct type for sizes and indexes: size_t

0___________
  • 60,014
  • 4
  • 34
  • 74
  • 1
    I apologize I forgot to clarify something. The allocation functions were written by the instructor and we are not allowed to change that part of the code. What you've done there, "size_t", I've never seen that. I know the sizeof function, am I understanding that these are 2 different things? – Mary Layman Jan 16 '22 at 22:15
  • @MaryLayman [`size_t`](https://en.cppreference.com/w/c/types/size_t) is the correct type to use when dealing with memory as, unlike `int`, it can the store the maximum size of any object on your system. The [`sizeof`](https://en.cppreference.com/w/c/language/sizeof) *operator* resolves to this type, and functions like [`strlen`](https://en.cppreference.com/w/c/string/byte/strlen) return it. While `int` will work for small sizes, your instructor has written fairly poor C code. (See also: [Do I cast the result of malloc?](https://stackoverflow.com/q/605845/2505965)) – Oka Jan 16 '22 at 22:37
  • @MaryLayman The instructor needs instruction. – 0___________ Jan 16 '22 at 22:51
  • @Oka @ 0___________You aren't the first to say this about the instructor. The string.h library has yet to be taught to us as well. I only know of it because of forums and other sources. The compiler we use in our class doesn't support it though. I feel that they are trying to teach us to do things the hard way in order to get us in the right mindset but man it makes things so much more frustrating. – Mary Layman Jan 16 '22 at 23:02
  • @MaryLayman `string.h` is not a library, it is a header. – M. Nejat Aydin Jan 16 '22 at 23:25
  • @M. Nejat Aydin shows how new this is to me. – Mary Layman Jan 17 '22 at 07:33
0

In scanf("%d", &**mat);, the first * will be balanced by its & counterpart, as the two operators have the opposite effect.

The result will be *mat, a value of type int *. While is is the correct type for the %d format specifier, *mat is the address of the first subarray in the matrix. Repeatedly reading values to this address will continue to overwrite the first value of the first subarray (mat[0][0]).

Since you never move any pointers, the rest of the matrix will remain uninitialized.

That said, there is no need to manually manage your pointers, since you are already calculating indexes. Simply use array subscript notation with your indexes, and take the address of each value's location.

scanf("%d", &mat[rows][cols]);

(See Member access operators)

In the print function,mat - num is going to resolve to some address you should not attempt to access, as it is outside the bounds of the mat object. This is a classic example of Undefined Behavior.

You then attempt to set m to be some value located through this address, but only once.

Again, you are calculating your indexes already, so use them to your advantage. Set m in the inner loop to the value found at mat[rows][cols], so that you update its value for each iteration.


Here's a functional program, with an example of freeing the memory you allocate.

(Note: as previously stated, size_t is the correct type to use for memory sizes and indexes. I have stuck with int here, to match your instructor's flawed implementations.)

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

int **allocateIntStarArray(int);
int *allocateIntArray(int);
int **allocateMatrix(int, int);

void readValues(int **, int, int);
void print(int **, int, int);

int main(void) {
    int rows, cols;

    if (2 != scanf("%d %d", &cols, &rows)) {
        fprintf(stderr, "Could not read matrix size information.\n");
        return EXIT_FAILURE;
    }

    int **mat = allocateMatrix(rows, cols);

    readValues(mat, cols, rows);
    print(mat, cols, rows);

    /* free your memory */
    for (int i = 0; i < rows; i++)
        free(mat[i]);
    free(mat);
}

void readValues(int **mat, int ncols, int nrows) {
    for (int i = 0; i < nrows; i++) {
        for (int j = 0; j < ncols; j++) {
            if (1 != scanf("%d", &mat[i][j])) {
                fprintf(stderr, "Invalid value read.\n");
                exit(EXIT_FAILURE);
            }
        }
    }
}

void print(int **mat, int ncols, int nrows) {
    for (int i = 0; i < nrows; i++) {
        for (int j = 0; j < ncols; j++) {
            int value = mat[i][j];

            if (value > 1000)
                printf("[X]");
            else if (value >= 100)
                printf("[*]");
            else
                printf("[ ]");
        }

        /* print a newline after every row */
        putchar('\n');
    }
}

int **allocateMatrix(int nrows, int ncols) {
    int **mat = allocateIntStarArray(nrows);

    for (int row = 0; row < nrows; ++row) {
        mat[row] = allocateIntArray(ncols);
    }

    return mat;
}

/* Provided functions, do not edit */
int **allocateIntStarArray(int num) {
    int **ptr = (int **) malloc(num * sizeof(int *));
    return ptr;
}

int *allocateIntArray(int num) {
    int *ptr = (int *) malloc(num * sizeof(int));
    return ptr;
}

Here is the program I used to quickly generate data for testing:

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

int main(void) {
    srand((unsigned) time(NULL));

    int rows = (rand() % 20) + 1;
    int cols = (rand() % 20) + 1;

    printf("%d %d\n", rows, cols);

    for (int i = 0, n = rows * cols; i < n; i++)
        printf("%d ", rand() % 2000);
}

Update:

As I suspected, you are getting the indexes for your rows and columns mixed up.

The way your matrix is built, the first subscript value (matrix[r]) corresponds to the row, and the second subscript value (matrix[r][c]) corresponds to the column in that row.

In read_values

for(i=0;i<ncols;i++){
    for(j=0;j<nrows;j++){
        scanf("%d", &mat[i][j]);
    }
}

and in print

for(i=0;i<ncols;i++){
    for(j=0;j<nrows;j++){
        int value = mat[i][j];
        /* ... */

you have flipped those around, accessing mat[COLUMN as i][ROW as j].

You will access memory outside the bounds, when i >= nrows, or j >= ncols.

It might also be a good idea to flip your nested loops around, so that they read "for each row, for each column", which better matches the subscript grammar:

for (int r = 0; r < nrows; r++)    
    for (int c = 0; c < ncols; c++)       
        scanf("%d", &mat[r][c]);

Additionally, in main, you must free(mat); only after you've freed all the subarrays first

for(int i=0;i<rows;i++){ 
    free(mat[i]);
}   

free(mat); /* move this outside, after the loop */

otherwise mat will be a dangling pointer on the second iteration, and mat[i] will invoke Undefined Behavior.

Final note, to match the assignment requirements, your middle range of temperatures should be value >= 100, as the range is inclusive ([100, 1000]).

Oka
  • 23,367
  • 6
  • 42
  • 53
  • thank you so much for this wonderful explanation. This class is the 4th of several in a series I've been working on completing. There is 3 left but I'm considering taking a different class on C programming, as I feel that everything that I need to know isn't covered or that it isnt explained well. Are there any that you can recommend? – Mary Layman Jan 17 '22 at 07:47
  • @MaryLayman While I can not recommend any specific classes to you, I can point you in the direction of the [*The Definitive C Book Guide and List*](https://stackoverflow.com/a/562377/2505965). – Oka Jan 17 '22 at 07:58
  • its a start, thank you again. – Mary Layman Jan 17 '22 at 19:07
  • So I reposted my code after revising it and it still did not allocate the memory correctly. So I posted it in the class forum and they said. "I think you're confused about lines and rows. You need to reserve place in memory for width lines of size length each." – Mary Layman Jan 19 '22 at 02:41
  • @MaryLayman Without seeing the revised code, I can only speculate: firstly, make sure you stay consistent with the ordering of your nested loops (including allocation functions) -- you can see in my example I always go *outer-row, inner-column*. If I'm reading the problem statement correctly, you need **length** *rows* of **width** *"zones"*, which does seem to contradict what you've just quoted. In either case though, switching the initial arguments to `scanf` will change the 'shape' of the memory (`scanf("%d %d", &rows, &cols)` <=> `scanf("%d %d", &cols, &rows)`), and thus the output. – Oka Jan 19 '22 at 03:23
  • @MaryLayman I would encourage you to update the question, however, including your revised program. You should see if the class forum can provide some sample input, and the expected output relative to it. Providing that would make it very clear as to how the program should behave. – Oka Jan 19 '22 at 03:27
  • There is actually some example of input and output posted. I will update the comment to include that and the revised code after I get home from work, as it's very difficult to copy from my phone. – Mary Layman Jan 19 '22 at 03:54
  • Ive replaced the code with the revised code in the original post and put the expected input and output. It does print as expected when I use the codecast sandbox provided by the class, but when I submit it as the answer to my assignment it says there is a memory allocation issue. – Mary Layman Jan 19 '22 at 06:14
  • Here is what is being said as to why it is incorrect "Your program failed after trying to access memory outside of allocated areas, or exceeding memory limit. It can be due to one of these : Your program exceeded the memory limit allowed for this task, be that with static variables, dynamic allocations, or the stack. Your program tried to access memory outside of its allocated areas." – Mary Layman Jan 19 '22 at 06:15
  • @MaryLayman Try switching just the initial read order for `rows` and `cols` (as in: `scanf("%d %d", &rows, &cols)` => `scanf("%d %d", &cols, &rows)`, and nothing else in the current code. If I'm reading the expected output correctly, the sample input confusingly gives the column size first, then the row size. I've updated my larger example, which should hopefully be accurate now. That said, the way your instructors / peers interchange *[rows / lines / columns / zones / vertical / horizontal / length / width]* when describing the problem is genuinely confusing, so I understand your frustration. – Oka Jan 19 '22 at 19:33
  • Not sure why this happened but they are saying "There is still the issue about accessing invalid places in memory. If you carefully look at the output in Codecast, you see that displayed array isn't the expected one. With data from provided example, you should get an array of 9 lines and 6 rows; while you get 9 rows. As I've said before, you're mixing lines and columns in your code." Updated code posted above. – Mary Layman Jan 19 '22 at 20:07
  • I really appreciate your help on this, Ive been doing it between working 40 hours a week and ive been stuck on it for 2 months with the holidays keeping me busy. Needless to say its infuriating. – Mary Layman Jan 19 '22 at 20:10
  • @MaryLayman Yeah, as I said, switch `&rows` and `&cols` in your very first `scanf` call - you want `scanf("%d %d", &cols, &rows)`. Also, watch out for `row <= nrows` in `allocateMatrix` - it should be `row < nrows`. – Oka Jan 19 '22 at 22:16
  • That did it! God that was awful. – Mary Layman Jan 20 '22 at 06:08
0
#include <stdio.h>
#include <stdlib.h>

int * allocateIntArray(int num);
int ** allocateIntStarArray(int num);

int main(void)
{
    int rown, coln,i,j;
    scanf("%d %d",&coln,&rown);
    int ** matrix = allocateIntStarArray(rown);
    for(i = 0; i < rown; i++){
        matrix[i] = allocateIntArray(coln);
    }

    for(i = 0; i < rown; i++){
        for(j = 0; j < coln; j++){
            scanf("%d",&matrix[i][j]);
        }
    }

    for(i = 0; i < rown; i++){
        for(j = 0; j < coln; j++){
            if(matrix[i][j]>1000){
                printf("[X]");
            }
            else{
                if(matrix[i][j]>=100){
                    printf("[*]");
                }
                else{
                    printf("[ ]");
                }
            }
        }
        printf("\n");
    }
    return 0;
}

int ** allocateIntStarArray(int num){
    int ** ptr = (int **) malloc(num * sizeof(int *));
    return ptr;
}

int * allocateIntArray(int num){
    int * ptr = (int *) malloc(num * sizeof(int));
    return ptr;
}
  • 1
    Rather than just posting working code, you should explain what's wrong with that in the questions and how your code fixes the issues. This is especially important when, like here, the asker is a student of the language. – Adrian Mole Mar 15 '23 at 13:35