My view of pointers and memory:
In a very simple way, when you declare a variable you are asking the compiler to reserve enough space to hold the value in the size of that type. So, "int x;", a local variable will tell the compiler to reserve 4 bytes of memory, enough to hold a 32 bits integer (in most 32/64 bits environments). Onde this space, in memory, is reserved you choose to label it 'x' (so you don't need to remember where is memory it is!).
Pointers are variables with enough space to hold an integer big enough to hold a memory address. When you declare "int *p;" you are telling the compiler to reserve 4 bytes (32 bits address, in 32 bits environment) or 8 bytes (64 bits address) and calling this space 'p'. The asterisk there is a syntatical rule telling the compiler that 'p' don't hold an 'int', but an address that points to an int.
The same thing with "struct product *item;". Here, 4 or 8 bytes are reserved to hold an address of a block of memory which will hold (eventually) all 16 bytes needed for the structure. "item" is the alias to that reserved space you choose. Its contents is the 'pointer' or an 'address'.
When you call malloc(), you are asking for "memory allocate" n bytes of memory and get the address of the begining of that block, so:
Code:
struct product *item;
item = malloc( sizeof *item );
Will allocate enough space to accomodate a single item pointed by the address inside "item" variable (in this case, the asterisk is an "indirection operator", not a "syntatic rule") and will return the address of this newly allocated block and put inside "item" variable. This can be a little confusing, since "item", initially, is "undefined", but sizeof don't need to read or write data "pointed" by this "undefined address". sizeof *item, in this case, is the same as sizeof( struct product ).
You can allocate an array, if you like:
Code:
item = malloc( 100 * sizeof( struct product ) );
Here I'd used sizeof( struct product ), but sizeof *item will do the same way... malloc() need the size, in bytes, of the block and always returns the address of the begining of this newly allocated block, which you can store in a "pointer" variable.
It is important to keep this initial address safe, since you'll need it to free the previously allocated block of memory, eventually. Or, even better, if you decide to resize this block (add another element to the array, for exemple) you can use realloc(), passing the initial address of the block and the new size.
Notice malloc() will allocate memory, but won't initialize it for you (the bytes allocated can be pure garbage). If you want to allocate memory and zero the entire block, you can use calloc(), or zero it yourself (with memset(), for example).