The NULL value is clearly very useful to indicate "the end of" or "non-existance of" some value. Optionial arguments in functions are indicated by NULL, for example. Or "it hasn't been allocated yet...".
But a NULL value is not useful as a reference, it is never right to use NULL value for anything other than comparison, or assignment when your pointer is not needed [obviously, assuming it has been freed properly].
I am working on a piece of code that runs early on in the operating system boot - in later running of the code, it needs to have the current task in some places. But before the system has started properly, there is no current task. So I need to indicate that "there is no current task". I use a combination of a NULL pointer [and then access the relevant 'reserve' data structures if the pointer is NULL] and a variable to check if a task has been created. [This is quite simplified, but the concept applies].
Technically, in MS-DOS (or other 8086 in 'real mode'), a NULL pointer would also be used to set the vector for divide by zero [which is "vector zero", and the vector table is at address zero - so NULL]. But since modern OS's have the interrupt vector table somewhere else [typically], and the address at zero is typically protected from read/write operations, that is no longer a 'valid use' of a pointer. Of course, some other embedded type systems may still use address zero for something 'meaningfull'.