In some programming languages [...] it is possible to pass a NULL parameter as an argument, but in C I always thought this would result in Undefined Behavior.
Passing a NULL parameter for a pointer by itself does not result in UB; it's attempting to access the memory pointed to by a pointer set to NULL that does.
Passing NULL is a very common practice for situations when something is not specified. The caller is expected to check parameters for NULL before performing the access. For example, the standard lets you pass NULL to free, which makes the function a lot more convenient.
don't NULL pointers simply point to nothing?
Yes, they do. But that "nothing" is globally well-known, so using a NULL lets you communicate the fact that a pointer points to nothing to functions that you call. In other words, the check
if (myPointer == NULL)
is well-defined*, so you can use it to your advantage.
* Unless you use a dangling pointer, i.e. a pointer that you have freed, or a pointer that points to object that went out of scope. You can prevent the first situation from happening by assigning NULL to every pointer that you free(), and the second situation by declaring pointers in the scope that has the same or higher level of nesting as the scope of an automatic object to which the pointer is pointing.