I'm not sure I really get the difference between NULL, (void *)0 and (char *)0.

According to the C standard:
An integer constant expression with the value 0, or such an expression cast to type
void *, is called a null pointer constant.
The macros are
NULL
which expands to an implementation-defined null pointer constant; and
So does that mean that those values can be used on pointers of any type? For example, are these valid null pointers?:
Code:
#include <stdio.h>

struct book {
    char title[25];
    char author[25];
};

int main(void) {
    int *ptr1 = 0;
    int *ptr2 = (void *)0;
    char *name = 0LL;
    struct book book1 = 0;
    struct book book2 = NULL;
    return 0;
}
And it also means that the NULL macro may be either (void *)0, or just plain 0 or even 0L.

Furthermore, the standard also says:
If a null pointer constant is converted to a
pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal
to a pointer to any object or function.
So this means those constants casted to a specific type are valid null pointers for that type, but not for another one? For example:
Code:
#include <stdio.h>

struct book {
    char title[25];
    char author[25];
};

int main(void) {
    int *ptr1 = (int *)0; // Correct, int null pointer

    int *ptr2 = (void *)0; // Correct, null pointer constant

    struct book book1 = (struct book *)0; // Correct, struct book null pointer

    struct book book2 = NULL; // Correct, null pointer constant

    int *ptr3 = 0LL; // Correct, null pointer constant, despite the constant being of type long long

    float *ptr4 = (int *)0; // Incorrect, assignment of an int null pointer to a float pointer

    return 0;
}
Did I understand it correctly??