问题
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;
}
回答1:
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:
void init(AUTO **root);
init
the value of the pointer to NULL, thus changing it. Sending AUTO *root
would not allow this.
void add_location(AUTO *new, AUTO **root)
add_location
can also set the base pointer if it is the first location added.
void read_file(FILE *in, AUTO **root);
read_file
changes the pointer by creating a new instance.
回答2:
A pointer is a pointer, and nothing more. It points to something else.
Take for example
int int_value = 1;
int *ptr_to_int = &int_value;
The variable ptr_to_int
points to the location where int_value
exists.
Now to have a "double pointer":
int **ptr_to_ptr_to_int = &ptr_to_int;
The variable ptr_to_ptr_to_int
points to where ptr_to_int
exists.
More "graphically" the above could be seen as something like
+-------------------+ +------------+ +-----------+ | ptr_to_ptr_to_int | ---> | ptr_to_int | ---> | int_value | +-------------------+ +------------+ +-----------+
There are basically three uses of "double pointers", both related to arrays.
- The first is to have a dynamic array of pointers to objects (mostly structures).
- The second is to have a dynamic array of dynamic arrays of objects (a.k.a. a dynamic "2d array").
- To emulate pass by reference for a pointer.
In the case you show, with the functions init
and read_file
taking a "double pointer", it is the last case, to emulate pass by reference.
来源:https://stackoverflow.com/questions/41293893/how-do-the-double-pointers-struct-tree-st-root-actually-work