Significance of double pointer and triple pointer in this code snippet - c

#include<stdio.h>
#include<stdlib.h>
void add(char **p);
void print(char **p);
int cnt=0;
main()
{
int option;
char **p=NULL;
while(1)
{
printf("------MENU-----\n");
printf("1>input\n 2>print\n3>exit\n");
printf("enter ur choice\n");
scanf("%d",&option);getchar();
switch(option)
{
case 1: add(p);
break;
case 2: print(p);
break;
case 3: return;
default: printf("Invalid option\n");
}
}
}
void add(char **p)
{
int i;
p=(char**)realloc(p,(cnt+1)*sizeof(char*));
if(p==NULL)
{
printf("Error: memory not available\n");
return;
}
p[cnt]=NULL;
p[cnt]=(char*)realloc(p[cnt],20*sizeof(char));
puts("enter a name");
gets(p[cnt]);
cnt++;
printf("cnt=%d\n",cnt);
}
void print(char **p)
{
int i;
for(i=0;i<cnt;i++)
printf("p[%d]=%s\n",i,p[i]);
}
In the above code, I am making a database of names. For this I am using dynamic memory allocation. I am allocation memory for 2D-array using array of pointers method. When I am executing this program on gcc compiler, I am getting segmentation fault. I am not understanding why is it happening?Could you please tell me where the bug is?

In main, all you do is to assign p = NULL and then use this NULL pointer in print, which causes the segfault. Note that add(p) is equivalent to add(NULL) and does not change p.
Did you mean to pass an address with add(&p)? If so, you need to fiddle a bit with the number of *.

p in main is handed over to add by value. add modifies a local copy then, but not the original p.
Besides the terrible formatting and everything you need to hand a pointer to your main's p to add:
...
case 1: add(&p);
...
void add(char ***p)
{
int i;
*p = realloc(*p,(cnt+1)*sizeof(char*));
if(*p==NULL)
{
printf("Error: memory not available\n");
return;
}
(*p)[cnt]=NULL;
(*p)[cnt]=realloc((*p)[cnt],20*sizeof(char));
puts("enter a name");
gets((*p)[cnt]);
cnt++;
printf("cnt=%d\n",cnt);
}

A copy of 2D pointer p is created in the add function, and updating this pointer will not be reflected in the main function.
A quick solution to this problem is to return the 2D pointer 'p' from the add function.
char** print(char **p)
{
....
....
return p;
}

Pass by reference. You need to pass the address of your pointer to make sure the changes in function add() is reflected in main(). Avoid using gets()
In this case
add(&p);
Accordingly your add() function definition should change to handle this.
Or the other way is for the add function to make the required allocations and return that address to your pointer
char **add();
Check the code below:
char **add(char **p)
{
int i;
p=(char**)realloc(p,(cnt+1)*sizeof(char*));
if(p==NULL)
{
printf("Error: memory not available\n");
return;
}
p[cnt]=NULL;
p[cnt]=(char*)realloc(p[cnt],20*sizeof(char));
scanf("%s",p[cnt]);
cnt++;
printf("cnt=%d\n",cnt);
return p;
}
So your call should be:
p = add(p);

Apart from the other things, I don't think anyone's mentioned the realloc trap of reallocation failure. Suppose this call
p=(char**)realloc(p,(cnt+1)*sizeof(char*));
fails to allocate the new memory: what happens then? Yes, you will get NULL returned: but the memory which was allocated, and which pis pointing to, does not get free'd. Instant memory leak.
You must call realloc like this:
char* pTemp = realloc(p, cnt + 1);
if(pTemp != NULL)
{
p = pTemp; // Now safe to update p
}
else
{
// Error handling as required, possibly including freeing p
}
Yet another reason not to use realloc. It doesn't really by you very much over doing the buffer copies yourself IMHO.

Related

Re-sizing dynamically allocated arrays

