Bug:
While passing a double pointer to a function the value of the fields that the pointer is pointing to seems to be dependent on some local variable of the function.
More specifically when I comment the line L(in the function "function")Output:
In the main function: 1
In the function: 1
But when I uncomment the same line,
Output:
In the main function: 1
In the function: 0
program:
typedef struct s{
int *value;
}s;
s** initialize()
{
s** temp = (s**)malloc(sizeof(s*));
s* f = (s*)malloc(sizeof(s));
f->value = NULL;
temp = &f;
return temp;
}
void function(s** what)
{
//Line L: size_t count = 0;
printf("In the function: %d\n", (*what)->value == NULL);
}
int main()
{
s** m = initialize();
printf("In the main function: %d\n", (*m)->value == NULL);
function(m);
}
What I have tried:
I thought that I am getting random outputs, but that was not the case as I am consistently getting the same output.
I tried deciphering the assembly language code but that was too cryptic for me.
Environment:
compiler: gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
operating system: linux mint
Here:
s** initialize()
{
s** temp = (s**)malloc(sizeof(s*));
s* f = (s*)malloc(sizeof(s));
f->value = NULL;
temp = &f; // temp is now the address of a local variable
return temp; // now temp is returned
// that's undefined behavior when the returned
// pointer is used
}
The variable f doesn't exist when the initialize function returns, so you are returning the address of a non-existing variable. Using the address will be undefined behavior, i.e. anything can happen and there is no way in general to explain it.
On a specific system, we can do some guessing. My guess is that once you add the line with a new variable, it overwrites the memory location where f used to be stored. Without the new variable the location where f used to be stored is still the same.
The function initialize
s** initialize()
{
s** temp = (s**)malloc(sizeof(s*));
s* f = (s*)malloc(sizeof(s));
f->value = NULL;
temp = &f;
return temp;
}
can invoke undefined behavior because it returns pointer to a local variable. And moreover it has a memory leak.
At first a memory was allocated and its address was assigned to the variable temp
s** temp = (s**)malloc(sizeof(s*));
Then the pointer was reassigned
temp = &f;
So the allocated memory is not freed.
The pointer is assigned by the address of a local variable
s* f = (s*)malloc(sizeof(s));
//...
temp = &f;
After exiting the function the variable f will not alive. So the pointer temp has an invalid value.
It seems what you mean is the following
s** initialize()
{
s** temp = (s**)malloc(sizeof(s*));
s* f = (s*)malloc(sizeof(s));
f->value = NULL;
*temp = f;
return temp;
}
If to make the changes you will get the expected result.
#include <stdio.h>
#include <stdlib.h>
typedef struct s{
int *value;
}s;
s** initialize()
{
s** temp = (s**)malloc(sizeof(s*));
s* f = (s*)malloc(sizeof(s));
f->value = NULL;
*temp = f;
return temp;
}
void function(s** what)
{
//Line L: size_t count = 0;
printf("In the function: %d\n", (*what)->value == NULL);
}
int main( void )
{
s** m = initialize();
printf("In the main function: %d\n", (*m)->value == NULL);
function(m);
free( *m );
free( m );
}
The program output is
In the main function: 1
In the function: 1
Related
Concerning double indirection (pointer to pointer) and passing those to a function
I cannot change the pointer here in function void test(int **nmbr, int *n);
int n = 5;
int n2 = 555;
int *nPtr = &n;
int *n2Ptr = &n2;
printf("the number = %d\n", *nPtr);
test(&nPtr, n2Ptr);
printf("the number is now = %d\n", *nPtr);
test
void test(int **nPptr, int *n2Ptr) {
int *p = *nPptr;
p = n2Ptr;
}
since the pointer p points to a copy of *nPtr, right?
But what about this code (this time the pointer points to a given struct in a linkedlist
the code snipped is from the site https://www.learn-c.org/en/Linked_lists
int remove_by_index(Person **head, int n) {
int i = 0;
int retval = -1;
Person *current = *head;
Person *temp_node = NULL;
if (n == 0) {
return pop_first(head);
}
for (i = 0; i < n-1; i++) {
if (current->next == NULL) {
return -1;
}
current = current->next;
}
temp_node = current->next;
retval = temp_node->nmbr;
current->next = temp_node->next;
free(temp_node);
return retval;
}
it removes a given node in the list by a given indexnumber
Here one can see that *current is local copy in the function and traversate in the list and lastly merges two nodes, without problem
So why does it work to change the pointer here but not in the function test(int **nPptr, int *n2Ptr)?
To be clear
in the function test:
int *p = *nPptr;
p is local copy and copies the pointer from *nPtr
in the function remove_by_index
Person *current = *head;
current is local copy and copies the pointer from *head. The list lives beyond the scope of the function remove_by_index(..) so I do not understand why it can be manipulated in the function by the local pointer *current, at the same time as it does not work to alter nPtr in function test(..)
In a function, changes to pointer variables or pointer parameters have no effect outside the function. However, if the pointer is pointing to an object outside the function, that object can be modified by dereferencing the pointer.
For example, in OP's test function:
void test(int **nPptr, int *n2Ptr) {
int *p = *nPptr;
p = n2Ptr;
}
p is initialized and then its value is changed by an assignment. This has no effect on any object outside the function. If the function were changed as follows:
void test(int **nPptr, int *n2Ptr) {
int *p = *nPptr;
p = n2Ptr;
*nPptr = p;
*p = 42;
}
Then two objects outside the function will have been modified (an int * and an int).
In OP's remove_by_index function, changes to the current variable as it progresses through the linked list have no external effect, but the line:
current->next = temp_node->next;
is equivalent to:
(*current).next = (*temp_node).next;
The external Person object that current is pointing to on the linked list has been modified by dereferencing of the pointer and assignment to the next member of the Person it is pointing to.
it shows invalid read of size 8 at HTSize by my main function and it seems to work(it prints 0) for an empty hash but what I believe is the issue is that the initialized hash in my HTCreate is passed as an argument in HTSize but unitialized.
I've tried(not in the code that is shown) passing the argument as &Table with the definition HTSize(HThash**) but instead of solving it the programm doesn't run and valgrind shows "Address 0x0 is not stack'd, malloc'd or (recently) free'd" as well. Inside the function I also put a pointer T to the hash to dereference the **hash.I work in linux.
typedef char* KeyType;
typedef int HTItem;
typedef struct node{
KeyType key;
HTItem item;
struct node *next;
}List;
typedef struct {
List *head;
}TableEntry;
typedef TableEntry *HTHash;
HTHash* HTCreate(void);
int HTSize(HTHash);
int TABLESIZE = 10;
int main(void)
{
HTItem *p;
HTHash *Table = HTCreate();
printf("%d\n",HTSize(*Table));
}
HTHash* HTCreate(void)
{
int i;
HTHash table = (HTHash)malloc(TABLESIZE*sizeof(TableEntry));
HTHash *T;
for(i=0;i<TABLESIZE;i++)
table[i].head = NULL;
T = &table;
return T;
}
int HTSize(HTHash hash)
{
int i,count=0;
List *temp = (List*)malloc(sizeof(List));
for(i=0;i<TABLESIZE;i++)
{
if(hash[i].head != NULL)
{
count++;
temp = hash[i].head->next;
while(temp != NULL)
{
count++;
temp = temp->next;
}
}
}
return count;
}
The mistake may be in HTCreate but Im'not sure.I checked if the hash gets initialized in the main function and it does.
Your problem is effectivelly in HTCreate function: it returns &table which is local to the function.
Demo:
HTHash *HTCreate(void)
{
int i;
HTHash table = NULL;
printf("&table before malloc: %p\n", &table);
table = malloc(TABLESIZE*sizeof(TableEntry));
printf("&table after malloc: %p\n", &table);
HTHash *T;
for(i=0;i<TABLESIZE;i++)
table[i].head = NULL;
T = &table;
return T;
}
Will print:
&table before malloc: 0x7fff200eb818
&table after malloc: 0x7fff200eb818
We see that malloc is without effect on this value.
Since this is local to the function, using this in another function can cause anything (undefined behavior)
The good way to return what you need from the function is to return a HTHash type:
HTHash HTCreate(void)
{
int i;
HTHash table = malloc(TABLESIZE*sizeof(TableEntry));
for(i=0;i<TABLESIZE;i++)
table[i].head = NULL;
return table;
}
Looking what is done in it, I saw that this function can be simplified :
HTHash HTCreate(void)
{
return calloc(TABLESIZE, sizeof(TableEntry));
}
This question already has answers here:
How to access a local variable from a different function using pointers?
(10 answers)
Closed 5 years ago.
I am learning structures and linked-lists. However I am facing a problem that prevents me from debugging my program's bugs, since it comes, seemingly, from the function printf, which is what I use to debug the program.
The following program works fine:
struct pointer_struct
{
struct new_struct *ptr;
};
struct new_struct
{
int i;
struct new_struct *ptr;
};
void init(struct pointer_struct *pointer, int nb)
{
struct new_struct my_struct;
my_struct.i = nb;
my_struct.ptr = NULL;
pointer->ptr = &my_struct;
}
int main(void)
{
struct pointer_struct pointer;
pointer.ptr = NULL;
init(&pointer, 15);
//printf("pointer.ptr = %p\n", pointer.ptr);
printf("pointer.ptr->i = %d\n", pointer.ptr->i);
}
Output:
pointer.ptr->i = 15
But as soon as I uncomment the commented line, i takes weird values. Here are some examples of outputs:
$./a.out
pointer.ptr = 0x7fffc6bcc650
pointer.ptr->i = -448723664
$./a.out
pointer.ptr = 0x7fffd09ed480
pointer.ptr->i = 1218512176
$./a.out
pointer.ptr = 0x7ffff630fa70
pointer.ptr->i = -1073674960
What is going wrong with printf?
You have an undefined behavior or UB, which is always A Bad Thing™.
void init(struct pointer_struct *pointer, int nb)
{
struct new_struct my_struct;
my_struct.i = nb;
my_struct.ptr = NULL;
pointer->ptr = &my_struct;
} // here my_struct lifetime is finish so pointer->ptr become invalid
int main(void)
{
struct pointer_struct pointer;
pointer.ptr = NULL;
init(&pointer, 15);
printf("pointer.ptr = %p\n", pointer.ptr); // pointer.ptr is not valid so it's UB
printf("pointer.ptr->i = %d\n", pointer.ptr->i); // This is UB too
}
You initialize pointer.ptr with a local variable.
void init(struct pointer_struct *pointer, int nb)
{
struct new_struct my_struct;
my_struct.i = nb;
my_struct.ptr = NULL;
pointer->ptr = &my_struct; // MISTAKE!!! my_struct is on the stack.
// its memory space could be overwritten at
// any time after the function returns.
}
Later, in main
printf("pointer.ptr = %p\n", pointer.ptr); // This call to printf uses the stack,
// and overwrites the space used
// by my_struct
printf("pointer.ptr->i = %d\n", pointer.ptr->i);
I have the following code in my program:
/*Making a tree using linked lists.
*/
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
typedef struct node
{
int value;
struct node * left;
struct node * right;
}Node;
Node * tree;
Node * maketree(int number)
{
Node p = { number, NULL, NULL };
return &p;
}
int main()
{
int a[5] = { 1, 2, 3, 1, 4 };
int number;
number = a[0];
tree = maketree(number);
printf("Root has a value of %d\n", tree->value);
printf("Root has a value of %d\n", tree->value);
}
Output:
Root has a value of 1
Root has a value of "Some garbage value!"
I can't understand how tree points to some other address (or printing some garbage value somehow) when I'm not at all playing with it and printing its value in 2 successive lines!
You are returning &p, the address of a local variable.
This variable goes out of scope as maketree() exits, making it undefined behavior to dereference the pointer.
In practice you're dereferencing a pointer to stack space that was allocated by maketree(), but has been de-allocated and very likely re-used (by printf(), which is quite a heavy function) since. This is totally invalid code.
The fix is to allocate the new node on the heap:
Node * maketree(int number)
{
Node *p = malloc(sizeof *p);
if(p != NULL)
{
p->value = number;
p->left = p->right = NULL;
}
return p;
}
In the function
Node * maketree(int number)
{
Node p = { number, NULL, NULL };
return &p;
}
You are returning the adress of a local variable namely p. Being local means that the memory location of the variable p is no more reserved for p after the end of the function. Nothing prevent it to be overwriten. This is called an undefined behavior.
You should use a dynamic allocation:
Node * maketree(int number)
{
Node *pp;
pp = malloc(sizeof(*pp));
if(pp != NULL)
{
pp->value = number;
pp->left = NULL;
pp->right = NULL;
}
return pp;
}
typedef struct node
{
struct node *leftChild, *rightChild;
int value;
} bst;
void insert(bst* b, int i)
{
b=malloc(sizeof(b));
b->value=i;
b->leftChild = NULL;
b->rightChild = NULL;
printf("[%i]",b->value);
return;
}
main()
{
bst* b;
insert(b,5);
printf("[%i]",b->value);
}
The second printf causes a Segmentation Fault, commenting it out it works fine. What am I doing wrong here (Ignore the meanings of the function names i.e. that the insert is an insert function for the bst)? Shouldn't bst persist in it's setup outside of the insert function?
You need to pass the pointer by reference to change where it points.
// just add this & here in the function definition.
// That's the only change in all your code (for c++)
void insert(bst * &b, int i)
Or in C, a pointer pointer
void insert(bst ** b, int i)
{
*b=malloc(sizeof(*b));
(*b)->value=i;
(*b)->leftChild = NULL;
(*b)->rightChild = NULL;
printf("[%i]",(*b)->value);
return;
}
Note the changes to dereference the pointer pointer.
When you call this function, you'll need to take the address of your bst *:
insert(&b,5);
The problem is that b in main points to an invalid memory. The b doesn't get changed by insert because insert uses a copy of the pointer. You have to options to solve this:
Make "insert" take a pointer to pointer
Allocate the memory before entering "insert"
Because the first one is harder to read (and understand), I'd prefer the second one. But it depends on you what variant you choose.
Memory allocation before insert:
typedef struct node
{
struct node *leftChild, *rightChild;
int value;
} bst;
void insert(bst* b, int i)
{
// No malloc here
b->value=i;
b->leftChild = NULL;
b->rightChild = NULL;
printf("[%i]",b->value);
return;
}
main()
{
bst* b = (bst *)malloc(sizeof(bst)); // This line has changed
insert(b,5);
printf("[%i]",b->value);
}
The variant with a double pointer:
typedef struct node
{
struct node *leftChild, *rightChild;
int value;
} bst;
void insert(bst** b, int i)
{
*b = (bst *)malloc(sizeof(bst)); // This line has changed
(*b)->value=i;
(*b)->leftChild = NULL;
(*b)->rightChild = NULL;
printf("[%i]", (*b)->value);
return;
}
main()
{
bst* b;
insert(&b,5); // Now b is changed
printf("[%i]",b->value);
}
Side note 1: you also have the malloc() incorrect - you should put use sizeof(bst) instead of sizeof(b) as the size. This is because sizeof(b) == sizeof(bst *) == size of an address, which is almost certainly not what you want.
Side note 2: do not forget to call free(b) at the end to avoid memory leaks.
Side note 3: check that malloc() doesn't return NULL. A rare case but a good program should expect that.
Since C does pass-by-value for arguments, the pointer being referenced inside insert() is a copy of the original b. The memory gets allocated to the variable b inside the function, but not to the variable in the main()function.
for eg.
# In main
b<main> = 0xOrigLoc
b<insert> = undefined
# In insert (before malloc)
b<main> = 0xOrigLoc
b<insert> = 0xOrigLoc
# After Malloc
b<main> = 0xOrigLoc
b<insert> = 0xNewLoc
Notice how b<main> remains untouched. You are only tweaking the copy of b inside the function.
The solution is to use a pointer to a pointer as described by the previous solution.
Rather than change the value, return the new value of b
bst* insert(bst* b, int i)
{
if (b == NULL)
{
bst* result = (bst*)malloc(sizeof(bst));
result->value=i;
result->leftChild = NULL;
result->rightChild = NULL;
printf("[%i]",b->value);
return result;
}
if (i < b->value)
b->left = insert(b->left, i);
else
b->right = insert(b->right, i);
return b;
}
int main()
{
bst* b = NULL; // Initialise it to empty.
b = insert(b,5);
printf("[%i]",b->value);
}
Well one problem is you're not typecasting your pointer.
The line:
b=malloc(sizeof(b));
Should be:
b=(bst*)malloc(sizeof(b));
Your code gives me an error when I compile it in gcc.
This is how to identify and fix errors in the future:
Testing in GDB:
Full code:
#include <malloc.h>
#include <stdio.h>
typedef struct node
{
struct node *leftChild, *rightChild;
int value;
} bst;
void insert(bst* b, int i)
{
b=(bst*)malloc(sizeof(b));
b->value=i;
b->leftChild = NULL;
b->rightChild = NULL;
printf("[%i]",b->value);
return;
}
main()
{
bst* b;
insert(b,5);
printf("[%i]",b->value);
}
Compiled with
%g++ -g main.cc
Ran with
%gdb a.out
GDB commands:
b main.cc:211
run
print b
(output is: 0x0)
step
step
print b
(output is: 0x4f60010)
step
step
step
step
step
print b
(output is: 0x0)
quit
This clearly indicates that your variable is getting deallocated when the function leaves its scope.
Changing the code to:
#include <malloc.h>
#include <stdio.h>
typedef struct node
{
struct node *leftChild, *rightChild;
int value;
} bst;
void insert(bst* b, int i)
{
b->value=i;
b->leftChild = NULL;
b->rightChild = NULL;
printf("[%i]",b->value);
return;
}
main()
{
bst* b;
b=(bst*)malloc(sizeof(bst));
insert(b,5);
printf("[%i]",b->value);
free(b);
}
and rerunning gdb with the following commands:
b main.cc:21
run
s
(short for step)
print b
(output: 0x1aab010)
s
s
s
s
s
s
print b
(output: 0x1aab010)
Your problem is clearly the scope of the allocation. Moving the allocation to main fixes this.
Yay your program now works!
This should give you insight into how to use the debugger to solve these kinds of simple errors in the future.
Edit:
As one op pointed out, your underlying problem is that pointers passed to functions in c are treated as local objects, so when you exit the function, any allocated memory to single pointers is tossed in the bitbucket.
Double pointers allow you to actually allocate within functions, you just have to be careful to dereference them as necessary. So your working code could also be:
#include <malloc.h>
#include <stdio.h>
typedef struct node
{
struct node *leftChild, *rightChild;
int value;
} bst;
void insert(bst** b, int i)
{
(*b)=(bst*)malloc(sizeof(bst));
(*b)->value=i;
(*b)->leftChild = NULL;
(*b)->rightChild = NULL;
printf("[%i]",(*b)->value);
return;
}
main()
{
bst* b;
insert(&b,5);
printf("[%i]",b->value);
free(b);
}
bst* b; declares a pointer... but doesn't allocate the actual structure.
allocate and initialize b and you'll be onto your next step.
This is slightly a style discussion but your main should look either like this.
main()
{
bst *b;
b = (bst*)malloc(sizeof(bst));
/* more initialization */
insert(b, 5);
free(b);
}
or like this
main()
{
bst *b;
bst_new(&b);
insert(b, 5);
bst_free(&b);
}