I was studying linked lists, and I had this question: Why would we need to declare the datatype for a double pointer? I think that, if you dereference such pointer (say dptr
(a pointer to a pointer) ), you'd get a pointer to insert_datatype
. Now if I were to just change this value, that is just changing where the dptr
points to. And hence when I dereference this double pointer twice, I would still be dereferencing a structure (in my below code). So is it really necessary to declare the datatype for double pointers if we are using them to only dereference once? I can see why we would need to know the datatype while dereferencing the dptr
twice, as *dptr
would give us a pointer to insert_datatype
, and when I do **dptr
, it would be needed to know what is the datatype *dptr
is pointing to.
#include "stdio.h"
#include "stdlib.h"
typedef struct Node{
int data;
struct Node* next;
}Node;
void addAtBeg(int**, int);
int main(){
int a;
Node* head=NULL;
printf("Enter data:\n");
scanf("%d",&a);
addAtBeg(&head,a);
printf("%#x\n",head);
printf("%d\n",(*head).data);
return 0;
}
void addAtBeg(int** head, int a){
Node* p;
p=(Node *) malloc(sizeof(Node));
p->data=a;
p->next=*head;
*head=p;
}
I also can seem to just declare a long long int* head
as the parameter receiving the &head
in the addAtBeg
, and it just works fine, as *head=p
would just fetch 8 bytes, and write the memory address at that location.
So, are these both behaviors UB or are valid syntaxes?
CodePudding user response:
Pointers to different types don't necessarily have the same representation. On most systems you're likely to come across this will be the case, but it's not true in general.
While it is legal to convert an object pointer of one type to an object pointer to another type and back, it is not allowed to dereference the converted pointer except in a few cases which don't apply here.
So this:
*head=p;
Triggers undefined behavior.
CodePudding user response:
Using either int** head
or long long int* head
in addAtBeg
's prototype is undefined behavior (specifically, the moment you do *head = p;
, assigning a Node*
to something that is thought to be a non-Node*
). long long int* head
is worse, in that it's far more likely to trigger a size mismatch (e.g. on 32 bit systems, the Node*
you're dereferencing to will typically be 32 bits, while long long int
is 64 bits, so the assignment would overwrite four bytes from neighboring value(s) on the stack). Both will probably work just fine (assuming you disable/ignore all warnings) up until the point where it turns out sizeof(long long int) != sizeof(Node*)
or (less commonly) sizeof(int*) != sizeof(Node*)
.
The real question is why you would do this? You still need to pass &head
(that's unavoidable, head
needs to be reassigned by the callee). There's no performance benefit. Since you're passing by pointer, you don't need the definition of Node
, just a forward declaration if the declaration/definition isn't in that file. The function is replacing the pointer entirely, and is clearly aware it's working with a Node
, so no information hiding or implementation details are being hidden from anyone. So what are you getting out of violating the type system?