A trivial example is assigning pointers to incompatible types or passing them to incompatible function parameters not being an error. GCC 14 and Clang 16 are only just now making these errors by default. C++ never had this problem.
Another example, is having type safe generic functions and classes. Using void* to pass around arbitrary types is something that you don't need to do in C++ because it supports generic programming.
In C++ you can for example, pass an array of a specific length to a function, and make it a compile time error to pass an array of incorrect length, using std::array. In C arrays decay to pointers.
static_case is also a thing in C++ that allows you to cast between types much more safely.
There are so many things that I could probably spend several hours typing them out and I don't think that is really needed.
C++ is certainly more type safe than C, whether or not you think the tradeoffs it makes are worth is something to debate for sure, but it gives you tools to write type safe code that simply don't exist in C.
https://wiki.gentoo.org/wiki/Modern_C_porting#What_changed.3...
So in summary, I think this just confirms that C++ people think it is safer because they do not know how you would do this in modern C.
Much of the existing C code out there is filled with these things because they weren't errors until recently, and people ignore warnings. This is why Fedora and Gentoo are doing so much work to port all of their software to "modern C", so that it will actually compile with new compilers, and hopefully fix bugs at the same time. Existing c++ code did not have this problem because it was always an error.
Note that modern compiler in this case means bleeding edge, released literally within weeks or months of this posts date, most distros are not yet compiling the world with these and most users are not using them.
https://fedoraproject.org/wiki/Changes/PortingToModernC https://wiki.gentoo.org/wiki/Modern_C_porting
>Passing void* around is also generally not necessary in C.
This is not true.
I know several examples in real libraries that require this, two OTTOMH is PAM and Wayland. Both of these require void pointers to give you access to some state object that you create and need passed around, it has no way to know what this type will be ahead of time, and C has no other way to do this.
Another example is data structures or algorithms on data structures like sorting, you either use macros to emulate generics or you use void pointers. Qsort is a perfect example.
>One can take the address of an array and then get the same type checking.
I don't think this is true, C will not include the length in the type. Passing std::array<T, N> in C++ causes an error if T or N are different, in C this length information is not part of the type and does not get type checked.