How to free double pointer array in C? - c

I am trying to write a function to deallocate/free a double pointer in C. I have a struct that has an allocated char, and a double pointer to another struct I have. I need to create a function free of that memory. The structs I have:
typedef struct {
char *name; // allocated
int age;
int height;
} student;
typedef struct {
char *name; // allocated
char *location; // allocated
double area;
int population;
student **students; // fully allocated, NOT a reference array
} school;
I need to dispose the school function, I do have a function already but I'm not sure it's right
void dispose_school(school *r)
{
free(r->name);
free(r->location);
free(r->students[i]);
free(r);
}
If someone can shine some light, thank you!

You will have to iterate through all the elements of students, free them. And then after your loop completes, free students as well. See this code-
for(int i = 0;i < r->population;i++) {
free(r->students[i]->name); // do this if your students[i]->name is dynamically allocated
free(r->students[i]);
}
free(r->students);

Freeing the double pointer is straightforward. You have the pointers you allocate to hold each student, you have the struct you allocate to hold each students values (name, age, height), and finally you have the name you allocated storage for to hold the student's name. This specifies the order in which you would have needed to allocate the memory.
To free() a complex set of allocations, you simply take the order in reverse. Here you have some number of students (the population), so you would loop school->population times freeing:
the block of memory allocated to hold the students name;
the block of memory for the student struct holding the name, age and height; and finally
After the loop completes, then you free all of the pointers students.
That will free all the memory you have allocated for students, but you are not done. You will have also allocated storage for the school name, location and perhaps the school struct itself (unless you declared the base school struct with automatic-storage-duration).
Presuming you have allocated for the school struct and the rest of the pointers, you can write a free function that takes a pointer to the school and free all memory with something similar to the following (assuming you have used school->population as the student counter):
void destroy_school (school *s)
{
/* loop over each student and free storage for (1) name and (2) struct */
while (s->population--) {
free (s->students[s->population]->name);
free (s->students[s->population]);
}
/* free storage for student pointers */
free (s->students);
/* free school name, location and school struct */
free (s->name);
free (s->location);
free (s); /* remove if s has automatic-storage-duration */
}
A Short Example
Putting the pieces together to allocate and free() all the needed information, you can write a short program to do so. Splitting the code into functions to create_school(), add_student(), print_school() and destroy_school() keeps the logic straight. Using the population as your student counter, you could do something similar to:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char *name; // allocated
int age;
int height;
} student;
typedef struct {
char *name; // allocated
char *location; // allocated
double area;
int population;
student **students; // fully allocated, NOT a reference array
} school;
school *create_school (const char *name, const char *loc, double area)
{
school *s = NULL; /* create school ptr init NULL */
size_t len = strlen (name); /* get lenght of name parameter */
if (!(s = malloc (sizeof *s))) { /* allocate/VALIDATE struct school */
perror ("malloc-s"); /* handle error on allocation fail */
return NULL; /* return failure */
}
if (!(s->name = malloc (len + 1))) { /* allocate/VALIDATE for name */
perror ("malloc-s.name"); /* handle error on allocation fail */
free (s); /* clean up previous allocations */
return NULL;
}
memcpy (s->name, name, len + 1); /* copy name to s->name */
len = strlen (loc);
if (!(s->location = malloc (len + 1))) { /* allocate/VALIDATE for loc */
perror ("malloc-s.loc"); /* handle error */
free (s->name); /* clean up prior allocations */
free (s);
return NULL;
}
memcpy (s->location, loc, len + 1); /* copy location to school */
s->area = area; /* set area and pop */
s->population = 0;
s->students = NULL; /* init students ptr NULL */
return s; /* return allocated school struct */
}
int add_student (school *s, const char *name, int age, int height)
{
size_t len = strlen (name); /* length of name */
void *tmp;
/* reallocate/VALIDATE pointer for next student
* ALWAYS realloc() using a temporary pointer so if realloc() fails
* you don't overwrite your original pointer with NULL creating a
* memory leak.
*/
if (!(tmp = realloc (s->students, (s->population + 1) *
sizeof *s->students))) {
fprintf (stderr, "error: realloc student: %d\n", s->population);
return 0;
}
s->students = tmp; /* assign realloc'ed block to students */
/* allocate/VALIDATE storage for struct student */
if (!(s->students[s->population] = malloc (sizeof **s->students))) {
perror ("malloc-s->students[s->population");
return 0;
}
/* allocate/VALIDATE storage for student name */
if (!(s->students[s->population]->name = malloc (len + 1))) {
perror ("malloc-s->students[population]->name");
return 0;
} /* copy name to allocated space */
memcpy (s->students[s->population]->name, name, len + 1);
s->students[s->population]->age = age; /* assign age and height */
s->students[s->population]->height = height;
s->population += 1; /* increment pop count */
return 1; /* return success */
}
void print_school (school *s)
{
printf ("\nSchool : %s\n"
"Location : %s\n"
"Area : %.2f\n"
"N-Students : %d\n\n",
s->name, s->location, s->area, s->population);
for (int i = 0; i < s->population; i++) {
printf (" %02d %-16s %4d %4d\n",
i,
s->students[i]->name,
s->students[i]->age,
s->students[i]->height);
}
}
void destroy_school (school *s)
{
/* loop over each student and free storage for (1) name and (2) struct */
while (s->population--) {
free (s->students[s->population]->name);
free (s->students[s->population]);
}
/* free storage for student pointers */
free (s->students);
/* free school name, location and school struct */
free (s->name);
free (s->location);
free (s);
}
int main (void) {
/* allocate/initialize struct school, population == 0, students == NULL */
school *RidgeMontHigh = create_school ("Ridgemont High",
"Hollywood, CA", 2357.8);
if (!RidgeMontHigh) { /* validate creation of school */
return 1;
}
/* add students to school (can validate each, but population will do) */
add_student (RidgeMontHigh, "Mickey Mouse", 103, 48);
add_student (RidgeMontHigh, "Minnie Mouse", 99, 44);
add_student (RidgeMontHigh, "Pluto (the dog)", 97, 47);
add_student (RidgeMontHigh, "Daffy Duck", 102, 46);
if (RidgeMontHigh->population == 0) { /* validate non-zero student pop */
return 1;
}
print_school (RidgeMontHigh); /* print school followed by students */
destroy_school (RidgeMontHigh); /* free all memory involved */
}
Example Use/Output
Compiling and running the code will produce the following:
./bin/schoolpop
School : Ridgemont High
Location : Hollywood, CA
Area : 2357.80
N-Students : 4
00 Mickey Mouse 103 48
01 Minnie Mouse 99 44
02 Pluto (the dog) 97 47
03 Daffy Duck 102 46
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to ensure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/schoolpop
==18986== Memcheck, a memory error detector
==18986== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==18986== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==18986== Command: ./bin/schoolpop
==18986==
School : Ridgemont High
Location : Hollywood, CA
Area : 2357.80
N-Students : 4
00 Mickey Mouse 103 48
01 Minnie Mouse 99 44
02 Pluto (the dog) 97 47
03 Daffy Duck 102 46
==18986==
==18986== HEAP SUMMARY:
==18986== in use at exit: 0 bytes in 0 blocks
==18986== total heap usage: 16 allocs, 16 frees, 1,290 bytes allocated
==18986==
==18986== All heap blocks were freed -- no leaks are possible
==18986==
==18986== For lists of detected and suppressed errors, rerun with: -s
==18986== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.

