For starters consider a simple example:
#include <stdio.h>
void f( int *px )
{
printf( "in f &( *px ) = %p\n", ( void * )&( *px ) );
}
int main(void)
{
int x = 10;
printf( "in main &x = %p\n", ( void * )&x );
f( &x );
return 0;
}
The program output might look like:
in main &x = 0x7ffccc31de34
in f &( *px ) = 0x7ffccc31de34
That is the variable x is passed to the function f by reference through a pointer to it.
Dereferencing the pointer in the function *px you get a direct access to the memory where the variable x is stored. The variable x itself was not transferred to the function. It s a pointer to the variable that was transferred.
So using the expression &( *px ) or &( px[0] ) or just px you will get the address of the original variable x in main.
When you are passing an array then it is implicitly converted to pointer to its first element. On the other hand, a function parameter having an array type is also adjusted to the type pointer to the array element type.
That is this function declaration:
void demo(struct Book bookArr[]);
is equivalent to:
void demo(struct Book * bookArr);
So the array itself is not copied in the function. It is the pointer to its first element that is passed to the function.
So the value of the parameter bookArr gets the address of the first element of the array. In fact the parameter bookArr get the value of the expression book &bookArr[0] where bookArr declared in main. So in fact all elements of the array declared in main are passed to the function by reference because using the pointer arithmetic you can get the address of each element of the array. That is bookArr is the address of the first element of the array *the expression you can also write like &bookArr[0]), bookArr + 1 is the address of the second element of the array, and so on.
Pay attention to that in main in this call
printf("bookArr address from main: %p\n",&bookArr);
the expression &bookArr does not have the type struct Book *. It has the type struct Book ( * )[2] because in main you have an array. On the other hand in the function demo bookarr has the type struct Book * (due to adjusting the parameter to pointer to the array element type) and the expression &bookArr has the type struct Book ** and yields the address of the parameter (local variable of the function).