How do you pass a struct Pointer in C? - c

I am a little confused by how pointers are passed in functions by reference?
For instance, here's some code that I have written
(I didn't copy the entire function, just the part of it that is relevant)
metadata * removeBlock(metadata *first)
{
metadata *temp = first;
if(first == NULL || first -> prev == NULL)
{
first -> in_use = 1;
first = NULL;
return temp;
}
}
What I want is that when the function returns, the original value passed in should be set to NULL.
Here is how I call the function, (this line of code will pull a metadata structure from a region in heap, it works correctly, I have debugged and made sure that after this, struct really points to a valid metadata struct)
metadata *strct = ((metadata *)ptr - sizeof(metadata));
removeBlock(strct);
However, after this method returns, strct is still the same value it was before I had passed it in the function. I tried passing in &strct, but that just threw an invalid cast exception. What is the best way to pass in the struct as an argument?
Thankyou.

I don't think what you want is a good design - if the user of your function wants the pointer set to null (why?) it would make sense to reset the value using the return value from your function.
Anyway, you'll want a pointer-to-a-pointer, like so:
metadata* removeBlock(metadata** first) {
metadata* temp = *first;
if( temp == NULL ) return temp;
if( temp->prev == NULL ) {
temp->in_use = true;
*first = NULL;
}
return temp;
}
metadata* strct = ((metadata*)ptr - sizeof(metadata));
removeBlock(&strct);

As #SheerFish said, all we have in C is pass-by-value. However, one can simulate pass-by-reference with a pointer.
void func(struct foo **fooptr) { *fooptr = 0; }
int main(int argc, char **argv) { struct foo *fooptr; func(&fooptr); }
This is passing a pointer ptr to the variable's value (never mind if that value was a pointer), allowing the function to play with the original value with *ptr. This technique is sometimes called pass-by-address, and is the closest C has to pass-by-reference.

If you pass by 'reference in C', you're going to need to keep in mind referencing and de referencing via ->/** and *. This bit of code I wrote may help you a bit
int delete_node(struct node** head, int target)
{
if(*head == NULL)
return 0;
if((*head)->data == target)
{
temp = *head;
*head = (*head)->next;
free(temp);
return 1;
}
}
Function call:
delete_node(&head, data)
You're working with direct memory pointer manipulation. You're tossing the location of the struct in memory, de referencing it, then changing the value at that memory location.

I didn't read all of the details, but this part jumped out as incorrect:
(metadata *)ptr - sizeof(metadata)
Pointer arithmetic is done in the units of the type, whereas sizeof gives you measurements in bytes.
So I suspect what you're trying to say is:
(metadata *)(((char*)ptr) - sizeof(metadata))
This is also making some assumptions about the machine you're running on, i.e. metadata may need to be padded to ensure that the fields are properly aligned for this use. If sizeof(metadata) is not a multiple of the word size, this will fail on a lot of architectures. (But x86 will let it slide, albeit with performance costs and some implications like atomic ops not working on the fields.)

pointers are passed by value. anything in c is passed by value. so in order to change the pointer passed to the function, it should receive metadata **first.
besides, you should use
metadata *strct = ((metadata *)ptr - 1);
as pointer arithmetic is done with multiples of sizeof(*p). so this is equivalent to
metadata *strct = ((metadata *)((char*)ptr - sizeof(metadata)));

Related

Initialising struct through a function whose members are dynamic arrays

I have the following struct definition (names have been generalised):
typedef struct structure{
int *array1;
int *array2;
} structure_t;
I need to initialise this struct data structure through a function which takes in the sizes of the two arrays as parameters. The function returns a pointer to that data structure and NULL on fail.
I am getting confused on how to go about this. I know that I cannot return a pointer of a locally declared struct, and I also know that I need to dynamically allocate memory for the two members array1 and array2, since the size is not known on compile time (it's inputted by this user). I have tried the following:
structure_t* init(int size1, int size2)
{
structure_t *st = malloc(sizeof (structure_t));
if(st == NULL) return NULL;
st->array1 = malloc((sizeof (int))*size1);
st->array2 = malloc((sizeof (int))*size2);
return st;
}
I have checked and everything is being initialised. But then when I come to free the memory it is not working properly, as only the pointer to array1 is being changed to NULL.
bool destroy(strcuture_t *st)
{
free(st->array1);
free(st->array2);
free(st);
if (st == NULL)
return true;
else
return false;
}
What am I doing wrong?
free does not change the value of the pointer passed to it. It cannot, as it receives only the value and not a reference to the pointer.
To record that a pointer no longer points to valid memory, you can set the pointer to NULL yourself after calling free, as with:
free(st);
st = NULL;
Further, once the memory pointed to by st is freed, the C standard does not define the behavior of accessing st->array1 or st->array2 or even of using the value of st at all. There is no reason to expect that checking st->array1 or st->array2 for a null pointer will produce any particular result. The comparison may evaluate to true, may evaluate to false, may cause your program to abort, or may cause your program to misbehave in other ways.

Valgrind + C: Handling uninitialized pointers without error

Maybe I got too involved with testing my assignment, but here's my dilemma:
This is my offending function (more or less):
struct thing{
char* data;
}
int function(struct thing* arg){
if(arg->data == NULL)
return -1;
}
This is my offending input:
struct thing *x = malloc(sizeof(struct thing));
function(x);
And valgrind outputs this when I test it:
Conditional jump or move depends on uninitialised value(s)
I'm about 99% sure that this is because valgrind has an issue with evaluating x->data when it hasn't been initialized to NULL or malloced. Is there a workaround for this?
To my opinion valgrind is right in complaining here because data is in fact not initialized and function is not able to determine if it was. The function can only check if it has the defined value you used for initialization.
So I would consider using a creation function that does the initialization as well. This way you could also allocate the memory for data right away when passing its size as parameter (or just NULL if you want to do that later).
struct thing *newThing(size_t dataSize)
{
struct thing *t = malloc(sizeof (struct thing));
if (t)
t->data = malloc(sizeof(char) * dataSize);
// or t->data = NULL if it should be done later
return t;
}
You are defining a function like this:
int function(struct thing *arg) {
if (arg->data == NULL) {
// abort here
return 1;
} else {
// use the value here
return 0;
}
}
Here, Valgrind is right about complaining, because it's impossible to achieve the intended behavior for function(). If arg->data is not explicitly initialized, then its value is indeterminate, and the if statement could execute any of the two branches depending on whichever random value it reads from arg->data.
In general, it's impossible for the function to work correctly, because:
If the check arg->data == NULL fails, the rest of the code will assume that arg->data is not NULL and therefore use the invalid value, most likely causing a crash or other kind of problem.
If the check arg->data == NULL passes, the rest of the code will wrongfully assume that the data is known to be invalid, and do any other kind of thing assuming that data was intended to be NULL, like for example call some cleanup code at the wrong time.
Other than this, if you really want to be pedantic, reading uninitialized variables is actually undefined behavior in C.
The right thing to do in your case is to either use calloc() instead of malloc() or to manually set x->data = NULL right after malloc().
The memory allocated by malloc is not initialized.
You need to set it to the know state:
struct thing *x = calloc(1,sizeof(*x));
or
struct thing *x = malloc(sizeof(*x));
memset(x, 0, sizeof(*x);
you can also of course just assign some values to the struct members,
struct thing *x = malloc(sizeof(*x));
x -> data = NULL;

Pointers Not Reflecting Change when value changed in a function

I am implementing a Linked List using an array.It has a function Reverse
defined below
void Reverse(List *l)
{
List *m=CreateList(ListSize(l));
for(int i=0;i<l->count;i++)
{
m->array[i]=l->array[l->count-i-1];
m->count++;
}
free(l->array);
free(l);
l=m;
//Traverse(l); Here it prints the reversed List
}
It takes a List structure as a argument. I am calling it from main like this
int main()
{
size=5;
List *l=CreateList(size);
Reverse(l);
Traverse(l); //Here the list is not printing the reversed list !
}
Why are the changes I made to l in reverse not showing up in main() ?
Thanks !
You have to pass a double pointer to be possible to change where does the pointer points.
void Reverse(List **l);
Reverse(&l);
Its not possible in C to pass by reference, from www-cs-students.stanford.edu:
In C, Pass-by-reference is simulated by passing the address of a
variable (a pointer) and dereferencing that address within the
function to read or write the actual variable. This will be referred
to as "C style pass-by-reference."
When you do l = m, you are just setting l within Reverse. l is part of the argument list to Reverse. When you change the value, it only changes it for the duration of the call to Reverse. It never gets propagated to the caller.
You can use Filip's method to propagate the value.
Or, you can:
redefine Reverse as: List *Reverse(List *l)
add return m as the last statement in it
and call via: l = Reverse(l).
But, this is a bit wasteful. You can also do an in-place reversal:
void Reverse(List *l)
{
int left = 0;
int right = l->count - 1;
// NOTE: this should be whatever the type of array[0] is:
int tmp;
for (; left < right; ++left, --right) {
tmp = l->array[left];
l->array[left] = l->array[right];
l->array[right] = tmp;
}
}
UPDATE:
When I do *l=*m in the last line the code works but I dont know why !
Even that has a bug.
That is because you are replacing the entire contents of what l points to. Essentially, you created a new [reversed] list m, filled it, then copied it back to l.
Doing free(l) before doing *l = *m is a bug. You are dereferencing a pointer to freed memory.
You do want to do free(l->array), but not free(l).
Also, after doing *l = *m, you need to do free(m). Otherwise, you have a memory leak on the struct that m points to [but not what m->array points to because it gets saved in l->array].
This is needless complexity and error prone.
And you did twice as much work as needed. When you do *l = *m, you are really doing:
l->count = m->count;
for (int i = 0; i < m->count; ++i)
l->array[i] = m->array[i];
In other words, don't do the *l = *m, even if it seems to work. Use one of the three easier/correct ways.
UPDATE #2:
It seems to work fine though(*l=*m) even if the memory that l points to doesn't exist . Please tell me what does the function free() actually does in this case
Yes, it seems to work fine, but it does not work fine [in the general case].
After you do free(l), what l pointed to is unavailable. The memory allocator assumes that you have no further use for this memory [because when you do free(l), you are telling it so]. The allocator is at liberty to do whatever it wishes with that memory:
In a multithreaded environment, between the time you call free(l) and it returning control to you, another thread could have done a malloc and now you have two threads pointing to the same memory and using it for different purposes. Doing the subsequent *l = *m would corrupt the data that the other thread is storing there. This introduces a subtle, intermittent, hard to find bug.
In a single threaded environment, the memory allocator might use the memory to store metadata for its own [internal] purposes. Again, before it returns from free(l). So, when you do *l = *m, you could be corrupting the allocator's internal data structures (or vice versa). The next time you issue a malloc, the allocator might segfault.
The next time you issue a malloc, it might return a pointer to the same memory that l is/was pointing to (e.g. you create a second list [in main] by calling l2 = CreateList(10)). Now, l and l2 have the same value (i.e. they point to the same struct). So, instead of l and l2 being separate lists, they collide. Or, l2 might be different, but l2->array might overlap with l
Below is one example of a general resource allocation/release problem. Because you don't know what the allocate or release functions do internally, you can't be safe accessing anything inside the resource after it's been released/freed. The release function below adds one line to illustrate why it's unsafe to do what you did with *l = *m
#include <malloc.h>
struct resource {
int count;
int *array;
};
struct resource *
allocate_resource(int count)
{
struct resource *ptr;
ptr = malloc(sizeof(struct resource));
ptr->count = count;
ptr->array = malloc(sizeof(int) * count);
return ptr;
}
void
free_resource(struct resource *ptr)
{
free(ptr->array);
// prevents "*l = *m" from "seeming to work"
ptr->array = NULL;
free(ptr);
}
int
main(void)
{
while (1) {
struct resource *x = allocate_resource(20);
// x may be utilized here ...
free_resource(x);
// x may _not_ be utilized here ...
// this will segfault
x->array[0] = 23;
}
return 0;
}
In C, function arguments are always passed by value. This means that in the function ReverseList() l is a copy of the pointer to List that was passed in the function call. So, when the reversed List is created, and the address is assigned to l within the function, this has no effect on the original pointer to List in main() (since l within Reverse() is only a copy of l within main()). It may help to give variables in functions names that are different from their corresponding names in callers to help keep this sort of thing straight.
One solution is to pass a pointer to pointer to List into the Reverse() function:
void Reverse(List **lptr)
{
List *m=CreateList(ListSize(*lptr));
for(int i=0;i<(*lptr)->count;i++)
{
m->array[i]=(*lptr)->array[(*lptr)->count-i-1];
m->count++;
}
free((*lptr)->array);
free(*lptr);
*lptr=m;
//Traverse(l); Here it prints the reversed List
}
Call this function with Reverse(&l). Here, since a copy of a pointer to a pointer to List is used in Reverse(), and lptr points to the pointer l in main(), lptr can be dereferenced and the value of m can be assigned to the actual pointer to List (l) found in main().
An alternative solution is to change the Reverse() function to return a pointer to List to the caller. Then the returned value is assigned to l:
List * Reverse(List *l)
{
List *m=CreateList(ListSize(l));
for(int i=0;i<l->count;i++)
{
m->array[i]=l->array[l->count-i-1];
m->count++;
}
free(l->array);
free(l);
return m;
//Traverse(l); Here it prints the reversed List
}
int main(void)
{
size=5;
List *l=CreateList(size);
l = Reverse(l);
Traverse(l); //Here the list is not printing the reversed list !
return 0;
}
Also, if CreateList() is not already checking for allocation errors, the code should be checking for these before freeing the previous allocations.

List to list assignment becomes garbage

I'm having some trouble with the following:
void BuildList(cs460hwp hw)
{
FILE* fp;
fp = fopen("HW2input.dat", "r");
if(fp == NULL)
{
printf("Couldn't open the file.");
return;
}
int numStudents;
int i;
bool success;
char* dueDate = malloc(9*sizeof(char));
char* course = malloc(7*sizeof(char));
char* wsuid = malloc(9*sizeof(char));
char* subDate = malloc(9*sizeof(char));
double points1 = 0;
double points2 = 0;
cs460hwp stuInsert = NULL;
fscanf(fp, "%d", &numStudents);
fscanf(fp, "%s", dueDate);
for(i = 0; i < numStudents; i++)
{
stuInsert = malloc(sizeof(cs460hwp));
fscanf(fp, "%s %s %s %lf", course, wsuid, subDate, &points1);
strcpy(stuInsert->course, course);
strcpy(stuInsert->wsuid, wsuid);
strcpy(stuInsert->subdate, subDate);
stuInsert->points1 = points1;
stuInsert->points2 = CalculatePoints(dueDate, subDate, points1);
stuInsert->nextPtr = NULL;
if(hw == NULL)
{
hw = stuInsert;
}
else
{
stuInsert->nextPtr = hw;
hw = stuInsert;
}
}
free(course);
free(wsuid);
free(subDate);
free(dueDate);
PrintGrades(hw);
fclose(fp);
}
struct hwpoints
{
char course[7];
char wsuid[9];
char subdate[9];
double points1;
double points2;
struct hwpoints *nextPtr;
};
typedef struct hwpoints *cs460hwp;
My goal here is to insert every entry to the top of the list. However, whenever I try to assign anything to nextPtr (such as in the else clause), it gets filled with garbage values. They're mostly truncated versions of old data, which leads me to believe they're being taken from the heap. I've been reading (a lot), but I'm having trouble finding advice on this particular problem.
nextPtr always becomes junk, and nextPtr->nextPtr causes a segfault. For every iteration of the loop. hw remains fine, but its pointer value never gets updated properly.
Even when I've attempted to move the memory allocation for the struct into a function, I've had the same (or similar) issues.
Can anyone point me in the right direction?
Two problems.
1) As pb2q mentioned, you are passing a pointer to a struct and trying to assign what the arg points to. That's allowed by the compiler, but it doesn't do anything for you outside the function. It might be OK in your case if:
void main()
{
cs460hwp hw = NULL;
BuildList(hw);
return;
}
Is the whole of your function. I don't know the assignment so you need to figure out if that's acceptable to you or not.
2) The much bigger problem:
stuInsert = malloc(sizeof(cs460hwp));
Did you check what sizeof(cs460hwp) comes out to be? it's 4. You're allocating enough memory for the size of a pointer, not the size of your structure. I'm pretty sure this is not what you want to do and this is what is killing you. Just for kicks, replace it with malloc(100) and see if your problem goes away. If so you just need to figure out what size you really want. ;)
A problem with your BuildList function is that you're passing a pointer to a struct hwpoints, and you're trying to re-assign what the argument points to. Since function arguments in C are pass-by-value you're only changing the copy of the pointer that your function receives, and those changes won't be reflected in the caller.
You can use this pointer to modify the contents of the list: you can change e.g. hw->course or hw->nextPtr, you can move elements around in your list. But you can't change what the head, hw points to, so you can't insert elements at the beginning of the list.
If you want to change your head pointer, as in these statements:
hw = stuInsert;
// ...
hw = stuInsert;
Then you'll need to pass a pointer to the pointer:
void BuildList(cs460hwp *hw)
And de-reference it as necessary in the body of the function.
I can't be sure that this is the cause of the output that you're observing, which may be due to other problems. But if, after some number of calls to BuildList, beginning with a head pointer equal to NULL, you're trying to print your list assuming that it has valid nodes, you could see garbage data.
Thanks to #Mike's answer, we see also that you're not allocating enough space for your list nodes:
stuInsert = malloc(sizeof(cs460hwp));
Will only allocate enough space for a pointer, since cs460hwp is typedef'd to be a pointer to struct hwpoints. You need to allocate enough space for the structure, not a pointer to it:
stuInsert = malloc(sizeof(struct hwpoints));

Seg Fault when copying a pointer

How come the following code result in seg fault? Basically after I copy the head pointer to temp, the head pointer gone.
typedef struct address * paddress; // defines struct pointer
void addAddressToList(paddress head, int addr[])
{
if (head == NULL) {
//head->addrArray = addr; // if list is initially empty
} else {
paddress temp;
temp = head;
while (temp->right != NULL) {
temp = temp->right; // go to end of the list
}
paddress newAddress = (paddress)malloc(sizeof(paddress*));
newAddress->intAddr = addr;
newAddress->right = NULL;
newAddress->left = temp; // connect the new address
temp->right = newAddress;
}
}
main() {
paddress addressListHead;
addressListHead = (paddress)malloc(sizeof(paddress*));
int intAddr1[] = {1,2,3,4,5,6,7};
char hexAddr1[] = "123456";
int intAddr2[] = {16,14,13,12,11};
char hexAddr2[] = "fedcb";
addressListHead->intAddr = intAddr1;
addressListHead->hexAddr = hexAddr1;
addAddressToList(addressListHead, intAddr2);
}
paddress addressListHead;
addressListHead = (paddress)malloc(sizeof(paddress*));
It seems to get rid of the compilation error, you have type casted what malloc is returning to paddress. addressListHead is a pointer, which means it can hold the address of an object but not the address of a pointer. The malloc here statement doesn't create an object. You need to change this -
addressListHead = (paddress)malloc(sizeof(paddress*));
to
addressListHead = (paddress)malloc(sizeof(struct address));
in main and addAddressToList functions.
Segmentation fault :
else {
paddress temp;
temp = head;
while (temp->right != NULL) {
temp = temp->right; // go to end of the list
}
I understand paddress::right is a pointer with the fact you are comparing it to NULL. But what is temp::right is initialized to. It is pointing to some garbage address and so you cannot ask for it to compare with NULL. Make it point to a valid memory location.
There is more than one problem in your code.
Firstly, the usual advice: stop using sizeof with type names (as much as possible). Use sizeof with expressions, not types. Type names belong in declarations and nowhere else.
Your problem with memory allocation could have been prevented if you used this malloc idiom
T *p = malloc(n * sizeof *p);
i.e. sizeof should be applied to *p, where p is the pointer to the array you are allocating and n is the total number of elements in that array. That way you never have to guess what type name you should specify under sizeof (an that way your code becomes type-independent).
In your case you are allocating just one object, so the code should look as
paddress newAddress = malloc(sizeof *newAddress);
(And don't cast the result of malloc - there's absolutely no point in doing that).
Secondly, when you the head element of the list, you need to initialize all the fields. Yet you never initialize right (or left) in the head element. Hence the crash even when the correct amount of memory is allocated.
In main(), you want
addressListHead = (paddress)malloc(sizeof(address));
That makes sure you get enough bytes to hold an address.
First error:
addressListHead = (paddress)malloc(sizeof(paddress*));
paddress* means a pointer to paddress which itself is a pointer to struct address. Hence paddress* is a pointer to a pointer to struct address. You would want to do:
addressListHead = (paddress)malloc(sizeof(struct address));
Also, I see that you made a similar mistake yesterday. Why do I get a seg fault? I want to put a char array pointer inside a struct
It's important to understand the concept of pointers properly. I would definitely recommend you to go through some tutorials on pointers. If you need help with that, let me know.

Resources