Related

C Program - reading integers from a file and decoding secret message

Hey :) I need some help with my code, which I think is mostly correct but I am having trouble figuring out where I am going wrong.
#include <stdio.h>
#include <stdlib.h>
int num_count(FILE* ptr){
int count = 0;
int numHolder = 0;
while((fscanf(ptr, "%d", &numHolder)) == 1){
count++;
}
return count;
}
void load_nums(FILE* ptr, int *codedPtr, int ncount){
int number = 0;
ncount = ncount - 1;
for(int i = 0; i <= ncount; i++){
fscanf(ptr, "%d", &number);
printf("%d", number);
*(codedPtr + i) = number;
}
return;
}
void decode(int *codedPtr, int ncount, char *decodedPtr){
char temp;
ncount = ncount - 1;
for(int i = 0; i <= ncount; i++){
temp = ((*(codedPtr + i) + *(codedPtr + (ncount - i))) + '0');
*decodedPtr = temp;
decodedPtr++;
}
return;
}
int main(int argc, char *argv[]){
int *codedPtr;
char *decodedPtr;
FILE *fp;
if (argc == 2){
fp = fopen(argv[1], "r+");
}
if(argc <= 1){
printf("Invalid command line: cmd infile outfile\n");
}
int numCount = num_count(fp);
printf("%d", *codedPtr);
codedPtr = (int*)calloc(numCount, sizeof(int));
decodedPtr = (char*)calloc(numCount, sizeof(char));
load_nums(fp, codedPtr, numCount);
decode(codedPtr, numCount, decodedPtr);
printf("\n%s\n\n", decodedPtr);
fclose(fp);
return(0);
}
I added some print functions to trouble shoot, and during the load_nums function the printf functions continuously prints 0's, it is not reading in the correct integer values from the file pointed to.
Could any of you help particularly with the load_nums function? Thank you all and let me know if you need any extra information. "-6 -76 53 -34 32 79 142 55 177 78" is what is in the file pointed to.
You are making things much more complicated than they need to be. You are dynamically allocating storage for both codedPtr and decodedPtr, there is no need to make two passes through the file (one to count integers, and one to read after allocation). Your decode is much more complex than necessary and there is a logic error. Adding '0' (it's not necessary in this case -- though normally it is to convert a decimal digit to its ASCII character value)
To address load_nums, change the return type to int * and allocate for codedPtr within load_nums using realloc as needed to increase the size of your allocated block of memory. Then return a pointer to the allocated block of memory holding your int values. Pass ncount as a pointer (e.g. int *ncount) so you can update the value at that address with the number of integers read so that the count is available back in the calling function (main() here).
Approaching allocation in this manner reduces your file I/O to a single-pass through the file (and file I/O is one of the most time consuming operations) Further, you completely eliminate the need for a num_count() function.
Putting those pieces together, you could do:
/* read integers from fp, dynamically allocating storage as needed,
* return pointer to allocated block holding integers and make ncount
* available through update pointer value.
*/
int *load_nums (FILE* fp, int *ncount)
{
int *codedPtr, avail = 2; /* declare pointer & no. to track allocated ints */
*ncount = 0; /* zero the value at ncount */
/* allocate avail no. of int to codedPtr - validate EVERY allocation */
if (!(codedPtr = malloc (avail * sizeof *codedPtr))) {
perror ("malloc-codedPtr");
return NULL;
}
while (fscanf (fp, "%d", &codedPtr[*ncount]) == 1) { /* read each int */
if (++(*ncount) == avail) { /* check if realloc needed (count == avail) */
/* always realloc to a temporary pointer */
void *tmp = realloc (codedPtr, 2 * avail * sizeof *codedPtr);
if (!tmp) { /* validate that realloc succeeds */
perror ("realloc-codedPtr");
return codedPtr; /* original codedPtr vals available on failure */
}
codedPtr = tmp; /* assign new block of mem to codedPtr */
avail *= 2; /* update avail with no. of int allocated */
}
}
return codedPtr; /* return pointer to allocated block of memory */
}
You would call the function in main() as, codedPtr = load_nums (fp, &numCount). You can wrap it in an if(...) statement to determine whether the allocation and read succeeded or failed:
int *codedPtr = NULL, numCount = 0;
...
if (!(codedPtr = load_nums (fp, &numCount))) /* read file/validate */
return 1;
(there is no need to pass codedPtr from main(). You can further validate by checking numCount > 0 -- that is left to you)
For your decode function, simply set up the for loop use two loop variables to iterate from the beginning and end towards the middle. This greatly simplifies things, e.g.
void decode (int *codedPtr, int ncount, char *decodedPtr)
{
/* loop from ends to middle adding values, + '0' NOT required */
for (int i = 0, j = ncount - i - 1; i < j; i++, j--)
decodedPtr[i] = codedPtr[i] + codedPtr[j];
}
(i starts at the first integer value and j at the last. Don't use *(codePtr + i) instead use codePtr[i] -- though equivalent, index notation is easier to read)
In main() you can alternatively open the file provided as the first argument to your program or read from stdin by default if no argument is provided (this is the way many Linux utilities work). Adding a simple ternary is all you need. Whether you are reading input or allocating memory (or using any function that is necessary for the continued correct operation of your code), you cannot use that function correctly unless you check the return to determine if the operation succeeded or failed. Lesson: validate, validate, validate....
Putting it altogether, you could do:
#include <stdio.h>
#include <stdlib.h>
/* read integers from fp, dynamically allocating storage as needed,
* return pointer to allocated block holding integers and make ncount
* available through update pointer value.
*/
int *load_nums (FILE* fp, int *ncount)
{
int *codedPtr, avail = 2; /* declare pointer & no. to track allocated ints */
*ncount = 0; /* zero the value at ncount */
/* allocate avail no. of int to codedPtr - validate EVERY allocation */
if (!(codedPtr = malloc (avail * sizeof *codedPtr))) {
perror ("malloc-codedPtr");
return NULL;
}
while (fscanf (fp, "%d", &codedPtr[*ncount]) == 1) { /* read each int */
if (++(*ncount) == avail) { /* check if realloc needed (count == avail) */
/* always realloc to a temporary pointer */
void *tmp = realloc (codedPtr, 2 * avail * sizeof *codedPtr);
if (!tmp) { /* validate that realloc succeeds */
perror ("realloc-codedPtr");
return codedPtr; /* original codedPtr vals available on failure */
}
codedPtr = tmp; /* assign new block of mem to codedPtr */
avail *= 2; /* update avail with no. of int allocated */
}
}
return codedPtr; /* return pointer to allocated block of memory */
}
void decode (int *codedPtr, int ncount, char *decodedPtr)
{
/* loop from ends to middle adding values, + '0' NOT required */
for (int i = 0, j = ncount - i - 1; i < j; i++, j--)
decodedPtr[i] = codedPtr[i] + codedPtr[j];
}
int main(int argc, char *argv[]) {
int *codedPtr = NULL, numCount = 0;
char *decodedPtr = NULL;
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
if (!(codedPtr = load_nums (fp, &numCount))) /* read file/validate */
return 1;
if (fp != stdin) /* close file if not stdin */
fclose (fp);
if (!(decodedPtr = malloc (numCount + 1))) { /* allocate/validate */
perror ("malloc-decodedPtr"); /* don't forget room for '\0' */
return 1;
}
decode (codedPtr, numCount, decodedPtr); /* decode the message */
decodedPtr[numCount] = 0; /* nul-terminate */
puts (decodedPtr); /* output decoded message */
free (codedPtr); /* don't forge to free what you allocate */
free (decodedPtr);
}
Example Use/Output
Testing your program, you find the decoded message is "Hello", e.g
$ echo "-6 -76 53 -34 32 79 142 55 177 78" | ./bin/codedptr
Hello
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to ensure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ echo "-6 -76 53 -34 32 79 142 55 177 78" | valgrind ./bin/codedptr
==32184== Memcheck, a memory error detector
==32184== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==32184== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==32184== Command: ./bin/codedptr
==32184==
Hello
==32184==
==32184== HEAP SUMMARY:
==32184== in use at exit: 0 bytes in 0 blocks
==32184== total heap usage: 7 allocs, 7 frees, 5,251 bytes allocated
==32184==
==32184== All heap blocks were freed -- no leaks are possible
==32184==
==32184== For counts of detected and suppressed errors, rerun with: -v
==32184== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have further questions.

Reading 3 strings from a text file

I am having trouble reading 3 strings from one line, moving to the next one, and putting them in my struct correctly.
My text file looks like this:
- John Johnson Math
- Eric Smith Biology
- etc
I need to arrange students by class they have chosen. How should i read first string as name then blank, second as last name, and third by class and do that for every line, then store correctly in my struct? Here is what I have right now:
#include <stdio.h>
#include <stdlib.h>
#define MAX 30
typedef struct students {
char nameFirst[MAX];
char NameLast[MAX];
char choClass[MAX];
struct students *next;
} Students;
int main() {
FILE *in;
Students *first = NULL, *New = NULL, *last = NULL, *old = NULL, *current = NULL;
in = fopen("studenti.txt", "r");
if (in == NULL)
{
printf("Cannot open file!\n");
return 1;
}
while (i = fgetc(in) != (int)(EOF))
{
New = calloc(1, sizeof(Students));
if (first == NULL)
first = New;
else
last->next = New;
j = 0;
while (i != (int)(' '))
{
New->nameFirst[j++] = (char)i;
i = fgetc(in);
}
New->nameFirst[j] = '\0';
}
}
Continuing from the comment, why are you approaching this problem with a linked-list? You can use a linked list, but the overhead and code complexity is more than required. A straight forward solution is a simply dynamic array of type students. The benefits of an array or significant. Direct access to all student, simple sorting with a single call to qsort, simple additions, etc..
Don't get me wrong, if you assignment is to use a linked-list, by all means do, but you should really look at an dynamic array of type student, where you can realloc as needed to add as many students as you may like.
For example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum { MAXC = 30, MAXS = 60, MAXLN = 128 };
typedef struct students {
char first[MAXC];
char last[MAXC];
char class[MAXC];
} students;
int main (int argc, char **argv) {
students *array = NULL;
size_t i, idx = 0, maxs = MAXS;
char buf[MAXLN] = "";
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
/* allocate/validate maxs students in array */
if (!(array = malloc (maxs * sizeof *array))) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return 1;
}
while (fgets (buf, MAXLN, fp)) { /* read each line into buf */
/* separate in to struct members */
if (sscanf (buf, "- %s %s %s", array[idx].first,
array[idx].last, array[idx].class) != 3)
continue;
if (++idx == maxs) { /* check against current allocations */
void *tmp = realloc (array, (maxs + MAXS) * sizeof *array);
if (!tmp) { /* valdate realloc array succeeded */
fprintf (stderr, "error: realloc memory exhausted.\n");
break; /* or break and use existing data */
}
array = tmp; /* assign reallocated block to array */
maxs += MAXS; /* update current allocations size */
}
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
printf ("\nstudents:\n\n"); /* output formatted data */
for (i = 0; i < idx; i++) {
char tmp[2 * MAXC + 2] = "";
strcpy (tmp, array[i].last);
strcat (tmp, ", ");
strcat (tmp, array[i].first);
printf (" %-60s %s\n", tmp, array[i].class);
}
putchar ('\n');
free (array); /* free all allocated memory */
return 0;
}
(note: if your data file really doesn't begin each line with '- ', then just remove that from the sscanf format-string)
Example Input
$ cat dat/studentclass.txt
- John Johnson Math
- Eric Smith Biology
- etc.
Example Use/Output
$ ./bin/structstudents <dat/studentclass.txt
students:
Johnson, John Math
Smith, Eric Biology
Memory Error/Check
In any code your write that dynamically allocates memory, you have 2 responsibilites regarding any block of memory allocated: (1) always preserves a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you haven't written beyond/outside your allocated block of memory, attempted to read or base a jump on an unintitialized value and finally to confirm that you have freed all the memory you have allocated.
For Linux valgrind is the normal choice. e.g.
$ valgrind ./bin/structstudents <dat/studentclass.txt
==14062== Memcheck, a memory error detector
==14062== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==14062== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==14062== Command: ./bin/structstudents
==14062==
students:
Johnson, John Math
Smith, Eric Biology
==14062==
==14062== HEAP SUMMARY:
==14062== in use at exit: 0 bytes in 0 blocks
==14062== total heap usage: 1 allocs, 1 frees, 5,400 bytes allocated
==14062==
==14062== All heap blocks were freed -- no leaks are possible
==14062==
==14062== For counts of detected and suppressed errors, rerun with: -v
==14062== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)
Always confirm All heap blocks were freed -- no leaks are possible and equally important ERROR SUMMARY: 0 errors from 0 contexts.
Look it over an let me know if the array approach fits your needs and if you have any questions.
Sorting by class member
In order to sort the array of struct students by the class, the easiest way is to use the qsort function. It is the standard sorting function provided by the C library (include stdio.h). You can sort by any member of the students struct. You can even sort by class, and by name.
The only problem new programmers have with qsort is writing the compare function to pass to qsort in order to have it sort in the desired order. The compare function will receive a pointer to two elements in your array of struct student. The parameters passes are passed as void * (const void * actually). Just like any void pointer, you must cast it to the proper type before you can dereference it. (in this case students *) Therefore, you simply need a function that casts the void pointers to students * and passes the values to strcmp. Example:
int compare (const void *a, const void *b)
{
return strcmp (((students *)a)->class, ((students *)b)->class);
}
The only thing left is calling qsort (after fclose in the code):
qsort (array, idx, sizeof *array, compare);
Your output is then sorted by class.
If you then want to sort further by last name after sorting by class, instead of returning on the strcmp for class, test if the result is not equal to zero, and return that result. If the result of the strcmp on class is zero, then you simply return strcmp (((students *)a)->last, ((students *)b)->last); That simply sorts by class first, but if the class is the same, sorts further by last. For example:
int compare (const void *a, const void *b)
{
int r;
if ((r = strcmp (((students *)a)->class, ((students *)b)->class)))
return r;
return strcmp (((students *)a)->last, ((students *)b)->last);
}
Example Input
$ cat dat/studentclass.txt
- Wade Williams Biology
- John Johnson Math
- Eric Smith Biology
- etc.
Example Use/Output
$ ./bin/structstudents <dat/studentclass.txt
students:
Smith, Eric Biology
Williams, Wade Biology
Johnson, John Math
Take the time to learn qsort.

Reading numbers from file into an array

I'm trying to get the numbers from a File into the Array. I tried different approaches, and i came up with the following code:
int* readFileWithNumbers ()
{
FILE *file = fopen("number_list.txt", "r");
int*integers = malloc(sizeof(int) * 240);;
int i=0;
int num;
while(fscanf(file, "%d", &num) > 0) {
integers[i] = num;
i++;
}
fclose(file);
return integers;
}
My input file is in the format:
106
-18
248
-237
148
142
38
-189
59
-120
-219
-172
237
-257
-154
-267
-34
-292
239
-182
-243
-115
-26
-238
298
Can you please guide me, what I'm doing wrong here ?
Generally, you will want to pass a FILE * pointer, the array, a pointer to the index, and a pointer to the current allocation of memory max to your function. That way your function can read values from the file into the array, keep a count of the current index to compare against the current allocation size so you can realloc if the number of values exceed your current allocation. Finally, by virtual of using a pointer for both your index and your max allocation, the changes to either are immediately available back in your calling function (typically main() for these short examples.
Putting all those pieces together, you could do something similar to:
/* read array of unknown size int 'a' from 'fp' */
int *readarr (FILE *fp, int *a, size_t *idx, size_t *max)
{
int tmp;
if (!a) /* if a not allocated -- allocate/validate */
if (!(a = calloc (1, *max * sizeof *a))) {
fprintf (stderr, "readarr() virtual memory exhausted.\n");
exit (EXIT_FAILURE);
}
while (fscanf (fp, " %d", &tmp) == 1) { /* for each int in file */
if (*idx == *max) { /* check idx and max */
void *ap = realloc (a, 2 * *max * sizeof *a); /* realloc */
if (!ap) { /* validate realloc success */
fprintf (stderr, "realloc() error: memory exhausted.\n");
break; /* if failure, return with exising data */
}
a = ap; /* assign new mem block, zero new mem */
memset (ap + *max * sizeof *a, 0, *max * sizeof *a);
*max *= 2; /* update allocation size */
}
a[(*idx)++] = tmp; /* add int to array, update index */
}
return a; /* return array */
}
(note: the function will accept an allocated array, or NULL and allocate/reallocate as needed. However, the value for your array a cannot be a statically declared array due to the call to realloc that takes place. Further, you must assign the return from the function to your array in main(). If 'a' is NULL to begin with, or if realloc takes place, a new pointer address is returned.)
Zeroing the new memory after realloc is not required, but it can help prevent an inadvertent read from an uninitialized value if you later iterate over the array with an index greater than the last stored value index.
Also note the current reallocation scheme doubles the amount of memory each time it calls realloc. You can choose to add as little or as much as you need.
With that, a short example putting it all together could be:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXI 64
int *readarr ( FILE *fp, int *a, size_t *idx, size_t *max);
int main (int argc, char **argv) {
int *array = NULL; /* pointer to array */
size_t i = 0, n = 0, maxi = MAXI; /* index, initial elements */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* valildate file pointer */
fprintf (stderr, "error: file open failed. '%s'\n",
argc > 1 ? argv[1] : "stdin");
return 1;
}
array = readarr (fp, array, &n, &maxi); /* read values from file */
if (fp != stdin) fclose (fp); /* close file */
for (i = 0; i < n; i++) /* print array */
printf (" array[%3zu] : %d\n", i, array[i]);
free (array); /* free memory */
return 0;
}
/* read array of unknown size int 'a' from 'fp' */
int *readarr (FILE *fp, int *a, size_t *idx, size_t *max)
{
int tmp;
if (!a) /* if a not allocated -- allocate/validate */
if (!(a = calloc (1, *max * sizeof *a))) {
fprintf (stderr, "readarr() virtual memory exhausted.\n");
exit (EXIT_FAILURE);
}
while (fscanf (fp, " %d", &tmp) == 1) { /* for each int in file */
if (*idx == *max) { /* check idx and max */
void *ap = realloc (a, 2 * *max * sizeof *a); /* realloc */
if (!ap) { /* validate realloc success */
fprintf (stderr, "realloc() error: memory exhausted.\n");
break; /* if failure, return with exising data */
}
a = ap; /* assign new mem block, zero new mem */
memset (ap + *max * sizeof *a, 0, *max * sizeof *a);
*max *= 2; /* update allocation size */
}
a[(*idx)++] = tmp; /* add int to array, update index */
}
return a; /* return array */
}
Example Input 100 Int
$ cat dat/100int.txt
27086
29317
32736
...
16892
8270
6444
Example Use/Output
$ ./bin/fscanf_array_dyn dat/100int.txt
array[ 0] : 27086
array[ 1] : 29317
array[ 2] : 32736
array[ 3] : 3356
...
array[ 97] : 16892
array[ 98] : 8270
array[ 99] : 6444
Memory Error Check
In any code your write that dynamically allocates memory, you have 2 responsibilites regarding any block of memory allocated: (1) always preserves a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you haven't written beyond/outside your allocated block of memory, attempted to read or base a jump on an unintitialized value and finally to confirm that you have freed all the memory you have allocated.
For Linux valgrind is the normal choice. There are many subtle ways to misuse a new block of memory. Using a memory error checker allows you to identify any problems and validate proper use of of the memory you allocate rather than finding out a problem exists through a segfault. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/fscanf_array_dyn dat/100int.txt
==7273== Memcheck, a memory error detector
==7273== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==7273== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==7273== Command: ./bin/fscanf_array_dyn dat/100int.txt
==7273==
array[ 0] : 27086
array[ 1] : 29317
array[ 2] : 32736
...
array[ 97] : 16892
array[ 98] : 8270
array[ 99] : 6444
==7273==
==7273== HEAP SUMMARY:
==7273== in use at exit: 0 bytes in 0 blocks
==7273== total heap usage: 3 allocs, 3 frees, 1,336 bytes allocated
==7273==
==7273== All heap blocks were freed -- no leaks are possible
==7273==
==7273== For counts of detected and suppressed errors, rerun with: -v
==7273== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)
Always confirm All heap blocks were freed -- no leaks are possible and equally important ERROR SUMMARY: 0 errors from 0 contexts.
Look it all over and let me know if you have any further questions. Basic allocation and freeing of memory is the basis for much of C. It's better you take the time now to completely understand the code, then end up beating your head against your desk later... Good luck with your coding.

Dynamic array of Strings inside a Struct realloc

I have an Array of Strings inside a Struct that looks like this
#define ID_LEN 20
struct Person{
char name[ID_LEN];
int num_items;
char **items;
};
int main(){
int num_persons = 10;
struct Person *ptr[num_persons];
I start with an array of 10 persons. Initially all persons have 0 items so their list of items is an malloc(0).
for(int i = 0; i < num_persons; i++){
ptr[i] = (struct Person *) malloc(sizeof(struct Person) + 1);
ptr[i]->num_items = 0;
ptr[i]->items = malloc(0 * sizeof(char*));
}
At some point I want to name those persons and add them some items like this.
strcpy(ptr[0]->name, "John");
ptr[0]->num_items = 1;
ptr[0]->items = (char **)realloc(ptr[0]->items, ptr[0]->num_items * sizeof(char*));
strcpy(ptr[0]->items[0], "pencil");
printf("Name: %s\n", ptr[0]->name);
printf("Number of items: %d\n", ptr[0]->num_items);
for(int i = 0; i < ptr[0]->num_items; i++){
printf("Item %d is %s\n", i, ptr[0]->items[i]);
}
I'm getting segmentation fault: 11. I'm not sure if I have done realloc correctly or not.
You have several problems here
First your ID_LEN
nowhere do you check if by copying to name you surpass the ID_LEN
so instead of
strcpy(ptr[0]->name, "John");
use
strcpy_s(ptr[0]->name,sizeof(ptr[0]->name),"John or whatever"); // C11
You allocate
ptr[0]->items = (char **)realloc(ptr[0]->items, ptr[0]->num_items * sizeof(char*))
but
you should not cast return value of malloc/realloc (search on web for
explanation, it has been described ad nauseum)
when using realloc, you should first check the return value, it may fail.
char** tmp = realloc(ptr[0]->items, ptr[0]->num_items * sizeof(char*))
if (tmp != NULL)
{
ptr[0]->items = tmp;
}
else
{
abort();
}
The memory realloc returns is partly uninitialized (old pointers remain but
new ones are uninitialized. In your case you did not have any previous
pointers so that one items[0] is uninitialized.
so when you do
strcpy(ptr[0]->items[0], "pencil");
it will fail since items[0] is pointing to some arbitrary memory location.
after you realloc pointers you need initialize them to point to a
memory large enough to accommodate your string
E.g.
ptr[0]->items[0] = strdup("pencil"); // does malloc then copies string
It is not so efficient to use realloc everytime you need to add one new
item, instead allocate a bunch of items but set the ones you are not using
to NULL then keep track of how many are left, once they run out allocate
another bunch
All the problem in your code revolves around using memory which is not allocated.
Consider
ptr[0]->items = (char **)realloc(ptr[0]->items, ptr[0]->num_items * sizeof(char*));//line 1
strcpy(ptr[0]->items[0], "pencil");//line 2
In line 1 you have allocated memory to hold ptr[0]->num_items number of pointers to c strings.But you have not actually allocated memory to store c strings i.e. those pointers are not pointing to actual memory.
When in line 2 you try to access ptr[0]->items[0] its just char* there is no memory allocated to it.
I added the below line before line 2 and your code worked fine.
for(int i=0;i<ptr[0]->num_items;i++)
ptr[0]->items[i]=malloc(sizeof(char)*10);
In addition to what the other answers provide, there are several other considerations relevant to what you are doing. The first, don't 0 allocate anything. That is a holdover from days gone by and not necessary.
Next, following your static declaration of a variable length array of num_persons pointers to type struct Person, you must decide whether you will allocate storage for all your pointers at once, or whether you will allocate storage for the struct, only when adding a person. That will have implications when you split your code up into functions.
Since the remainder of your data structure will be allocated dynamically, you must validate each allocation succeeds before copying/initializing, whatever.
When you allocate your empty structure, take a close look at calloc instead of malloc. This is one instance where the default initialization provided by calloc can help.
The remainder of your task is just an accounting problem -- that is accounting for which members require allocation, the current state and size of each allocation, and then finally to free the memory you have allocated when it is no longer needed.
Take for example the code you might want for adding a person with an item to your array of pointers. To validate the addition, you will need to know that ptr is a valid pointer and whether you have reached your num_persons limit before you begin to allocate memory. You next must allocate storage for a struct Person and assign the address for that block to ptx[X] (for whatever index X you are working with).
Once allocated, you can initialize defaults (or if calloc was used, know that all values were initialized to 0/NULL). Following that you can copy name (limited to ID_LEN) and allocate pointers and storage for each item you wish to hold in your pointer-to-pointer-to-char* items (which you will handle similar to ptr accept that the pointers for items are allocated as well) Note there is no need to realloc (items,... at this point as this is the first time items is allocated.
If you put all of that together, you can come up with a function to add a person together with their first item similar to the following, where max is the maximum number of persons allowed, nm is the name, itm is the item, and idx is a pointer to the current index for that person:
/* add person with item */
struct person *addpwitem (struct person **ptr, int max, char *nm,
char *itm, int *idx)
{
if (!ptr || *idx + 1 == max) return NULL;
int i = *idx;
/* allocate storage for struct */
if (!(ptr[i] = calloc (1, sizeof **ptr))) {
fprintf (stderr, "error: ptr[%d], virtual memory exhausted.\n", i);
return NULL;
}
strncpy (ptr[i]->name, nm, ID_LEN); /* copy name */
/* allocate/validate pointer to char* ptr[i]->items */
if (!(ptr[i]->items =
malloc (ptr[i]->num_items + 1 * sizeof *(ptr[i]->items)))) {
fprintf (stderr, "error: items*, virtual memory exhausted.\n");
return NULL;
}
/* allocate/validate memory for ptr[num_items]->items[num_items] */
if (!(ptr[i]->items[ptr[i]->num_items] = strdup (itm))) {
fprintf (stderr, "error: items, virtual memory exhausted.\n");
return NULL;
}
ptr[i]->num_items++;
(*idx)++;
return ptr[i];
}
(note: you must assign the return of the function to ptr[X] in the calling function. Also note, C-style recommends all lower-case names, which is what you see above, leave CamelCase names to C++)
Once you have a person added, you will want the ability to add items to the list of items associated with that person. This is where realloc comes in to allow you to resize the number of pointers-to-items for that person. You can do something similar to add person, but the index you pass no longer needs to be a pointer as it will not be updated within the function. e.g.
/* add item to person at index 'i' */
struct person *additem (struct person **ptr, int i, char *itm)
{
if (!ptr) return NULL;
void *tmp;
/* allocate/realloc/validate pointer to char* ptr[i]->items */
if (!(tmp = realloc (ptr[i]->items,
(ptr[i]->num_items + 1) * sizeof *(ptr[i]->items)))) {
fprintf (stderr, "error: items*, virtual memory exhausted.\n");
return NULL;
}
ptr[i]->items = tmp; /* assign tmp on successful realloc */
/* allocate/validate memory for ptr[num_items]->items[num_items] */
if (!(ptr[i]->items[ptr[i]->num_items] = strdup (itm))) {
fprintf (stderr, "error: items, virtual memory exhausted.\n");
return NULL;
}
ptr[i]->num_items++;
return ptr[i];
}
You are now able to add both persons and items to the list, but need a way to iterate over all values in the list if you are going to make use of them. Whether searching, printing, or freeing memory, all iteration functions will have similar form. To print all persons and items you could do something similar to the following:
/* print the list of persons and items */
void prndetail (struct person **ptr, int idx)
{
if (!ptr || !*ptr) return;
int i, p;
for (p = 0; p < idx; p++) {
printf (" %-20s %2d", ptr[p]->name, ptr[p]->num_items);
for (i = 0; i < ptr[p]->num_items; i++)
printf ("%s%s", i ? ", " : " ", ptr[p]->items[i]);
putchar ('\n');
}
}
In any code your write that dynamically allocates memory, you have 2 responsibilites regarding any block of memory allocated: (1) always preserves a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed. When you are done with your list, you will need to free it. Similar to printing, it can be done as follows:
/* free all allocated memory */
void deletelist (struct person **ptr, int idx)
{
if (!ptr || !*ptr) return;
int i, p;
for (p = 0; p < idx; p++) {
for (i = 0; i < ptr[p]->num_items; i++)
free (ptr[p]->items[i]);
free (ptr[p]->items);
free (ptr[p]);
}
// free (ptr); /* if array of pointers allocated, not static */
}
That's really all there is to the accounting lesson. If you keep track of each part of the data structure you are using, managing the memory is straight forward. Putting all the pieces together in a short example, you could do something like:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum { NPER = 10, ID_LEN = 20 };
struct person {
char name[ID_LEN];
int num_items;
char **items;
};
/* add person + item - to add both person and item at once */
struct person *addpwitem (struct person **ptr, int max, char *nm,
char *itm, int *idx);
/* add item to existing person */
struct person *additem (struct person **ptr, int i, char *itm);
void prndetail (struct person **ptr, int idx);
void deletelist (struct person **ptr, int idx);
int main (void) {
int idx = 0;
struct person *ptr[NPER];
/* allocate storage for struct, add person + item */
if (!(ptr[idx] = addpwitem (ptr, NPER, "John", "pencils", &idx))) {
fprintf (stderr, "error: adding ptr[%d] failed.\n", idx);
return 1;
}
printf ("\nadded John:\n");
prndetail (ptr, idx); /* print contents of persons & items */
additem (ptr, idx - 1, "pens"); /* add next item */
printf ("\nadded item 'pens' for John:\n");
prndetail (ptr, idx); /* print contents of persons & items */
/* add next person + item */
if (!(ptr[idx] = addpwitem (ptr, NPER, "Martha", "paper", &idx))) {
fprintf (stderr, "error: adding ptr[%d] failed.\n", idx);
return 1;
}
printf ("\nadded Martha:\n");
prndetail (ptr, idx); /* print contents of persons & items */
deletelist (ptr, idx); /* free all allocated memory */
return 0;
}
/* add person with item */
struct person *addpwitem (struct person **ptr, int max, char *nm,
char *itm, int *idx)
{
if (!ptr || *idx + 1 == max) return NULL;
int i = *idx;
/* allocate storage for struct */
if (!(ptr[i] = calloc (1, sizeof **ptr))) {
fprintf (stderr, "error: ptr[%d], virtual memory exhausted.\n", i);
return NULL;
}
strncpy (ptr[i]->name, nm, ID_LEN); /* copy name */
/* allocate/validate pointer to char* ptr[i]->items */
if (!(ptr[i]->items =
malloc (ptr[i]->num_items + 1 * sizeof *(ptr[i]->items)))) {
fprintf (stderr, "error: items*, virtual memory exhausted.\n");
return NULL;
}
/* allocate/validate memory for ptr[num_items]->items[num_items] */
if (!(ptr[i]->items[ptr[i]->num_items] = strdup (itm))) {
fprintf (stderr, "error: items, virtual memory exhausted.\n");
return NULL;
}
ptr[i]->num_items++;
(*idx)++;
return ptr[i];
}
/* add item to person at index 'i' */
struct person *additem (struct person **ptr, int i, char *itm)
{
if (!ptr) return NULL;
void *tmp;
/* allocate/realloc/validate pointer to char* ptr[i]->items */
if (!(tmp = realloc (ptr[i]->items,
(ptr[i]->num_items + 1) * sizeof *(ptr[i]->items)))) {
fprintf (stderr, "error: items*, virtual memory exhausted.\n");
return NULL;
}
ptr[i]->items = tmp; /* assign tmp on successful realloc */
/* allocate/validate memory for ptr[num_items]->items[num_items] */
if (!(ptr[i]->items[ptr[i]->num_items] = strdup (itm))) {
fprintf (stderr, "error: items, virtual memory exhausted.\n");
return NULL;
}
ptr[i]->num_items++;
return ptr[i];
}
/* print the list of persons and items */
void prndetail (struct person **ptr, int idx)
{
if (!ptr || !*ptr) return;
int i, p;
for (p = 0; p < idx; p++) {
printf (" %-20s %2d", ptr[p]->name, ptr[p]->num_items);
for (i = 0; i < ptr[p]->num_items; i++)
printf ("%s%s", i ? ", " : " ", ptr[p]->items[i]);
putchar ('\n');
}
}
/* free all allocated memory */
void deletelist (struct person **ptr, int idx)
{
if (!ptr || !*ptr) return;
int i, p;
for (p = 0; p < idx; p++) {
for (i = 0; i < ptr[p]->num_items; i++)
free (ptr[p]->items[i]);
free (ptr[p]->items);
free (ptr[p]);
}
// free (ptr); /* if array of pointers allocated */
}
Example Use/Output
$ ./bin/struct_p2p2c
added John:
John 1 pencils
added item 'pens' for John:
John 2 pencils, pens
added Martha:
John 2 pencils, pens
Martha 1 paper
Memory Error Check
It is imperative that you use a memory error checking program to insure you haven't written beyond/outside your allocated block of memory, attempted to read or base a jump on an unintitialized value and finally to confirm that you have freed all the memory you have allocated.
It is simple to do:
$ valgrind ./bin/struct_p2p2c
==7618== Memcheck, a memory error detector
==7618== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==7618== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==7618== Command: ./bin/struct_p2p2c
==7618==
added John:
John 1 pencils
added item 'pens' for John:
John 2 pencils, pens
added Martha:
John 2 pencils, pens
Martha 1 paper
==7618==
==7618== HEAP SUMMARY:
==7618== in use at exit: 0 bytes in 0 blocks
==7618== total heap usage: 8 allocs, 8 frees, 115 bytes allocated
==7618==
==7618== All heap blocks were freed -- no leaks are possible
==7618==
==7618== For counts of detected and suppressed errors, rerun with: -v
==7618== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)
Always confirm All heap blocks were freed -- no leaks are possible and equally important ERROR SUMMARY: 0 errors from 0 contexts.
Look over this and the rest of the answers. There are many good suggestions between them. Let us know if you have further questions.
You are allocating for the list of items, but you are not allocating space for each individual item string. Your
strcpy(ptr[0]->items[0], "pencil");
will fail.

Using realloc on a 2D array c

I'm kinda new to C sorry if my questions is somewhat vague;
I need to use realloc on a 2D array without losing it's previous data, I have this function in my program to do it:
void modifyMatrix(int **iMat, int iRow, int iRow2, int iCol)
{
int i;
iMat = (int**)realloc(iMat, (iRow2)*sizeof(int*));
for(i=iRow; i<iRow2; i++)
{
iMat[i]=NULL;
}
for(i=0; i<iRow2; i++)
{
iMat[i]=(int*)realloc(iMat[i], (iCol)*sizeof(int));
}
}
Where iRow is the original size and iRow 2 & iCol are the new size and are all being captured elsewhere in the program.
Whenever I try to print the matrix I keep getting junk data or memory values on the rows and columns that are added, what am I doing wrong?
Let me know if you need the full code or any other questions to clarify, thanks in advance!
Edit:
Below you can see the code I use to create the Matrix
My bad, I think I should've added that the Matrix is already created elsewhere in the program, with this function I'm just trying to modify the dimensions, thanks for the quick response btw!, below you can find the function with which I'm creating the array
void createMatrix(int ***iMat, int iRow, int iCol)
{
int **iRow2 = (int**)calloc(iRow, sizeof(int*));
int i;
for (i=0; i<iRow; i++)
{
iRow2[i] = (int*)calloc(iCol, sizeof(int));
}
*iMat=iRow2;
}
Also, I can only use the array I've already created to do this, I can't create an temp one (which I know would be the easy way to do it)
In c the variables are passed by value, because of this the iMat inside the modifyMatrix() is not modifying the one in the caller function.
You need to pass the address of iMat instead
void modifyMatrix(int ***iMat, int iRow, int iRow2, int iCol)
{
int i;
int **safe;
safe = realloc(*iMat, iRow2 * sizeof(int *));
if (safe == NULL)
return;
*iMat = safe;
for (i = 0 ; i < iRow ; i++)
{
int *keep_old_pointer;
keep_old_pointer = realloc(safe[i], iCol * sizeof(int));
if (keep_old_pointer == NULL)
do_something_allocation_failed();
safe[i] = keep_old_pointer;
}
for (int i = iRow ; i < iRow2 ; ++i)
safe[i] = malloc(iCol * sizeof(int));
}
Also, don't assign NULL to every element and then try to realloc() because if realloc() makes sense in this situation then you are overwriting the pointers with NULL without freeing them.
And don't overwrite the realloc()ed pointer before checking if the allocation was succesfull, because if it fails you wont be able to free the previous pointer because you would have lost reference to it, causing a memory leak.
When you are passing an array of pointers to a function to realloc, you basically have 2 choices; (1) pass the address of the array to the function (i.e. &array) as the parameter, meaning your function definition will be reallocfoo (int ***array, size_t* size) or (2) assign the return of the function in the calling routine. (e.g. array = reallocfoo (array, &size);)
Since you have already been given answers for (1), let's look at how you would implement and use (2). Note: there is no need to make your function the type of the array, it is just returning a memory address, so making use of a generic void pointer is fine. For example:
void *xrealloc2 (void **memptr, size_t *n)
{
void *tmp = realloc (memptr, *n * 2 * sizeof tmp);
if (!tmp) {
fprintf (stderr, "%s() error: virtual memory exhausted.\n", __func__);
return NULL;
}
memptr = tmp;
memset (memptr + *n, 0, *n * sizeof tmp);
*n *= 2;
return memptr;
}
Also note since you are reallocating an array of pointers, there is no need to pass the type size (a pointer is a pointer is a pointer -- in all the cases we care about here). Putting this to work, since you are not passing the address of your array, you will need to assign the return to complete the reallocation. (much as you did in your code above) e.g.:
if (ridx == rmax) /* if realloc is needed */
ia = xrealloc2 ((void **)ia, &rmax);
Note: the current number of pointers (rmax) is passed as a pointer so its value can be updated to twice current in the reallocation function. (so when you run out next time, you can realloc based on the correct updated current number). Putting all the pieces together, you get a short example that just forces reallocation twice. Additionally, the original allocation is placed in a function as well to keep the main body of code tidy and the return checks for the allocation in the function. (you can decide how you handle memory exhaustion -- NULL return or exit, examples of both are shown in the two functions)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define RMAX 2
#define COLS 5
void *xcalloc (size_t n, size_t s);
void *xrealloc2 (void **memptr, size_t *n);
int main (void) {
int **ia = NULL;
size_t rmax = RMAX;
size_t rows = 0;
size_t ridx = 0, cidx = 0;
srand (2275311); /* arbitrary repeatable seed */
ia = xcalloc (RMAX, sizeof *ia);
/* intentionally force reallocation */
while (ridx < 3 * RMAX) {
ia[ridx] = xcalloc (COLS, sizeof **ia);
for (cidx = 0; cidx < COLS; cidx++)
ia[ridx][cidx] = rand () % 1000 + 1;
ridx++;
if (ridx == rmax)
ia = xrealloc2 ((void **)ia, &rmax);
}
rows = ridx;
printf ("\n the reallocated 2D array elements are:\n\n");
for (ridx = 0; ridx < rows; ridx++) {
for (cidx = 0; cidx < COLS; cidx++)
printf (" %4d", ia[ridx][cidx]);
putchar ('\n');
}
putchar ('\n');
for (ridx = 0; ridx < rows; ridx++)
free (ia[ridx]);
free (ia);
return 0;
}
/** xcalloc allocates memory using calloc and validates the return.
* xcalloc allocates memory and reports an error if the value is
* null, returning a memory address only if the value is nonzero
* freeing the caller of validating within the body of code.
*/
void *xcalloc (size_t n, size_t s)
{
register void *memptr = calloc (n, s);
if (memptr == 0)
{
fprintf (stderr, "%s() error: virtual memory exhausted.\n", __func__);
exit (EXIT_FAILURE);
}
return memptr;
}
/* realloc array of pointers ('memptr') to twice current
* number of pointer ('*nptrs'). Note: 'nptrs' is a pointer
* to the current number so that its updated value is preserved.
* no pointer size is required as it is known (simply the size
* of a pointer
*/
void *xrealloc2 (void **memptr, size_t *n)
{
void *tmp = realloc (memptr, *n * 2 * sizeof tmp);
#ifdef DEBUG
printf ("\n reallocating %zu to %zu\n", *n, *n * 2);
#endif
if (!tmp) {
fprintf (stderr, "%s() error: virtual memory exhausted.\n", __func__);
return NULL;
}
memptr = tmp;
memset (memptr + *n, 0, *n * sizeof tmp);
*n *= 2;
return memptr;
}
After you compile the code and run it, it will confirm that instead of just the maximum 2 rows (pointers) originally allocated, reallocation occurs twice increasing that number to 8 (e.g. 2->4->8) so all 6 rows of integers assigned are properly allocated:
the reallocated 2D array elements are:
155 573 760 410 956
553 271 624 625 934
259 291 811 161 185
756 211 16 6 449
124 869 353 210 317
310 181 897 866 831
If you have any questions, let me know. Don't forget, always run any code that allocates or reallocated through valgrind (or similar memory checker) to insure your memory use is correct and that your free all memory you allocate.
==29608== Memcheck, a memory error detector
==29608== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==29608== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==29608== Command: ./bin/realloc2d
==29608==
the reallocated 2D array elements are:
<snip>
==29608==
==29608== HEAP SUMMARY:
==29608== in use at exit: 0 bytes in 0 blocks
==29608== total heap usage: 9 allocs, 9 frees, 232 bytes allocated
==29608==
==29608== All heap blocks were freed -- no leaks are possible
==29608==
==29608== For counts of detected and suppressed errors, rerun with: -v
==29608== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
You can compile the code above with the -DDEBUG flag to have it print to stdout each time it reallocates and provide the current count of pointers allocated. Good luck.
On success, realloc frees the pointer you pass to it and returns a pointer to the newly allocated memory. You're getting "junk values" because dereferencing a pointer to freed memory is undefined behavior.
It's not pretty, but the way to fix this (as another answer pointed out) is to pass a triple pointer (int***). That way the function can modify the original value of the pointer. This is how you simulate reference semantics in C, which is a strictly "pass by value" language.
void modifyMatrix(int ***iMat, int iRow, int iRow2, int iCol)
{
int i;
int **newMatrix = realloc(*iMat, iRow2 * sizeof(int*));
if (newMatrix == NULL) {
/* handle realloc error here */
}
else {
*iMat = newMatrix; /* assign pointer to the new memory */
}
for(i=iRow; i<iRow2; i++)
{
(*iMat)[i]=NULL;
}
for(i = 0; i < iRow2; i++)
{
int* newRow = realloc((*iMat)[i], (iCol)*sizeof(int));
if (newRow == NULL) {
/* handle realloc error here */
}
else {
(*iMat)[i] = newRow;
}
}
}
You've also got to add some error checking. If realloc fails and returns a NULL pointer, you've just created a memory leak: you no longer have the previous value of the pointer.
Note that I removed all of your casts. It's bad practice to cast the return of malloc and friends in C.

Resources