Ok, it is obvious, you need help getting over the first part of C using/referencing dynamically allocated memory, so let's look at the basics. But, before we even look at code, let's talk about how you are going to compile it. You need to enable warnings when you compile, and then eliminate all warnings before you consider your code done. The warnings are there to help you. At minimum you want to enable -Wall -Wextra
when using gcc
, you can check for the equivalent with other compilers. Your compile string will look similar to:
gcc -Wall -Wextra -g -o square square.c
The -g
will generate symbols for debugging with gcc
. When you are done debugging you will want to replace -g
with your desired optimization level 0
(zero, the default), or 1, 2, 3, fast
. You specify the option with a capital -O
(Oh, not zero) (e.g. -O3
or -Ofast
(gcc 4.6 and newer)).
Now knowing how you will build your code, let's look at how to write it. First, in C, there are no 2D arrays. There are only ways to simulate indexing for 2D arrays. When you are using an array of pointers to type (e.g. Square **matrix
), the way to simulate a 2D array is to declare and allocate an array of pointers to type Square
:
Square **matrix = NULL;
matrix = calloc (ROWS, sizeof *matrix);
This will declare ROWS
number of pointers to matrix of type Square*
. Then, for each pointer, you allocate a block of memory to hold the desired number of struct Square
:
for (row = 0; row < ROWS; row++)
{
matrix[row] = malloc (COLS * sizeof **matrix);
...
}
You have now declared ROWS
number of pointers to arrays of COLS
number type Square
. This allows you to simulate a 2D array even though there is no requirement that any of the arrays are contiguous in memory.
Note: calloc
was used to allocate the pointers. calloc
both allocates and initializes to NULL
(or 0
). Every time you allocate a block of memory, you need to validate that the allocation was successful. You do this by checking the return from malloc
, calloc
, or realloc
-- every time. E.g.:
matrix = calloc (ROWS, sizeof *matrix);
if (!matrix) {
fprintf (stderr, "%s() error: virtual memory exhausted.\n", __func__);
exit (EXIT_FAILURE);
}
You can create a helper function to allocate/check and return a pointer to the new memory block to keep your code tidy.
Note2: after you allocate and validate a block of memory, it is your responsibility to (1.) preserve a pointer to the starting address of that block of memory so (2.) that block of memory can be freed when you no longer need it.
For your 1D
array, things are much simpler, you simply allocate storage for the number of type Square
you need. E.g.:
matrix = calloc (ROWS * COLS, sizeof *matrix);
You can simulate a 2D array from this allocation as well by simply creating logic that allows 2D array type indexing to refer to any location within your contiguous array. (it looks like matrix[row*ROWS+col]
where 0 <= row < ROWS
). Though not part of the example below, if you did want to simulate a 2D array from your matrix2
, then for sake of completeness you could implement the print numbers part as follows:
void print1DMatrixAs2DNumbers (Square *matrix)
{
if (!matrix) return;
int row, col;
printf ("\n simulated 2D array numbers are:\n\n");
for (row = 0; row < ROWS; row++) {
for (col = 0; col < COLS; col++)
printf (" %4d", matrix[row * ROWS + col].i);
putchar ('\n');
}
putchar ('\n');
}
What does (1.) in Note2 mean? It means you must pay particular attention to not do something like the following:
while (1) {
...
matrix++;
}
When you are done with the loop, what points to the start of the block of memory you originally allocated? Nothing. If you don't have the starting address, that memory can no longer be freed. If you find yourself in that situation, create a pointer to matrix and use that in the loop. (e.g. Square *p = matrix;
then while (1) {... p++;}
)
Those are the basics behind doing what it is you are trying to do. The remainder is just syntax and sorting out your program logic. Yes, I know you need help there too.
The following is an example implimenting your code. It is intended to show you how to put the pieces together, not do it for you. C is an extremely elegant and flexible low-level language that provides power and control over the machine that few languages, other than assembler, can provide. There is learning curve associated with any language, C is no different. However, unlike other higher level languages, C gives you the flexibility to do just about anything the syntax allows. There are no protections built into functions protecting against writing beyond the end of arrays or writing to areas of memory after an attempted allocation fails. Learning C, the responsibility is on you to learn C to that level, to protect against the forseeable problems than come for the power you are given.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define ROWS 8
#define COLS 8
typedef struct square
{
int i;
char c;
} Square;
Square **initializeMatrix (void);
void printMatrixNumbers (Square**);
void printMatrixLetters (Square**);
void shuffleMatrix (Square**);
Square *initialize1DMatrix (void);
void print1DMatrixLetters (Square*);
void print1DMatrixNumbers (Square*);
void shuffle1DMatrix (Square*);
int main (void)
{
srand (time (NULL));
Square **matrix = initializeMatrix();
while (1)
{
int choice;
printf ("\nPrint which set?: \n\n"
" 1. letters\n"
" 2. numbers\n"
" 3. shuffle matrix\n"
" 4. move to 1D matrix\n"
" > ");
scanf ("%d", &choice);
if(choice == 1) printMatrixLetters (matrix);
else if(choice == 2) printMatrixNumbers (matrix);
else if(choice == 3) shuffleMatrix (matrix);
else if(choice == 4) break;
else printf("Didn't understand that input. Try again\n");
}
Square *matrix2 = initialize1DMatrix();
printf ("\nNow for the 1D array:\n\n");
while (1)
{
int choice;
printf ("\nPrint which set?: \n\n"
" 1. letters\n"
" 2. numbers\n"
" 3. shuffle matrix\n"
" 4. quit\n"
" > ");
scanf ("%d", &choice);
if(choice == 1) print1DMatrixLetters (matrix2);
else if(choice == 2) print1DMatrixNumbers (matrix2);
else if(choice == 3) shuffle1DMatrix (matrix2);
else if(choice == 4) break;
else printf("Didn't understand that input. Try again\n");
}
/* free simulated 2D matrix */
size_t i;
for (i = 0; i < ROWS; i++)
free (matrix[i]);
free (matrix);
/* free matrix2 */
free (matrix2);
return 0;
}
Square **initializeMatrix ()
{
/* unless you can't have a null-terminator, this is fine */
char letters[] = "abcdefghijklmnopqrstuvwxyz";
int row, col;
Square **matrix = NULL;
/* allocate ROWS number of pointers to struct Square
* 'calloc' allocates and initializes NULL, you must then
* validate your allocation by checking the return.
*/
matrix = calloc (ROWS, sizeof *matrix);
if (!matrix) {
fprintf (stderr, "%s() error: virtual memory exhausted.\n", __func__);
exit (EXIT_FAILURE);
}
/* allocate COLS number of struct Square and validate */
for (row = 0; row < ROWS; row++)
{
matrix[row] = malloc (COLS * sizeof **matrix);
if (!matrix) {
fprintf (stderr, "%s() error: virtual memory exhausted.\n",
__func__);
exit (EXIT_FAILURE);
}
for (col = 0; col < COLS; col++)
{
/* fill i with random number between 0 - 999 */
matrix[row][col].i = rand() % 1000;
/* fill c with random letter 'a-z' */
matrix[row][col].c = letters[rand() % 26];
}
}
return matrix;
}
Square *initialize1DMatrix ()
{
/* unless you can't have a null-terminator, this is fine */
char letters[] = "abcdefghijklmnopqrstuvwxyz";
int i;
Square *matrix = NULL;
/* allocate memory for ROWS * COLS struct Square
* and validate
*/
matrix = calloc (ROWS * COLS, sizeof *matrix);
if (!matrix) {
fprintf (stderr, "%s() error: virtual memory exhausted.\n",
__func__);
exit (EXIT_FAILURE);
}
for (i = 0; i < ROWS * COLS; i++)
{
/* fill i with random number between 0 - 999 */
matrix[i].i = rand() % 1000;
/* fill c with random letter 'a-z' */
matrix[i].c = letters[rand() % 26];
}
return matrix;
}
void printMatrixNumbers (Square **matrix)
{
if (!matrix) return;
int row, col;
printf ("\n simulated 2D array numbers are:\n\n");
for (row = 0; row < ROWS; row++) {
for (col = 0; col < COLS; col++)
printf (" %4d", matrix[row][col].i);
putchar ('\n');
}
putchar ('\n');
}
void printMatrixLetters (Square **matrix)
{
if (!matrix) return;
int row, col;
printf ("\n simulated 2D array letters are:\n\n");
for (row = 0; row < ROWS; row++) {
for (col = 0; col < COLS; col++)
printf (" %4c", matrix[row][col].c);
putchar ('\n');
}
putchar ('\n');
}
void shuffleMatrix (Square **matrix)
{
if (!matrix) return;
fprintf (stderr, "%s() warning: not yet implemented.\n", __func__);
}
void print1DMatrixNumbers (Square *matrix)
{
if (!matrix) return;
size_t i;
printf ("\n matrix2 numbers are:\n\n");
for (i = 0; i < ROWS * COLS; i++)
printf (" matrix2[%2zu] : %4d\n", i, matrix[i].i);
putchar ('\n');
}
void print1DMatrixLetters (Square *matrix)
{
if (!matrix) return;
size_t i;
printf ("\n matrix2 letters are:\n\n");
for (i = 0; i < ROWS * COLS; i++)
printf (" matrix2[%2zu] : %c\n", i, matrix[i].c);
putchar ('\n');
}
void shuffle1DMatrix (Square *matrix)
{
if (!matrix) return;
fprintf (stderr, "%s() warning: not yet implemented.\n", __func__);
}
Compile
gcc -Wall -Wextra -o bin/square square.c
Use/Output
$ ./bin/square
Print which set?:
1. letters
2. numbers
3. shuffle matrix
4. move to 1D matrix
> 2
simulated 2D array numbers are:
180 468 335 205 480 606 40 276
360 581 824 731 59 827 573 708
837 18 557 109 234 348 255 54
527 479 60 174 891 799 868 922
35 230 867 335 406 375 660 629
416 243 670 948 123 377 607 48
943 291 617 263 14 37 419 565
126 664 578 357 712 44 738 17
Print which set?:
1. letters
2. numbers
3. shuffle matrix
4. move to 1D matrix
> 1
simulated 2D array letters are:
l a f q l e x y
x p y w p w c t
u c h g l q a t
n m a p v s f l
i d l l x j r z
q u t j x p p e
s o s e c q s c
d c k p p p j c
Print which set?:
1. letters
2. numbers
3. shuffle matrix
4. move to 1D matrix
> 4
Now for the 1D array:
Print which set?:
1. letters
2. numbers
3. shuffle matrix
4. quit
> 2
matrix2 numbers are:
matrix2[ 0] : 371
matrix2[ 1] : 844
matrix2[ 2] : 287
matrix2[ 3] : 69
matrix2[ 4] : 98
matrix2[ 5] : 327
matrix2[ 6] : 125
matrix2[ 7] : 706
matrix2[ 8] : 54
matrix2[ 9] : 400
...
matrix2[59] : 504
matrix2[60] : 655
matrix2[61] : 604
matrix2[62] : 583
matrix2[63] : 597
Print which set?:
1. letters
2. numbers
3. shuffle matrix
4. quit
> 1
matrix2 letters are:
matrix2[ 0] : f
matrix2[ 1] : h
matrix2[ 2] : u
matrix2[ 3] : r
matrix2[ 4] : a
matrix2[ 5] : u
matrix2[ 6] : b
matrix2[ 7] : f
matrix2[ 8] : y
matrix2[ 9] : e
...
matrix2[60] : a
matrix2[61] : u
matrix2[62] : z
matrix2[63] : h
Print which set?:
1. letters
2. numbers
3. shuffle matrix
4. quit
> 4
Memory Leak/Error Check
In any code your write that dynamically allocates memory, it is imperative that you use a memory error checking program. For Linux valgrind
is the normal choice. There are so many subtle ways to misuse a block of memory that can cause real problems, the is no excuse not to do it. There are similar memory checkers for every platform. They are simple to use. Just run your program through it.
$ valgrind ./bin/square
==9866== Memcheck, a memory error detector
==9866== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==9866== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==9866== Command: ./bin/square
==9866==
Print which set?:
1. letters
2. numbers
3. shuffle matrix
4. move to 1D matrix
> 2
simulated 2D array numbers are:
299 713 762 909 504 705 697 846
600 735 239 2 870 258 998 155
819 88 649 688 921 890 3 657
418 52 761 739 17 612 159 664
340 264 454 848 49 345 179 359
747 958 523 845 398 259 928 240
380 963 808 561 253 614 613 733
442 222 740 209 228 697 743 777
<snip>
Print which set?:
1. letters
2. numbers
3. shuffle matrix
4. quit
> 4
==9866==
==9866== HEAP SUMMARY:
==9866== in use at exit: 0 bytes in 0 blocks
==9866== total heap usage: 10 allocs, 10 frees, 1,088 bytes allocated
==9866==
==9866== All heap blocks were freed -- no leaks are possible
==9866==
==9866== For counts of detected and suppressed errors, rerun with: -v
==9866== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)