back

by uecker·2y ago·view on hn ↗
Ok, let's debunk this point for point: - passing incompatible types is a constraint error in C just as in C++. Whether this is a warning or not depends on the compiler and on compiler flags. And as you observed yourself, the defaults are now even the same. - Passing void* around is also generally not necessary in C. - There is no reason to let arrays decay in pointers in C. One can take the address of an array and then get the same type checking. - Whether static_casts really makes much of a difference is debatable. In my C code I avoid casts and the rare exceptions I wrap in type-safe macros.

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.

1 comments
>passing incompatible types is a constraint error in C just as in C++. Whether this is a warning or not depends on the compiler and on compiler flags.

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.

There is plenty of old C code, this is irrelevant. Equally irrelevant is that you found a library that uses a void pointer. I can show type unsafe C++ code as well, and would be equally meaningless. We talk about whether C++ is more type safe in principle and it is not.

Here is the array example: https://godbolt.org/z/P8494W3Pf Note how grotesquely bad the C++ syntax is for exactly the same type safety.

>>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.

No, you can take the address of an array of length N, as long as you're content with having a function that requires the array be that particular length. For example, if you wanted to write a function that adds one to every element of an array with ten elements, and require that it must have ten elements, you can write it like this:

  void add_one(int (*array)[10]) {
      size_t i = 0;
      do {
          (*array)[i] += 1;
      } while (++i < 10);
  }
However, you usually don't see people do this, because they want their functions to work on all arrays, regardless of their length. So, they pass a pointer to the first element and a value representing the length.

You will see people pass pointers to arrays when writing functions that operate on 2D arrays, because that's how you pass a pointer to the first element of a 2D array. The address of the first element of an array of size 10 arrays of int, is a pointer to a size 10 array of int.

  // 2D_array_ptr: a pointer to the first element of an (n x 10) 2D array
  void add_one_to_array_of_array10(int (*2D_array_ptr)[10], size_t n) {
      for (size_t i = 0; i < n; ++i) {
          for (size_t j = 0; j < 10; ++j)
              array[i][j] += 1;
      }
  }
In function signatures, array declarations decay to be equivalent to pointers, so you could also write the function signature like this:

  void add_one_to_array_of_array10(int 2D_array_ptr[][10], size_t n)