Passing in double pointer as params in C - c

Let's say there is an API as follows:
void myAPI(int8** ptr)
If I want to pass a pointer of a struct into this function this is my code:
typedef myStruct {
int a;
};
myStruct *ptr = NULL;
memset(ptr, 0, sizeof(myStruct));
myAPI((int8**)&ptr);
My question is what if instead of using ptr I now have the following?
myStruct myStruct_info;
memset(&myStruct_info, 0, sizeof(myStruct));
Would I also be doing myAPI((int8**)&myStruct_info)?

Would I also be doing myAPI((int8**)&myStruct_info)?
Short answer is no you can not.
&myStruct_info is the address of your myStruct_info variable.
And the parameter for your myAPI function needs the address of a pointer to a variable.
If you say int8 ** a = (int8**)&myStruct_info; then:
a - holds the address of myStruct_info
*a - the value of myStruct_info
and
**a - means that you take the value stored in myStruct_info and use it as a pointer - REALLY BAD -

Related

Pointers, structs and memset in C [duplicate]

This question already has answers here:
How to find the size of an array (from a pointer pointing to the first element array)?
(17 answers)
Why isn't the size of an array parameter the same as within main?
(13 answers)
Closed 5 months ago.
I have been learning C for a few days now without any other programming experience, so I might not be clear when asking my question. It is mostly about pointers. For convenience purposes I named the variables so no one gets confused.
#include <stdio.h>
#include <string.h>
struct car {
char* name;
int speed;
float price;
};
void fun(struct car* p);
int main(void) {
struct car myStruct = { .name = "FORD", .speed = 55, .price = 67.87 };
fun(&myStruct);
printf("%d\n", myStruct.name == NULL);
printf("%d\n", myStruct.speed == 0);
printf("%d\n", myStruct.price == 0.0);
return(0);
}
void fun(struct car* p) {
memset(p, 0, sizeof(p));
}
This is my code.
I declare the struct car type globally, so it can be seen by other functions.
I write a function prototype that takes an argument of type struct car* and stores a copy of the argument into the parameter p that is local to the function.
Later, I write the actual function body. As you can see, I call the memset function that is in the string.h header. According to Linux man pages, it looks like this void* memset(void* s, int c, size_t n);.
What the memset function does in this case, is it fills the first sizeof(struct car* p) bytes of the memory area pointed to by the struct car* p with the constant byte c, which in this case is 0.
In the main function I initialize the myStruct variable of type struct car and then call the function fun and pass the address of myStruct into the function. Then I want to check whether all of the struct car "data members" were set to 0 by calling the printf function.
The output I get is
1
0
0
It means that only the first "data member" was set to NULL and the rest weren't.
On the other hand, if I call the memset function inside the main function, the output I get is
1
1
1
If I understand pointers correctly (it's been a few days since I've first heard of them, so my knowledge is not optimal), struct car myStruct has its own address in memory, let's say 1 for convenience.
The parameter struct car* p also has its own address in memory, let's say 2 and it stores (points to) the address of the variable struct car myStruct, so to the 1 address, because I passed it to the function here fun(&myStruct);
So by dereferencing the parameter p, for example (*p).name, I can change the value of the "data member" variable and the effects will be seen globally, because even though the p parameter is only a copy of the original myStruct variable, it points to the same address as the myStruct variable and by dereferencing the pointer struct car* p, I retrieve the data that is stored at the address the pointer points to.
So (*p).name will give me "FORD" and (*p).name = "TOYOTA" will change the data both locally in the function fun and globally in other functions as well, which is impossible without creating a pointer variable, if I do p.name = "TOYOTA", it changes only the value of the copy, that has its own address in the memory that is different from the address of the original struct variable, of the "data member" variable name locally, inside the function fun. It happens, because in this case I operate only on the copy of the original myStruct variable and not on the original one.
I think that in C there is only pass by value, so essentially every parameter is only a copy of the original variable, but pointers make it so that you can pass the address of the original variable (so it's like "passing by reference", but the copy is still made regardless, the thing is that then the function operates on the original address instead of on the parameter's address).
What I don't know is, why the memset function only changes the first "data member" variable to NULL and not all of them ?
void fun(struct car* p) {
memset(p, 0, sizeof(p));
p->name = NULL;
p->speed = 0;
p->price = 0.0;
}
If I do this then it changes all the values to NULL, 0, 0, but I don't know, if it is a good practice to do that as it is unnecessary in this case, because I explicitly initialize all the "data members" in the struct with some value.
void fun(struct car* p) {
memset(&p, 0, sizeof(p));
}
This also works and gives NULL, 0, 0. So maybe I should actually pass &s into the function instead of s, but I don't know how this works. The function void* memset(void* s, int c, size_t n); takes void* as the argument and not void**, the latter is understandable, because:
struct car myStruct = { .name = "FORD", .speed = 55, .price = 67.87 }; // It has its own address in memory and stores the struct at this address
struct car* p = &myStruct; // It points to the address of myStruct and retrieves the data from there when dereference happens, so first it goes to address 1 and then gets the data from this address
void** s = &p; // It points to the address of p and retrieves the data from there when double dereference happens, so it first goes to address 2 and gets the data and the data is address 1, then it goes to address 1 and gets the data, so the struct
But void* means pointer to void, so to any data type. It confuses me why void* s = &p; works, even though p itself is a pointer, so s should be a pointer to pointer to void, so void** s instead of void* s.
Also the memset function returns a pointer to the memory area s, so if s = &p and p = &myStruct, then it returns a pointer to the memory area of the struct, so a pointer to &myStruct. Maybe that's why it works.
In this call
memset(p, 0, sizeof(p));
you are setting to 0 only a part of object of the structure that is equal to the size of the pointer p.
Instead you need to write
memset(p, 0, sizeof(*p));
that is to set the whole object of the structure type with 0.
Pay attention to as the variable p is a pointer then this record
p.name = "TOYOTA";
is just syntactically incorrect.
This function
void fun(struct car* p) {
memset(&p, 0, sizeof(p));
}
does not set the passed object of the structure type through the pointer p to zeroes. Instead it sets to zeroes the memory occupied by the local variable p itself.
As for this question
But void* means pointer to void, so to any data type. It confuses me
why void* s = &p; works, even though p itself is a pointer, so s
should be a pointer to pointer to void, so void** s instead of void*
s.
then according to the C Standard (6.3.2.3 Pointers_
1 A pointer to void may be converted to or from a pointer to any
object type. A pointer to any object type may be converted to a
pointer to void and back again; the result shall compare equal to the
original pointer.
So you can write for example
struct car myStruct =
{
.name = "FORD", .speed = 55, .price = 67.87
};
struct car *p = &myStruct;
void *q = &p;
and then
( *( struct car ** )q )->name = "My Ford";

How to access second member of struct via pointer?

I have seen the first address of struct is simultaneously the first address of first member of that struct. Now what I would like to understand is, why I need always double pointer to move around in the struct:
#include <stdio.h>
#include <stdlib.h>
struct foo
{
char *s;
char *q;
};
int main()
{
struct foo *p = malloc(sizeof(struct foo));
char ar[] = "abcd\n";
char ar2[] = "efgh\n";
*(char**)p = ar;
*(char**)((char**)p+1) = ar2; //here pointer arithmetic (char**)p+1
printf("%s\n",p->q);
}
the question is, why do I need char** instead of simple char*?
What I saw in assembler is in case of simple char*, the arithmetic would behave like normal char. That is -> the expression of (char*)p+1 would move the address p just by one byte (instead of 8 as address are 8 bytes long). But yet the type char* is address, so I don't get why the arithmetic behave like the dereference type instead (plain char -> one byte).
So the only solution for me was to add another indirection char**, where the pointer-arithmetic magically takes 8 as size. So why in structs is needed such bizarre conversion?
You are doing funny things. You should just do:
struct foo *p = malloc(sizeof(struct foo));
char ar[] = "abcd\n";
char ar2[] = "efgh\n";
p->s = ar;
p->q = ar2;
First of all, what you are doing is slightly bizarre. It's also unsafe, since there may be padding between struct members and your address calculation may be off (that's likely not true in this particular case, but it's something to keep in mind).
As to why you need multiple pointers...
The type of p is struct foo * - it's already a pointer type. Each of the members s and q have type char *. To access the s or q members, you need to dereference p:
(*p).s = ar; // char * == char *
(*p).q = ar2; // char * == char *
So if you're trying to access the first character pointed to by s through p, you're trying to access a character through a pointer (s) through another pointer (p). p does not store the address of the first character of s, it stores the address of the thing that stores the address of the first character of s. Hence the need to cast p to char ** instead of char *.
And at this point I must emphasize DON'T DO THIS. You can't safely iterate through struct members using a pointer.
The -> operator was introduced to make accessing struct members through a pointer a little less eye-stabby:
p->s = ar; // equivalent to (*p).s = ar
p->q = ar2; // equivalent to (*p).q = ar2
As the address of an object of a structure type is equal to the address of its first member then you could write for example
( void * )&p->s == ( void * )p
Here is a demonstrative program
#include <stdio.h>
#include <stdlib.h>
struct foo
{
char *s;
char *q;
};
int main(void)
{
struct foo *p = malloc(sizeof(struct foo));
printf( "( void * )p == ( void * )&p->s is %s\n",
( void * )p == ( void * )&p->s ? "true" : "false" );
return 0;
}
Its output is
true
So the value of the pointer p is equal to the address of the data member s.
In other words a pointer to the data member s is equal to the pointer p.
As the type of the data member s is char * then pointer to s has the type char **.
To assign the pointed object you need to cast the pointer p of the type struct foo * to the type char **. To access the pointed object that is the data member s you have to dereference the pointer of the type char **.
As a result you have
*(char**)p = ar;
Now the data member s (that is the pointer of the type char *) is assigned with the address of the first element of the array ar.
In the second expression the left most casting is redundant
*(char**)((char**)p+1) = ar2;
^^^^^^^^
because the expression (char**)p+1 is already has the type char **. So you could just write
*((char**)p+1) = ar2;
why do I need char** instead of simple char*
With pointer usage, the the left side of the assignment, code needs the address of the object.
*address_of_the_object = object
As the object is a char *, the type on the left side, the address of the object, needs to be type char **.
How to access second member of struct via pointer?
Better to instead use the sensible:
p->q = ar2;
... then the convoluted:
// |-- address of p->q as a char * ----|
*((char **) ((char *)p + offsetof(struct foo, q))) = ar2;
//|------------ address of p->q as a char ** ---|
OP's *(char**)((char**)p+1) = ar2; is amiss as it does the wrong pointer math and assumes no padding.
Convoluted approach details.
To portable find the offset within a struct, use offsetof(struct foo, q). It returns the byte offset and will accounts for potential padding. Add that to a char * version of the struct address to do the proper pointer addition to form the address of p->q. That sum is a char *, Convert to the type of the address of the object. Lastly de-reference it on the LHS as part of the assignment.

Pointer initialization: address or value

I have a question regarding pointer initialization in C.
I understand that *ptr will give the value of that pointer is pointing to.
ptr will give you the address.
Now I got following syntax:
int *ptr = (int *) malloc(sizeof(*ptr));
Why is *ptr being initialized with an address of the Heap and not a value? malloc() returns an address right?
Shouldn't it be:
int *ptr;
ptr = malloc(...);
With *ptr, * is acting as the dereferencing operator.
With int *ptr, * is acting as part of the type declaration for ptr.
So the two things are entirely different, even though * is used. (Multiplication and comment blocks are further uses of * in C).
In that line, int * is the type.
int *ptr = (int *) malloc(sizeof(*ptr));
Is just this compressed into one line:
int *ptr;
ptr = (int *) malloc(sizeof(*ptr));
Actually , this:
int *ptr = (int *) malloc(sizeof(*ptr));
Is just short syntax for this:
int *ptr;
ptr = malloc(...);
The * is used for defining a type pointer and not to dereference the pointer .
Both snippets above do the same thing.
In the first case, the * before ptr is not the derefernece operator but is part of the definition of the type. So you actually are assigning a value to (initializing, actually) ptr, not *ptr.
The difference between
int *ptr = (int *) malloc(sizeof(*ptr));
and
int *ptr;
ptr = malloc(...);
is basically the same as the difference between
int i = 5;
and
int i;
i = 5;
The first variant defines and initializes a variable in one go. The second variant defines the variable but leave it uninitialized, and then assign a value to it.

Passing structs into function by address vs a pointer in C

I was hoping someone could help me figure out why one version of the below code works, while the other doesn't. Below I've included the initArray method, stored in "worksheet.c". The function is accessed in main, both versions are given below.
void initArray(struct dynArray *a) {
a->data = malloc(10 * TYPE_SIZE);
assert(a->data != 0);
a->size = 0;
a->capacity = 10;
}
This works. I create a dynArray struct and pass it to initArray by reference.
#include "worksheet0.h"
#include <stdio.h>
int main(void)
{
struct dynArray b;
initArray(&b);
return 0;
}
This fails with a seg fault. I thought that passing b here would be the same as passing the struct by reference.
int main(void)
{
struct dynArray *b = NULL;
initArray(b);
return 0;
}
Because in the second case there is no memory allocated to which the struct pointer points to. It is simply a pointer having the value NULL. On your case by dereferencing the value NULL you have invoked undefined behavior.
It would work if you allocate memory, make changes to it and then return it's value. [But then you have to return the address of the allocated memory.] OR you can pass the address of the pointer variable and allocate memory to which dereferenced pointer (here the pointer has type struct dynAray**) would point to and make changes to it.
Let's be more clear now slowly:
Why the first case works? You have a struct dynArray variable whose address you have passed into the function and then you have accessed the content of that address - wait! that means you have accessed the struct dynArray variable itself and made changes to its member variables. Yes that is what exactly happened in the first case.
In the second case, you have a pointer to struct dynArray. And then you passed it - de-referenced it. Where was it pointing to? Is it some struct dynArray variable's address that it contained? No. It was NULL. So it is wrong if you expect it to work.
The second would work - but you have to change things a bit! Let's see how:
struct dynArray* initArray() {
struct dynArray* a = malloc(sizeof *a);
assert(a != NULL);
a->data = malloc(10 * TYPE_SIZE);
assert(a->data != 0);
a->size = 0;
a->capacity = 10;
return a;
}
And in main()
struct dynArray* b;
b = initArray();
You don't even need to pass the pointer variable. That would be meaningless if you want to do it like this.
And you know you can also pass the address of the pointer variable so that you can make changes to it -
void initArray(struct dynArray** a) {
*a = malloc(sizeof **a);
assert((*a) != NULL);
(*a)->data = malloc(10 * TYPE_SIZE);
assert((*a)->data != 0);
(*a)->size = 0;
(*a)->capacity = 10;
}
For this in main() you would call it like this
struct dynArray* b;
initArray(&b);
In the first example a pointer holding the address of an actual struct is passed to the function. But, in the second example the pointer b does not point to a struct. Instead, this pointer is initialized to NULL, and when this null pointer is dereferenced in the initArray() function, undefined behavior ensues.

What parameters would I put in this function 'copy' to copy a to b?

struct info
{
int val;
};
void copy(struct info ** dst, struct info * src)
{
*dst = (struct info *)malloc(sizeof(struct info));
**dst = *src;
}
int main()
{
struct info *a, *b;
a = (struct info *)malloc(sizeof(struct info));
a -> val = 7;
copy( , );
a -> val = 9;
printf("%d", b->val);
}
I have tried (b, a), (*b, *a), (b, *a) and so one but the argument is always unexpected by the compiler. Have been trying for an hour with no result - just a half melted brain.
The first argument is supposed to be a pointer to a pointer. Since b is a pointer, you need to take its address, which is &b.
The second argument is supposed to be a pointer. a is a pointer, so you just pass it directly.
copy(&b, a);
* is for indirecting through a pointer to access what it points to. That's the exact opposite of what you want, which is to get a pointer to the variable itself. You use * inside the copy function to access what the pointers given point to.
BTW, you should also see Do I cast the result of malloc?
And don't forget to free the structures when you're done using them.

Resources