How to properly free char** in C - c

I have really been confused about this 2D char array
char **arg = malloc(sizeof(char*) * argc)
for (int i = 0; i < argc; i++)
arg[i] = malloc(sizeof(char) * size)
...
...
Now suppose after a series of operations, I forget the variable argc, how can I free those memory?
Can I do something like this? Is this absolutely correct under all circumstances?
char **tmp = arg;
while (*tmp != NULL){
free(*tmp);
tmp++;
}
free(arg);

No
while(*tmp != NULL){
you may reach above a point where you will dereference memory which hasn't been assigned to and trigger undefined behaviour.
Or as suggested you can explicitly assign a NULL to the last allocated pointer, and in that case it will work.

As others have said, the problems with freeing in a loop as shown is that an extra item (argc + 1) needs to be allocated and it has to be set to NULL. An alternative technique is to first allocate the space for the pointers as you have done
char **arg = malloc(sizeof(char*) * argc)
Then, if you know all the subsequent items are the same size, allocate it in one huge block and set the rest of the elements at spaced offsets
arg[0] = malloc(sizeof(char) * size * argc);
for (int i = 1; i < argc; ++i)
arg[i] = arg[i - 1] + size;
Freeing the space is a doddle: no need to even remember argc
free(arg[0]); /* this will free the memory used by all the elements */
free(arg);
The big disadvantages to this technique is that if any of the array elements overrun, it will corrupt the next item. This cannot be detected so easily with a heap check unless it is the last item.

if you define your char * array like this:
char **arg = calloc( 1, sizeof( char * ) * argc );
you can be sure that every undefined pointer will be equal to NULL
and then you can use the while loop almost like you suggested:
char *tmp = *arg;
while ( tmp != NULL ) {
free( tmp );
tmp++;
}
free(arg);

Related

How can I free an array of pointers where each pointer points to an address of a string which I allocated inside of a function?

How can I free an array of pointers where each pointer points to an address of a string which I allocated inside of a function?
I created a simple array of pointers, *pointers[], inside my main and I'm passing it to readline which returns the amount of lines I have read.
The purpose of this is to store all the lines of the input separated by '\n'. Each line is stored in a pointer in the array of pointers.
So after readline is called and it returns, I can printf("%s", pointers[0]) which will show the first line the user typed in.
The way I assign a line to each pointer in my array is inside readline by allocating a char pointer, *p, to have MAXLENGTH sizes and passing the current address of p to the respective pointer. After each assignment is done, I jump to the next free address of p.
Finally, my question is if I have to free (and how) my array of pointers after the readline routine is complete and I have printed all the lines stored.
I'll leave the two functions, main and readline, here for you.
#define NUMOFLINES 5
#define MAXLENGTH 1000
void main(void) {
char *pointers[NUMOFLINES]; // Array of pointers to string
int nlines; // Number of pointers read
nlines = readlines(pointers);
writelines(pointers, nlines);
/* FAILED ATTEMPT TO FREE THE ARRAY OF POINTERS */
for(int i = 0 ; i < nlines ; i++)
free(pointers[i]);
}
int readlines(char *pointers[]) {
char line[MAXLENGTH];
char *p;
int iptr, len;
/* ALLOCATE P */
p = malloc(sizeof(char) * MAXLENGTH);
for(iptr = 0 ; iptr < NUMOFLINES && (len = get_line(line)) > 0 ; iptr++)
{
strcpy(p, line); // copy the line to the array, ending in len-th position
pointers[iptr] = p; // Pass current adress 'p' to the respectively index of pointers
p += len+1; // Next free adress in array p
}
return iptr;
}
As you can see I tried to free by going through each pointer and calling free(pointer[i], but all I get is
malloc(): corrupted top size
Aborted (core dumped)
Do I need to call free?
Thanks in advance.
You are calling malloc() only 1 time, but you are calling free() multiple times. Every char* in your array points within that single malloc()'ed memory block. You can only call free() 1 time on the starting address of that block. Calling free() on addresses inside the block is undefined behavior.
Also, the memory block you are allocating with malloc() is not nearly large enough, assuming each string that get_line() outputs can be up to MAXLENGTH characters. You are only allocating enough memory for the maximum length of 1 single line, so as soon as your reading loop has copied MAXLENGTH characters into the allocated block, you are advancing your p pointer beyond the bounds of the block, causing subsequent strcpy()'s to write into and corrupt random memory.
If you want to stay with a single malloc() and sub-divide it, try something more like this instead:
#define NUMOFLINES 5
#define MAXLENGTH 1000
int main(void) {
char *pointers[NUMOFLINES], *buffer;
int nlines = readlines(pointers, &buffer);
if (nlines < 0) return -1;
writelines(pointers, nlines);
free(buffer);
return 0;
}
int readlines(char *pointers[], char **buffer) {
*buffer = malloc(sizeof(char) * (NUMOFLINES * MAXLENGTH));
if (*buffer == NULL) return -1;
int iptr, len;
char *p = *buffer;
for(iptr = 0; (iptr < NUMOFLINES) && ((len = get_line(p)) > 0); ++iptr)
{
pointers[iptr] = p;
p += len + 1;
}
return iptr;
}
Otherwise, I suggest you allocate a separate buffer for each pointer in the array, eg:
#define NUMOFLINES 5
#define MAXLENGTH 1000
int main(void) {
char *pointers[NUMOFLINES];
int nlines = readlines(pointers);
if (nlines < 0) return -1;
writelines(pointers, nlines);
for(int i = 0; i < nlines; ++i)
free(pointers[i]);
return 0;
}
int readlines(char *pointers[]) {
char line[MAXLENGTH], *p;
int iptr, len;
for(iptr = 0; (iptr < NUMOFLINES) && ((len = get_line(line)) > 0); ++iptr)
{
p = malloc(sizeof(char) * (len + 1));
if (p == NULL) {
for(int j = 0; j < iptr; ++j) free(pointers[j]);
return -1;
}
memcpy(p, line, sizeof(char) * len);
p[len] = '\0';
pointers[iptr] = p;
}
return iptr;
}
You only call malloc once, so you should only call free once.
After readlines returns, pointers[0] points to the start of the memory you allocated, while the remaining array members point someplace within that same block of memory. That means there's only one pointer to free.
So instead of this:
for(int i = 0 ; i < nlines ; i++)
free(pointers[i]);
Just do this:
free(pointers[0]);
To reiterate, you should only pass to free a pointer value that was returned from malloc (or realloc or calloc).
You don't need to free the other pointers you maintain within your buffer. If you do a single
p = malloc(sizeof(char) * MAXLENGTH);
then you need a single
free(p);
The malloc and free have to match exactly 1-to-1 and be called with the same addresses.
Since you want to modify p, you should do:
char* original = malloc(sizeof(char) * MAXLENGTH);
p = original;
and, once done:
free(original)

How to access a double pointer structure in another structure

I'm having trouble accessing my double pointer struct within my structure.
typedef struct monster
{
char *name;
char *element;
int population;
} monster;
typedef struct region
{
char *name;
int nmonsters;
int total_population;
monster **monsters;
} region;
region **
readRegion (FILE * infile, int *regionCount)
{
region **temp;
char garbage[50];
char garbage2[50];
char rName[50];
int monsterNum;
fscanf (infile, "%d %s", regionCount, garbage);
temp = malloc (*regionCount * sizeof (region *));
for (int i = 0; i < *regionCount; i++)
{
fscanf (infile, "%s%d%s", rName, &monsterNum, garbage2);
temp[i] = createRegion (inFile, rName, monsterNum);
}
return temp;
}
region *
createRegion (FILE * inFile, char *rName, int nMonsters)
{
region *r = malloc (sizeof (region));
char rMonster[50];
int rLength;
r->name = malloc ((strlen (rName) + 1) * sizeof (char));
strcpy (r->name, rName);
r->nmonsters = nMonsters;
for (int i = 0; i < nMonsters; i++)
{
r->monsters.name = (nMonsters * sizeof (r->monsters.name));
fscanf (in, "%s", rMonster);
r->monsters.name = malloc ((strlen (rMonster) + 1) * sizeof (char));
strcpy (r->monsters.name, rMonster);
}
return r;
}
Hopefully my code is readable where you can get the jist of what im trying to do with the monster** monsters pointer in my region struct. Any explnation on how to access and use a double struct pointer within a structure would help.
I've tried to clean up and re-interpret your createRegion to read a lot more like traditional C:
region* createRegion(FILE * inFile, char *rName, int nMonsters) {
region *r = malloc(sizeof(region));
char buffer[1024];
r->name = strdup(rName);
r->nmonsters = nMonsters;
r->monsters = calloc(nMonsters, sizeof(monster*));
for (int i=0; i < nMonsters; i++) {
// Allocate a monster
monster *m = malloc(sizeof(monster));
fscanf(in,"%s", buffer);
m->name = strdup(buffer);
m->element = NULL; // TBD?
m->population = 1; // TBD?
// Put this monster in the monsters pointer array
r->monsters[i] = m;
}
return r;
}
Where the key here is you must allocate the monsters. Here it's done individually, but you could also allocate as a slab:
region* createRegion(FILE * inFile, char *rName, int nMonsters) {
region *r = malloc(sizeof(region));
char buffer[1024];
r->name = strdup(rName);
r->nmonsters = nMonsters;
// Make a single allocation, which is usually what's returned from
// C functions that allocate N of something
monsters* m = calloc(nMonsters, sizeof(monster));
// Normally you'd see a definition like m in the region struct, but
// that's not the case here because reasons.
r->monsters = calloc(nMonsters, sizeof(monster*));
for (int i=0; i < nMonsters; i++) {
fscanf(in,"%s", buffer);
m[i].name = strdup(buffer);
m[i].element = NULL; // TBD?
m[i].population = 1; // TBD?
// Put this monster in the monsters pointer array
r->monsters[i] = &m[i];
}
return r;
}
Note I've switched out the highly quirky strlen-based code with a simple strdup call. It's also very odd to see sizeof(char) used since on any computer you're likely to interface with, be it an embedded microcontroller or a fancy mainframe, that will be 1.
Inasmuch as you are asking about accessing a double pointer inside a structure, I think your issue is mostly about this function:
region *
createRegion (FILE * inFile, char *rName, int nMonsters)
{
region *r = malloc (sizeof (region));
char rMonster[50];
int rLength;
r->name = malloc ((strlen (rName) + 1) * sizeof (char));
strcpy (r->name, rName);
r->nmonsters = nMonsters;
[Point A]
So far, so good, but here you start to run off the rails.
for (int i = 0; i < nMonsters; i++)
{
r->monsters.name = (nMonsters * sizeof (r->monsters.name));
Hold on. r->monsters has type monster **, but you are trying to access it as if it were a monster. Moreover, r->monsters has never had a value assigned to it, so there's very little indeed that you can safely do with it.
I think the idea must be that r->monsters is to be made to point to a dynamically-allocated array of monster *, and that the loop allocates and initializes the monsters, and writes pointers to them into the array.
You need to allocate space for the array, then, but you only need or want to allocate the array once. Do that before the loop, at Point A, above, something like this:
r->monsters = malloc(nMonsters * sizeof(*r->monsters)); // a monster **
Then, inside the loop, you need to allocate space for one monster, and assign a pointer to that to your array:*
r->monsters[i] = malloc(sizeof(*r->monsters[i])); // a monster *
Then, to access the actual monster objects, you need to either dererference and use the direct member selection operator (.) ...
(*r->monsters[i]).name = /* ... */;
... or use the indirect member selection operator (->) ...
r->monsters[i]->name = /* ... */;
. The two are equivalent, but most C programmers seem to prefer the latter style.
At this point, however, I note that in the body of the loop, you seem to be trying to make two separate assignments to the monster's name member. That doesn't make sense, and the first attempt definitely doesn't make sense, because you seem to be trying to assign a number to a pointer.
fscanf (in, "%s", rMonster);
r->monsters.name = malloc ((strlen (rMonster) + 1) * sizeof (char));
strcpy (r->monsters.name, rMonster);
Using the above, then, and taking advantage of the fact that sizeof(char) is 1 by definition, it appears that what you want is
// ...
r->monsters[i]->name = malloc(strlen(rMonster) + 1);
strcpy (r->monsters[i]->name, rMonster);
And finally,
}
return r;
}
Note well that corresponding to the two levels of indirection in type monster **, each access to an individual monster property via r->members requires two levels of derferencing. In the expressions above, one is provided by the indexing operator, [], and the other is provided by the indirect member access operator, ->.
* Or you could allocate space for all of the monsters in one go, before the loop, and inside the loop just initialize them and the array of pointers to them. The use of a monster ** suggests the individual allocation approach, but which to choose depends somewhat on how these will be used. The two options are substantially interchangeable, but not wholly equivalent.

Setting a specific element of a char** array to NULL

I am attempting to set the last element in a second char ** array to NULL after I encounter a specific char in the first array.
int test(char ** args){
char ** chmd1;
for(int i = 0; args[i] != NULL; ++i){
if(!strncmp(args[i], "<", 1)){
chmd1[i] = NULL;
break;
}
chmd1[i] = args[i];
}
for(int i = 0; chmd1[i] != NULL; ++i){
printf("%s", chmd1[i]);
}
return 0;
}
This code segfaults as the second for loop goes on for more iterations past where the NULL should be.
I want to be able to be able to do this just by manipulating pointers and not using any mallocs, but I'm completely stuck.
This code segfaults as the second for loop goes on for more iterations past where the the NULL should be.
You have not allocated memory for chmd1 and yet you are using it like it points to valid memory.
I want to be able to be able to do this just by manipulating pointers and not using any mallocs, but I'm completely stuck.
You can't do that. You have use malloc (or one of the other functions from the malloc group of functions: calloc, realloc) to allocate memory for chmd1 before you can use it.
allocate memory for pointer
char ** chmd1;
I want to be able to be able to do this just by manipulating pointers and not using any mallocs, but I'm completely stuck.
Without allocating memory to chmd1, it will not be possible.
You have to allocate memory for char ** chmd1; before assigning value NULL (or copy elements from args) to any element.
It can be something like
char ** chmd1 = malloc(NUMBER * sizeof(char*));
or even
char * chmd1[NUMBER];
To determine NUMBER value find the NULL in the args first.
EDIT:
Also you can use realloc in your loop as:
char **chmd1 = NULL;
int i;
for(i = 0; argv[i] != NULL; ++i){
chmd1 = (char**)realloc(chmd1, i * sizeof(char*) );
if(!strncmp(argv[i], "<", 1)){
chmd1[i] = NULL;
break;
}
chmd1[i] = argv[i];
}
// then use i as size of chmd1
for(int cnt = 0; cnt < i; cnt++)
{
if( chmd1[i] == NULL ) ; // do something
}
chmd1[i] = args[i];
chmd[i] is a pointer in 2D space and you are not allocating memory for the pointer.

How can I make a copy of char** in C?

I am having trouble understanding the concept of pointers in C. I have an array of pointers to character strings,
char ** args;
and I want to copy args to another array of pointers to character strings.
char ** args2;
I tried doing,
args2 = args;
but when I manipulate args2, I also manipulate args. I know this is because of the fact that the arrays are pointers. Also, if I run a for loop,
// by the time I get to the for loop
// args holds {"string1", "string2", "string3"}
// args2 = NULL;
for(i = 0; args[i] != NULL; i++){
args2[i] = args[i];
}
I get a segmentation fault error.
Thanks in advance.
You need to allocate space for args2 first! Even when you do that, don't forget that you'll still just be copying pointers to the original strings, so if you modify the strings in args2, the original strings are the ones being being modified.
You probably want something like:
i = 0;
while (args[i] != NULL)
i++;
args2 = malloc(i * sizeof *args2);
for(j = 0; j < i; j++)
{
args2[i] = strdup(args[i]);
}
Which both allocates memory for the pointers in arg2 and also duplicates all of the original strings. Add one to i before the second loop and set args2[i] to NULL after the second loop if you want to have a NULL terminated array like you started with.
Imagine char** as a pointer of pointers. Each element of it is another pointer, which points to a string (basically you have an array of strings).
Assuming you have a char** args2. If you do:
for(i = 0; args[i] != NULL; i++){
args2[i] = args[i];
}
You copy the value of each pointer from args to args2. It is the same thing as saying:
char *a = "abc";
char *b; // allocate memory
b = a;
Or, in order to copy strings you need to copy their content, not just the pointer to the first element. So, you need to use something like strcpy. Note that in order for this to work, you also need to allocate memory for args2, which I omit here.
for(i = 0; args[i] != NULL; i++){
strcpy(args2[i], args[i]);
}
Also, you might want to add the NULL terminator of args:
args2[i] = NULL;
In order to allocate memory, you need to know the size of the args array. Assume args has n element. Then, the memory allocation for args2 could be done as following (I also add the copying part):
// Allocate memory for n pointers to char
char **args2 = malloc(sizeof(char*) * n);
for(i = 0; args[i] != NULL; i++){
// For each pointer of args 2, allocate memory to store the
// correspondent string of args, including the NUL.
args2 = malloc(strlen(args[i] + 1));
strcpy(args2[i], args[i]);
}
You might want to consider using a static char matrix at the beginning that handles memory allocation, in order to facilitate learning.

Reading file to char** using fgets and memcpy

I'm trying to read a set of lines from a file to an array. I'm doing this to learn malloc and realloc.
#define MAX_LINE 301
char** read_file_lines(char* filename) {
char** ptr = NULL;
int max = 5;
int i = 0;
FILE *fp = fopen(filename, "r");
if(fp != NULL) {
char line[MAX_LINE];
while(fgets(line, MAX_LINE, fp) != NULL) {
/* allocate some extra memory for some more lines */
if(i == max) {
int new_max = max * 2;
int nr_bytes = new_max * sizeof(char) * MAX_LINE;
char **ptr2 = realloc(ptr, nr_bytes);
if(ptr2 != NULL) {
ptr = ptr2;
ptr2 = NULL;
max = new_max;
}
}
// ptr[i] = line;
// strcpy(ptr[i], line);
memcpy(ptr[i], line, strlen(line));
i++;
}
fclose(fp);
}
else {
printf("Error opening file %s\n", filename);
}
return ptr;
}
The code compiles. However, when it is executed, an error occurs (the program crashes).
I did some debugging and determined that the problem is in up in the memcpy () instruction. I had previously tried using strcpy, which also gives a similar problem.
I went to check memcpy ()'s protocolo and it is as followS:
void * memcpy ( void * destination, const void * source, size_t num );
Now, if ptr is char**, isn't ptr[i] equivalent to a char* ?
Thanks for your comments.
It looks like ptr isn't initialized to point to any memory at all. Also, you're not allocating any memory for the individual lines.
To initialize ptr, change the declaration to:
int max = 5;
char** ptr = malloc(max * sizeof(char*));
Try adding this before the call to memcpy:
ptr[i] = malloc(strlen(line) + 1);
and change the calculation for the realloc call:
int nr_bytes = new_max * sizeof(char*);
EDIT: To explain in more detail: ptr is a pointer to an array of pointers. You have to allocate memory for ptr (that is, enough memory just to store individual pointers). In addition to this, you also have to allocate each individual array of characters that the individual elements of ptr will point to.
The first change I suggested ensures that ptr always points to enough memory to hold 5 pointers (or more, once it's been realloc'd.)
The second change ensures that each member of ptr always points to valid memory before you try to access it as a pointer.
And the third change is required because ptr points to elements that are pointers to char, not char.
Nah. Arrays are not pointers. Pointers to pointers are not arrays of arrays. If you want a two-dimensional dynamic array, then you have to allocate memory for 1. the array of pointers that point to the individual lines, and 2. for the lines themselves too.
Problem is that at first execution memory is not allocated: i is 0, max is 5, the if condition is false and the realloc is never executed.

Resources