This question already has an answer here:
We have to create a binary tree with car models and modify it in different ways. The main problem for me is the use of a double pointer (struct tree_st **root
).
Why can't I just use a standard, single pointer?
Here's the code if you need more details of what I'm talking about:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX_SIZE 20+1
typedef struct auto_st //auto as in "car"
{
char marka[MAX_SIZE]; //marka as in "car model"
int kubikaza; // "cubage"
int godiste; // "year of production"
struct auto_st *left, *right;
}AUTO;
FILE *safe_fopen(char *filename, char *mode, int exit_code);
void init(AUTO **root);
AUTO *create_new(char marka[], int kubikaza, int godiste);
void add_location(AUTO *new, AUTO **root);
void read_file(FILE *in, AUTO **root);
void write_one(FILE *out, AUTO *root);
void write_all(FILE *out, AUTO *root);
void find(AUTO *root, char marka[]);
AUTO *newest(AUTO *root, int max_kub);
int main(int arg_num, char *args[])
{
AUTO *root;
if(arg_num!=4)
{
printf("WRONG NUMBER OF ARGUMENTS!\n");
exit(1);
}
int max_kub = atoi(args[1]);
char *in_filename = args[2];
char *out_filename = args[3];
FILE *in = safe_fopen(in_filename,"r",10);
FILE *out = safe_fopen(out_filename,"w",11);
init(&root);
read_file(in,&root);
write_all(out,root);
AUTO *best = newest(root, max_kub);
if(best==NULL)
{
printf("CAR DOESN'T EXIST!\n");
}
else
{
write_one(out,best);
}
find(root,"Ferrari");
fclose(in);
fclose(out);
return EXIT_SUCCESS;
}
FILE *safe_fopen(char *filename, char *mode, int exit_code)
{
FILE *pf = fopen(filename,mode);
if(pf==NULL)
{
printf("CAN'T OPEN FILE %s\n", filename);
exit(exit_code);
}
return pf;
}
void init(AUTO **root)
{
*root = NULL;
}
AUTO *create_new(char marka[], int kubikaza, int godiste)
{
AUTO *new = (AUTO*)malloc(sizeof(AUTO));
if(new == NULL)
{
printf("NOT ENOUGH RAM!!\n");
exit(5);
}
strcpy(new->marka,marka);
new->kubikaza = kubikaza;
new->godiste = godiste;
new->left = NULL;
new->right = NULL;
return new;
}
void add_location(AUTO *new, AUTO **root)
{
if(*root==NULL)
{
*root = new;
}
else if(strcmp((*root)->marka,new->marka)==1)
{
add_location(new,&((*root)->left));
}
else if(strcmp((*root)->marka,new->marka)==-1)
{
add_location(new,&((*root)->right));
}
}
void read_file(FILE *in, AUTO **root)
{
char tmarka[MAX_SIZE];
int tkubikaza;
int tgodiste;
while(fscanf(in,"%s %d %d",tmarka, &tkubikaza, &tgodiste)!=EOF)
{
AUTO *new = create_new(tmarka, tkubikaza, tgodiste);
add_location(new,root);
}
}
void write_one(FILE *out, AUTO *root)
{
fprintf(out,"%s %d %d\n",root->marka, root->kubikaza, root->godiste);
}
void write_all(FILE *out, AUTO *root)
{
if(root!=NULL)
{
write_all(out,root->left);
write_one(out,root);
write_all(out,root->right);
}
}
void find(AUTO *root, char tmarka[])
{
if(root!=NULL)
{
if(strcmp(root->marka,tmarka)==0)
{
printf("%s %d %d\n",root->marka, root->kubikaza, root->godiste);
}
else if(strcmp(root->marka,tmarka)==1)
{
find(root->left, tmarka);
}
else if(strcmp(root->marka,tmarka)==-1)
{
find(root->right, tmarka);
}
}
}
AUTO *newest(AUTO *root, int max_kub)
{
if(root==NULL)
{
return NULL;
}
AUTO *best = NULL;
if(root->kubikaza <= max_kub)
{
best = root;
}
AUTO *left = newest(root->left, max_kub);
if(left!=NULL)
{
if(best==NULL || left->godiste > best->godiste)
{
best = left;
}
}
AUTO *right = newest(root->right, max_kub);
if(right!=NULL)
{
if(best==NULL || right->godiste > best->godiste)
{
best = right;
}
}
return best;
}
Unless explicitly defined as a reference, all parameters in C/C++ are passed by value, including pointers. When you change the contents of the pointer, you don't change the pointer itself, which is really just a memory address. If you have to be able to change the pointer, then you have to either pass it by reference, or as a double pointer.
In your code:
init
the value of the pointer to NULL, thus changing it. SendingAUTO *root
would not allow this.add_location
can also set the base pointer if it is the first location added.read_file
changes the pointer by creating a new instance.A pointer is a pointer, and nothing more. It points to something else.
Take for example
The variable
ptr_to_int
points to the location whereint_value
exists.Now to have a "double pointer":
The variable
ptr_to_ptr_to_int
points to whereptr_to_int
exists.More "graphically" the above could be seen as something like
There are basically three uses of "double pointers", both related to arrays.
In the case you show, with the functions
init
andread_file
taking a "double pointer", it is the last case, to emulate pass by reference.