On x86 what will happen is the code will compile, but the function is going to read its argument from a floating point register instead of an integer register as it should. This:
1. Is a bug, since a completely unrelated garbage value is going to be printed.
2. Leaks the value of a register, which may be a security issue.
There are still other common issues which can easily turn into vulnerabilities, leaking private process memory, when people pass untrusted strings as format strings with the intention of printing them raw.
So you want a safe print to prevent trivial bugs in general, and security vulnerabilities in particular.
char buf[10];
const char* foo = "wrong?";
int res = snprintf(buf, 20, "What could possibly go %d", foo);
Will compile and do... something...error: format '%d' expects argument of type 'int', but argument 4 has type 'const char*' [-Werror=format=]
You only get into trouble when you use runtime format strings (like passing a user string as first argument to printf)
It also doesn't work if your code isn't a textbook example of being wrong. [0] is _slightly_ more contrived but still suffers all of the exact same problems, despite all of the information being available at compile time.
The "type-safe" means "type-checked" by the compiler for correctness to help prevent bugs. It doesn't mean "safety-as-in-not-dangerous".
Type-unsafeness in general also just allows for hard-to-find bugs, since only certain data at runtime will introduce undefined behavior.