Also I believe in entering null values will lead to undefined behaviour.
I'm not sure how you'd enter NULL given scanf.
The program contains potential use of uninitialized memory UB, because scanf error return is not checked and num1 and num2 are not default initialized. And a + b can invoke signed integer overflow UB. A program with more than zero UB cannot be considered memory safe.
For example if the program runs in a context where stdin can't be read scanf will return error codes and leave the memory uninitialized.
num1 and num2 are declared on the stack and not the heap. The lifetimes of the variables are scoped to the function and so they are initialized. Their actual values are implementation-specific ("undefined behavior") but there is no uninitialized memory.
> And a + b can invoke signed integer overflow UB. A program with more than zero UB cannot be considered memory safe.
No, memory safety is not undefined behavior. In fact Rust also silently allows signed integer overflow.
Remember, the reason memory safety is important is because it allows for untrusted code execution. Importantly here, even if you ignore scanf errors and integer overflow, this program accesses no memory that is not stack local. Now if one of these variables was cast into a pointer and used to index into a non-bounds-checked array then yes that would be memory unsafety. But the bigger code smell there is to cast an index into a pointer without doing any bounds checking.
That's sort of what storing indexes separately from references in a lot of Rust structures is doing inadvertently. It's validating accesses into a structure.
Generally your comment strikes me as assuming that UB is some kind of error. In practice UB is more a promise the programmer made to never do certain things, allowing the compiler to assume that these things never happen.
How UB manifests is undefined. A program that has more than zero UB cannot be assumed to be memory safe because we can't make any general assumptions about its behavior because. UB is not specified to be localized it can manifest in any way, rendering all assumptions about the program moot. In practice when focusing on specific compilers and machines we can make reasonable localized assumptions, but these are always subject to change with every new compiler version.
Memory safety is certainly critical when it comes to exploits, but even in a setting without adversaries it's absolutely crucial for reliability and portability.
> In fact Rust also silently allows signed integer overflow.
Silently for release builds, and panic in debug builds. The behavior is implementation defined and not undefined, in practice this is a subtle but crucial difference.
Take this example https://cpp.godbolt.org/z/58hnsM3Ge the only kind of UB AFAIKT is signed integer overflow, and yet we get an out-of-bounds access. If instead the behavior was implementation defined the check for overflow would not have been elided.
especially when you phrase it as
> Can you provide examples for it? Because it honestly doesn't seem like it has ever been done.
it comes off as pedantic and arrogant.
It obviously is possible to write memory safe software in C and obviously it has been done before otherwise we would not be currently communicating over the goddamn internet.
Asking for evidence of something this obvious is akin to asking for a source on if water is in fact wet.
>it comes off as pedantic and arrogant.
Interesting the way this was perceived. I thought he was just asking a honest question.
Again shows online discussion and communication is hard.