简体   繁体   中英

How to access values from pointers to pointers in C?

I am writing a program that is attempting to implement a hash table in C. It has to use a pointer to a pointer as an attribute in a struct data type. Here is the code of the relevant structs.

      struct node {
      char * data;
      struct node * next;
    };

    struct hashtable {
      int size;
      struct node ** table;
    };

Part one of question:

I have been doing a lot of research to figure out just how pointers to pointers work but I still can't seem to get my head around it. Every example I've found assigns two different pointer names to the first and second pointer. eg

  x = 5;
  *p = &x;
  **q = &p; 

What about the case above "struct node ** table;" Is ** table the same as **q in this case? What would the values of q , *q and **q be in this case? is q = 5 and then *q and **q work back through the addresses or is **q = 5 and *q and q store the addresses?

Part two of question:

How do I access a pointer to a pointer within another function? Here is the code I have right now.

struct hashtable * hashtable_new(int size){

    struct hashtable *newTable;

    if ( size < 1 )
    {
        return NULL;
    printf("Its returning Null.");
    }
    else
    {
        newTable = malloc(sizeof(struct hashtable));
        newTable -> size = size;
        newTable -> table = malloc(sizeof(struct node) * size);
        int i;
        for (i = 0; i < size; i++ )
        {

            newTable -> table[i] = NULL;
        }
    fprintf(stderr, "Table has been created.");
        return newTable;
    }

};

I'm not sure I understand how to access either the pointer or the pointer to a pointer through the -> symbol. Is it "newtable -> table" or "newtable -> -> table" ?

The aim of the table is essentially to be a 2d table, where the list is primarily 1D and can spread to 2D to deal with collisions in the hashing.

End note:

Hopefully I've provided enough information to make contextual sense asking this question. Its my first time asking a question on stackoverflow so feel free to ask me extra question, provide CC or flag any mistakes I've made asking this question.

Thank you!

There are several ways to look at a pointer to a pointer. One way is as you described with

int   x =  5;
int  *p = &x;
int **q = &p; 

After all that, the following are all true:

**q == *p ==  x == 5
 *q ==  p == &x
  q == &p

However, another way to look at a pointer to a pointer is as an array of pointers. For example, assume the declaration:

int *arr[10];

Except when it is the operand of the sizeof or unary & operands, the expression arr will be converted ("decay") from type "10-element array of int * " to "pointer to int * ", or int ** .

If you want to dynamically allocate an N -element array of pointers to struct node , you would write

struct node **arr = malloc( sizeof *arr * N );

You would use the [] subscript operator to access elements in arr as you would a normal array. Since arr has type struct node ** , each arr[i] has type struct node * , so you would access members of the structure like so:

arr[i]->data = "foo"; // use -> when the LHS is a pointer to a struct or union
arr[i]->next = NULL;

struct hashtable allows you to create multiple hashtable instances, each one of which has a size member and an array of struct node * which you allocate at runtime, like so:

struct hashtable h1;

h1.size = 512;
h1.table = calloc( h1.size, sizeof h1.table ); // calloc initializes all elements of
                                               // h1.table to NULL
if ( !h1.table )
{
  // calloc call failed, handle as appropriate
}

You'd insert a new element into the table like so:

/**
 * Assumes we know that the string data points to is not already in
 * the table.  hashfunc is a pointer to the hashing function.
 */
void insert( struct hashtable *h, const char *data, size_t (*hashfunc)(const char *))
{
  /**
   * Create a new table entry.  Remember that table is an array of
   * *pointers* to struct node, and that each array element is initially
   * NULL.  We still need to allocate something for that table entry
   * to point *to*.
   */
  struct node *newNode = malloc( sizeof *newNode );
  if ( !newNode )
  {
    /**
     * Memory allocation for new node failed, handle as appropriate
     */
  }

  newNode->data = malloc( strlen( data ) + 1 );
  strcpy( newNode->data, data );

  /**
   * Get the table index by computing the hash and modding against the
   * table size.
   */
  size_t idx = hashfunc( data ) % h->size;

  /**
   * Insert newNode at the head of the list starting at
   * h->table[idx].  In reality you'd probably want to insert 
   * strings in order, but for a toy example this is good enough.
   */
  newNode->next = h->table[idx]; // newNode->next points to the previous head of list
  h->table[idx] = newNode;       // newNode becomes new head of list.
}

When you're done, your hashtable looks something like this:

    +---+
h1: |   | size
    +---+             +---+     +---+---+     +---+---+
    |   | table ----> |   | --> |   |   | --> |   |   |
    +---+             +---+     +---+---+     +---+---+
                      |   | 
                      +---+     +---+---+
                      |   | --> |   |   |
                      +---+     +---+---+     +---+---+     +---+---+
                      |   | --> |   |   | --> |   |   | --> |   |   |
                      +---+     +---+---+     +---+---+     +---+---+
                       ...

table points to an array of struct node * , each of which may point to an instance of struct node , each of which may point to another instance of struct node . In the picture above, two strings have hashed to table[0] , one to table[2] , and three to table[3] .

Here's a little program that might help you, it might be worth playing about with pointers for a bit and inspecting them, output addresses etc.

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

int
main(void)
    {
        char    *p1 = "String of text.";
        char    **p2;

        /**
         * We can print the avtual address of *p1 and **p2...
         */
        fprintf(stdout, "p1 = %p\n", p1);
        fprintf(stdout, "p2 = %p\n", p2);

        /**
         * Now if we point p2 at p1 we can then check what address
         * *p2 points at...
         */
        p2 = &p1;
        fprintf(stdout, "*p2 = %p\n", *p2);

        /**
         * So if you think about it, *p2 should be the same at p1
         * if we print it as a string:
         */
        fprintf(stdout, "p1 is %s\n*p2 is %s\n", p1, *p2);

        exit(EXIT_FAILURE);
    }

Hope this helps.

As for referencing the struct from another function, if you're passing a pointer to that structure you use a single -> to refer to any member of the struct...but you are allocating another struct within your struct - using a pointer.

Say I have

struct one {
    char *string;
}

struct two {
    struct *one a;
    struct one b;
    char *whatever;
}

If have a pointer to a struct two:

struct two *t;

And it had its member a allocated a struct one...then I'd reference the members of a using ->

t -> a -> string;

But b isn't a pointer so you would do:

t -> b.string;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM