Home > database >  Different ways Asterisk (* operator) is used in C
Different ways Asterisk (* operator) is used in C

Time:11-10

So I know that the * operator is used for multiplication and as a pointer but sometimes I see the * operator and I am not sure what it means. Have a look at this code which defines a matrix

#ifndef MATRIX_H
#define MATRIX_H

/* The data structure representing a matrix */
typedef struct {
    int rows;       /* number of rows */
    int columns;    /* nuumber of columns */
    int **content;  /* matrix elements in a two dimensional array */
} matrix_t;

what does a double asterisk used for here?

I assume ** operator is used for pointers in 2d arrays but I am not sure.

CodePudding user response:

Multiple indirection is a thing - you can have pointers to pointers, pointers to pointers to pointers, pointers to arrays of pointers, arrays of pointers to pointers, pointers to arrays of pointers to pointers to arrays of pointers to functions returning pointers, etc.

As used here, the intent is that you allocate an array of pointers:

matrix_t mat;
mat.content = calloc( 2, sizeof *mat.content ); 

which gives us

             int **     int *
              ---        --- 
mat.content: |   | ---> |   | mat.content[0]
              ---        --- 
                        |   | mat.content[1]
                         --- 
                        

and then for each of those pointers, you allocate an array of int:

for ( size_t i = 0; i < rows; i   )
  mat.content[i] = calloc( 2, sizeof *mat.content[i] );

giving us

             int **     int *                     int
              ---        ---                       --- 
mat.content: |   | ---> |   | mat.content[0] ---> |   | mat.content[0][0]
              ---        ---                       --- 
                        |   | mat.content[1] -    |   | mat.content[0][1]
                         ---                  |    --- 
                                              |       
                                              |   
                                              |    --- 
                                               -> |   | mat.content[1][0]
                                                   --- 
                                                  |   | mat.content[1][1]
                                                   --- 
                                                   

The expression mat.content has type int **, so the expression *mat.content has type int *; thus, sizeof *mat.content == sizeof (int *). Similarly, the expression mat.content[i] also has type int * (*p == p[0]), so *mat.content[i] has type int, so sizeof *mat.content[i] == sizeof (int).

Because of how array subscripting works, mat.content[i][j] works exactly the same way as if you had declared mat.content as a regular 2D array of int. It's just instead of all the rows being allocated in a single contiguous chunk, they're allocated piecemeal.

General rules:

T *p;        // p is a pointer to T
T *ap[N];    // ap is an array of pointer to T
T (*pa)[N];  // pa is a pointer to an array of T
T *fp();     // fp is a function that returns pointer to T
T (*pf)();   // pf is a pointer to a function that returns T

T **p;       // p is a pointer to a pointer to T
T ***p;      // p is a pointer to a pointer to a pointer to T

const T *p;  // p is a pointer to const T - you can write to p, but not *p
T const *p;  // same as above
T * const p; // p is a const pointer to T - you can write to *p, but not to p

When used in an expression, the * "dereferences" the pointer - if you have a declaration like

int x;
int *p = &x;

the expression *p acts as kind of an alias for x:

*p = 10; // does the same thing as x = 10;

The object p stores the location of x, and the type of the expression p is int * (pointer to int). The expression *p is equivalent to the expression x - not only does it yield the value stored in x, you can write a new value to x through *p as shown above.

The declarations above give us this relationship:

 p == &x // int * == int *
*p ==  x // int   == int

The array subscript expression a[i] is defined as *(a i) - given a starting address designated by a, offset i elements (not bytes!) from that address and dereference the result.

*p == *(p   0) == p[0]

Arrays are not pointers, nor do they store a pointer to their first element. Unless it is the operand of the sizeof, _Alignof, or unary & operators, or is a string literal used to initialize a character array in a declaration, an expression of type "N-element array of T" will be converted, or "decay", to an expression of type "pointer to T" and the value of the expression will be the address of the first element. This is important - if you declare an array

int a[10];

a does not store an address to its first element; instead, any time the compiler sees the expression a outside of the contexts listed above, it will basically replace it with something equivalent to &a[0].

  • Related