I have written the following code to resize an array if the an item is going to go out of range when storing it. This code works as intended.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//! Our location struct for storing locations
struct location
{
char loc_name[35];
char description[85];
float latitude;
float longitude;
};
void runMenu();
void add_location(struct location** p_location_array, int* p_array_size, int* p_current_size);
void resize_array(struct location** p_location_array, int* p_array_size);
void print (struct location* p_array, int p_current_size);
// Print out the main menu to the user.
void runMenu()
{
printf("[A]dd additional Locations\n");
printf("[P]rint the current list of locations\n");\
printf("[Q]uit the program\n");
}
//! Resize the array to two times it's origional size.
void resize_array(struct location** p_location_array, int* p_array_size)
{
// Allocate enough space for two times the size of the array
int new_size = 2 * (*p_array_size);
struct location* new_location_array = malloc(new_size * sizeof(struct location));
if (!new_location_array)
{
printf ("Cannot add more elements heap has exhausted all space\n");
exit(1);
}
// Copy the old array to the new array.
memcpy(new_location_array, *p_location_array, ((*p_array_size ) * sizeof(struct location)));
// We will update the current size of the array for later checking.
*p_array_size = 2 * (*p_array_size);
// We have a copy of the old array so we can free it.
free(*p_location_array);
// The contents of the pointer reference get the array we malloced in this function
*p_location_array = new_location_array;
}
//! Add a new location to our array. If the array isn't large enough resize it then insert the new struct.
void add_location(struct location** p_location_array, int* p_array_size, int* p_current_size )
{
// Get the users input
struct location new_location;
printf("Enter the new location name\n ");
fscanf(stdin, "%s", new_location.loc_name);
printf("Enter a description of the location\n");
fscanf(stdin, "%s", new_location.description),
printf("Enter the latitude\n");
fscanf(stdin, "%f", &new_location.latitude);
printf("Enter the longitude\n");
fscanf(stdin, "%f", &new_location.longitude);
// Check to see if the size is correct.
if (*p_array_size <= *p_current_size)
{
// If not the correct size resize the array
resize_array(p_location_array, p_array_size);
}
// Insert our sruct
(*p_location_array)[*p_current_size] = new_location;
}
//! Loop over and print out the locations
void print (struct location* p_array, int p_current_size)
{
int i;
for (i = 0; i < p_current_size; i++)
{
struct location current = p_array[i];
printf("%s : %s : %f : %f\n", current.loc_name, current.description, current.latitude, current.longitude);
}
}
int main()
{
char choice = ' ';
short control = 1;
int size;
int currentSize = 0;
printf("Enter the inital size of the array\n");
scanf(" %d", &size);
// Make a new struct array from the heap
struct location* m_location_array =
malloc(size * sizeof(struct location));
// Make sure we have a valid chunk of the heap.
if (!m_location_array)
exit(1);
while(control)
{
runMenu();
scanf(" %c", &choice);
switch (choice)
{
case 'a':
case 'A':
// Do Add additional
add_location(&m_location_array, &size, &currentSize);
currentSize++;
break;
case 'p':
case 'P':
// Do printing
print (m_location_array, currentSize);
break;
case 'Q':
case 'q':
control = 0;
break;
default:
printf("Invalid input\n");
}
}
// clean up after ourselves.
free (m_location_array);
return 0;
}
However, when I originally wrote this function I thought it would be possible to just pass in a the pointer to the array instead of a reference to the pointer like so:
void resize_array(struct location* p_location_array, int* p_array_size)
Calling this function without the reference to pointer threw a segfault indicating the memory was being doubly freed. Is that because the pointer when passed to the function someway gets freed and reallocated? Furthermore, why is it necessary to pass a pointer by reference like this? Even if the pointer is a copy of the original pointer wouldn't it still point to the same piece of memory? Any points in the correct direction is much appreciated.
You gave a pointer to the function and you have a call to free there. So the memory is freed. After that using that pointer causes undefined behaviour and you may not use it.
Modifying the pointer variable inside the function doesn't change the pointer outside the function. That is why you need a pointer to the pointer so that you can modify the pointer variable outside the function.
Even if the pointer is a copy of the original pointer wouldn't it still point to the same piece of memory?
Yes, and that is the point: it will keep on pointing to the same place unless you change it. If you do a new malloc it will point to a completely different place.
Also a hint: realloc might be worth checking out.
If you pass pointer to function it is a copy of original one. Then even if you assign to this pointer inside the function, like
p_location_array = new_location_array;
the original pointer (that outside the function) still has unchanged value. So in case the original pointer pointed to some memory area and you have passed it to the function
void resize_array(struct location *p_location_array, int* p_array_size)
and you have called free() inside thefunction and assigned NULL to the pointer, after your function has returned the original pointer will comapre as not NULL.
// warning, changed prototype
void resize_array(struct location *p_location_array, int* p_array_size);
struct location *loc = malloc(size * sizeof(struct location)); // assume loc = 0x12345678
if (loc == NULL) EXIT_FAILURE;
// change pointer inside the function
// assign NULL to the pointer
resize_array(loc, size_p);
if (loc != NULL)
free(loc); // this will be called, loc is still 0x12345678,
// double free, UB

c free() works when I call but not in my function

I am learning C and I am playing with malloc and free. But for some reason when I use free() in the main everything works but when I put it in my function it does not
#include <stdlib.h>
#include <stdio.h>
struct st {
int number;
};
void del(struct st *s) {
if (s == NULL) return;
free(s); s = NULL;
}
int main() {
struct st *s;
s = (struct st *)malloc(sizeof(struct st));
s->number = 4;
printf("The value is: %d", s->number);
del(s);
// free(s); s= NULL;
if(s == NULL) printf("\nThe struct is removed from memory\n");
else printf("\nThe value is: %d\n", s->number);
return 0;
}
This echo:
The value is: 4
The value is: 0
But if I do:
// del(s);
free(s); s= NULL;
it works
You are passing the pointer to your function, which means it only has access to the local copy of this pointer. So your free(s) will free only this local copy. If you want to free a variable, that is outside of the scope of the function from which you call free, you need to gain access to it by derefering again (passing a pointer to this pointer).
void del(struct st **s) {
if (*s == NULL) return;
free(*s);
*s = NULL;
}
should work fine.
Edit: call the function by del(&s);
Your del function frees the allocated memory and assigns NULL to the local copy of the variable. However, the local copy is not the same copy as that at the call-site (even if both point to the same allocated object, that has been freed).
You then end up doing what's called "use after free()", which is (probably) not actively harmful in this specific case, but can be a crash bug in the general case (and possibly a vector for code injection and exploits).
Either re-write del to take a pointer to a pointer and pass &s in, or have del return "the new value" and do s = del(s).

Accessing dynamically allocated structure inside a function

In the below code, I want to access the structure element that is dynamically allocated inside a function. I have declared structure globally, but allocated inside fun1(). I cannot access the structure element after receiving the structure elements. Kindly help me where I have gone wrong. This works fine if it is in single function. Thanks in advance.
struct s1
{
int a;
char b[10];
} *s2;
int val;
void main()
{
fun1();
fun2();
printf("\n Element a %d",(s1->a));
}
fun1()
{
struct s1 *s2=malloc(sizefof(struct s1)*val);
recv(fd,(void)&s2,sizeof(struct s1),0);
}
fun2()
{
printf("\n Element a %d",(s1->a));
}
There's a lot of confusion here.
Something like this might be what you're after:
struct s1
{
int a;
char b[10];
};
struct s1 * fun1(int val)
{
struct s1 *s = malloc(val * sizeof *s);
if(s != NULL)
recv(fd, s, val * sizeof *s, 0);
return s;
}
void fun2(struct s1 *ptr)
{
printf("First element's a %d", ptr[0].a);
}
int main(void)
{
struct s1 *data = fun1(100);
fun2(data);
printf("First element's a is %d", data[0].a);
return 0;
}
Note that recv() can fail too, and that should be checked. It should, also, perhaps be a read()?
Basically I removed the global variables, and fixed basic errors in allocation and pointer usage.
You have a couple of problems I can see, assuming that int val; was initialized and it's value is reasonable
You don't check for the return value of malloc, it's very unlikely that this is the problem, but that also depends on the value of val.
You pass the address of the pointer to recv.
fun2() makes no sense at all, the code shouldn't even compile.
You redeclare s2 in fun1(), which doesn't seem to be what you want, since you have initially declared s2 global.
The printf() statement in main() is also broken.
Note: in 4 and 5, you should notice that s1 is the name of the struct, which was a very bad choice and pretty much the first source for your confusion.
So fun1() should be re-wirtten
fun1()
{
s2 = malloc(sizefof(*s1) * val);
if (s2 != NULL)
recv(fd, s2, val * sizeof(*s2), 0);
}
and fun2()
fun1()
{
if (s2 != NULL)
printf("\n Element a %d", s2->a);
}

Warning: return from incompatible pointer type in C

