+ 1

fully dynamic arrays

I am trying to build a fully dynamic 2D array in C, where the number of rows and columns is determined at runtime. I used calloc to allocate each row, so I expected that all elements would be initialized to 0. When I pass my fully dynamic array (int **grades) to a printing function that expects int **, the output is indeed all zeros, as expected. However, when I try to pass the same dynamic array to a function declared as void print_grades(int grades[][NUM_OF_CLASSES], int n_rows, int n_cols), I expected it to decay to int ** and also print zeros, but instead I get strange, seemingly garbage values. Why does the output differ in this case? How does C handle the difference between a pointer-to-pointer (int **) and a 2D array (int[][NUM]) when passing them to functions? https://www.sololearn.com/en/compiler-playground/c6bN8FuNgQXV

16th Sep 2025, 10:30 AM
Dareen Moughrabi
Dareen Moughrabi - avatar
4 odpowiedzi
+ 2
Dareen Moughrabi your problem is: int **grades (rows separately malloced) ≠ int grades[][NUM] (contiguous 2D array). Passing int ** to int grades[][NUM] causes undefined behavior (garbage values) Why It happens: In C, int ** and int [][NUM] are different types. int grades[][NUM] is treated as a pointer to arrays of NUM integers (int (*)[NUM]) and expects rows to be contiguous. Your dynamic array has separate malloc blocks per row, so grades[i][j] accesses wrong memory. You can fix it by keeping int ** and change function: void print_grades(int **grades, int n_rows, int n_cols) { for (int i = 0; i < n_rows; i++) { for (int j = 0; j < n_cols; j++) printf("%d ", grades[i][j]); printf("\n"); } } Call it like: print_grades(grades, num_of_students, num_of_courses); Or allocate contiguous array: int (*grades)[NUM_OF_CLASSES] = malloc(sizeof(int[NUM_OF_STUDENTS][NUM_OF_CLASSES])); for (int i = 0; i < NUM_OF_STUDENTS; i++) for (int j = 0; j < NUM_OF_CLASSES; j++) grades[i][j] = 0; print_grades(grades, NUM_OF_STUDENTS, NUM_OF_CLASSES); free(grades); Here grades is a pointer to an array of NUM_OF_CLASSES integers: int (*)[NUM_OF_CLASSES]. Memory is contiguous, so grades[i][j] works as expected. Remember that always match allocation type with function parameter type. Good Luck!
16th Sep 2025, 1:07 PM
Riyadh JS
Riyadh JS - avatar
+ 1
After reading about this more i found that the int [][num] is supposed to decay to a pointer to an array of Num ints , the only assumption that i could make that maybe i am moving such that each step is sizeof num * size of int instead of the correct spacing
16th Sep 2025, 10:43 AM
Dareen Moughrabi
Dareen Moughrabi - avatar
+ 1
Thanks kind of guessed thats the case after understanding what it decays to yet wanted to be sure , this was phrased nicely thanks ,cleared a lot of confusion Riyadh JS
16th Sep 2025, 1:12 PM
Dareen Moughrabi
Dareen Moughrabi - avatar
+ 1
Dareen Moughrabi Exactly! int[][NUM] decays to int (*)[NUM], a pointer to a row of NUM ints. The key is that rows must be contiguous in memory. With int ** each row is separately allocated, so indexing grades[i][j] in a int[][NUM] function reads the wrong addresses. That’s why your spacing assumption fails — the compiler expects NUM * sizeof(int) stride between rows, but your rows aren’t contiguous.
16th Sep 2025, 1:14 PM
Riyadh JS
Riyadh JS - avatar