The below code generates an incompatible pointer type error and warning: control reaches end of non-void function in the filename function:
#include <stdio.h>
#include <stdlib.h>
int quit;
char *filename(int *);
int main ()
{
filename(&quit);
return 0;
}
char *filename(int *i1)
{
char input[16];
char *dum=(char*)malloc(16*sizeof(char));
if (dum==NULL){
printf("Memory could not be allocated \n");
}
else {
printf("Memory was allocated – remember to free\n \n");
*i1=1;
fputs("Input filename = ", stdout);
fflush(stdout);
fgets(input,sizeof(input),stdin);
printf("Filename = \"%s\"\n",input);
return i1;
}
}
I'm new to this, can someone help me with this error?
Well, yes? The function is declared to return char *, but you return i1 which is the input argument and has type int *.
You might mean to return the newly allocated string dum, and perhaps also fill it with the data that was read using fgets() to the separate character array input. In this case, you need to copy the data over, and return dum.
It would be more concise, simpler, and generally better to read directly into dum:
fgets(dum, 16, stdin);
return dum;
Note that this duplicates the size of the buffer from the malloc() call, which is a "code smell". This can be improved by making it a local constant in the function:
char * filename(void)
{
const size_t max_fn = 16;
char *dum;
if((dum = malloc(max_fn)) != NULL)
{
if(fgets(dum, max_fn, stdin) != dum)
{
free(dum); /* Input failed, free the buffer and drop the pointer. */
dum = NULL;
}
}
return dum;
}
My latter code also has the benefit that it checks return values of functions that can fail. Both memory allocation (malloc()) and I/O (fgets()) can fail, so you must check their return values.
In addition to what unwind has said you should return something after the line printf("Memory could not be allocated \n");
Well, firstly, your indenting is appalling. Reindenting this makes it a little clearer what is going on:
char *filename(int *i1)
{
char input[16];
char *dum=(char*)malloc(16*sizeof(char));
if (dum==NULL){
printf("Memory could not be allocated \n");
}
else {
printf("Memory was allocated – remember to free\n \n");
*i1=1;
fputs("Input filename = ", stdout);
fflush(stdout);
fgets(input,sizeof(input),stdin);
printf("Filename = \"%s\"\n",input);
return i1;
}
}
So in one path of the if, you return 'i1' which is an int pointer, which is most definitely not a char pointer.
In the other half, you just drop through to the end of the function, which returns nothing.
You are lucky your compiler warns you about this. A lot, by default, just ignore that.
as an aside, you have a memory leak. You malloc memory for dum, but never free it.

Why is my pointer not null after free?

void getFree(void *ptr)
{
if(ptr != NULL)
{
free(ptr);
ptr = NULL;
}
return;
}
int main()
{
char *a;
a=malloc(10);
getFree(a);
if(a==NULL)
printf("it is null");
else
printf("not null");
}
Why is the output of this program not NULL?
Because the pointer is copied by value to your function. You are assigning NULL to the local copy of the variable (ptr). This does not assign it to the original copy.
The memory will still be freed, so you can no longer safely access it, but your original pointer will not be NULL.
This the same as if you were passing an int to a function instead. You wouldn't expect the original int to be edited by that function, unless you were passing a pointer to it.
void setInt(int someValue) {
someValue = 5;
}
int main() {
int someOtherValue = 7;
setInt(someOtherValue);
printf("%i\n", someOtherValue); // You'd expect this to print 7, not 5...
return 0;
}
If you want to null the original pointer, you'll have to pass a pointer-to-pointer:
void getFree(void** ptr) {
/* Note we are dereferencing the outer pointer,
so we're directly editing the original pointer */
if (*ptr != NULL) {
/* The C standard guarantees that free() safely handles NULL,
but I'm leaving the NULL check to make the example more clear.
Remove the "if" check above, in your own code */
free(*ptr);
*ptr = NULL;
}
return;
}
int main() {
char *a;
a = malloc(10);
getFree(&a); /* Pass a pointer-to-pointer */
if (a == NULL) {
printf("it is null");
} else {
printf("not null");
}
return 0;
}
Because the getFree() function takes a copy of the pointer. ptr and c are both pointers, but they are different variables. It's the same reason why this function will output "6":
void Magic(int x)
{
x = 1;
}
void main()
{
int a = 6;
Magic(a);
printf("%d", a);
}
You are passing pointer a by value, so it is not modified by function. It's only a copy of pointer modified within function, the original variable value is not affected.
Update:
If you wanted to make your life easier by replacing freeing + nulling a variable with a single line of code, you need either a macro:
#define MYFREE(x) free(x); x = NULL;
or a function with pointer to pointer argument:
void myfree(void** pp) { free(*pp); *pp = NULL; }
Pointers are stored as integers somewhere in memory.
When you do a = malloc(10);, a has some value, say 0x1.
When you call getFree(a);, the function copies a into void *ptr.
Now a=0x1 and ptr=0x1.
When you do ptr=NULL, only ptr is changed to NULL, but a is still 0x1..
You are passing the pointer By value.. (By default C passes the argument by value) which means you are updating the copy only ..not the real location..for that you might need to use pointer to pointer in C
void getFree(void **ptr)
{
if(*ptr != NULL)
{
free(*ptr);
*ptr = NULL;
}
return;
}
The question has already been answered but if it helps, I can explain it graphically.
You are doing this --> the pointer is copied by value to your function so it points to the array
but instead you want this -->point to the original pointer
As Merlyn Morgan-Graham already said, the way to solve it is to add operators * and &.

Resources