2

C23: a slightly better C

 4 months ago
source link: https://lemire.me/blog/2024/01/21/c23-a-slightly-better-c/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

C23: a slightly better C

One of the established and most popular programming languages is the C programming language. It is relatively easy to learn, and highly practical.

Maybe surprisingly, the C programming language keeps evolving, slowly and carefully. If you have GCC 23 or LLVM (Clang) 16, you already have a compiler with some of the features from the latest standard (C23).

// Only include stdio.h if it exists
#if __has_include (<stdio.h>)
  #include <stdio.h>
#endif

#include <stdlib.h>

[[deprecated]]
void f() {}

[[nodiscard]]
int g(int x) {
  return x + 1;
}

int main() {
  f(); // compile-time warning: 'f' is deprecated
  g(1); // compile-time warning
  auto x = 0b1111;
  typeof(x) y = 1'000'000; // type of y is the same as x
  printf("%d\n", x); // prints 15
  printf("%d\n", y); // prints 1000000
  constexpr int N = 10;
  // compile-time asserts using static_assert
  static_assert (N == 10, "N must be 10");
  bool a[N]; // array of N booleans
  for (int i = 0; i < N; ++i) {
    a[i] = true;
  }
  printf("%d\n", a[0]); // prints 1
}
  1. The first part of the code contains some preprocessor directives, which are instructions for the compiler to process the source code before compiling it. The #if directive checks a condition at compile time and includes the following code only if the condition is true. The __has_include macro is a feature of C++17 adopted by C23 that checks if a header file exists and can be included. In this instance, it is not useful because we know that stdio.h is present, but in other instances, this can prove useful to determine what headers are available.
  2. The next part of the code defines two functions with attributes, which are annotations that provide additional information to the compiler about the behavior or usage of a function, variable, type, etc.
    • The [[deprecated]] attribute is a feature of C++14 adopted by C23 that marks a function as obsolete and discourages its use. The compiler will issue a warning if the function is called or referenced.
    • The [[nodiscard]] attribute is a feature of C++17 adopted by C23 that indicates that the return value of a function should not be ignored or discarded. The compiler will issue a warning if the function is called from a discarded-value expression.

    In this case, the function f is deprecated and does nothing, while the function g returns the input value plus one and should not be ignored. The first two lines of the main function call the functions f and g and trigger the warnings.

  3. The third line of the main function declares a variable x with the auto keyword, which is a feature of C++11 that lets the compiler deduce the type of the variable from its initializer. In this case, the initializer is a binary literal, which is a feature of C++14 and adopted by C23 that allows writing integer constants in binary notation using the prefix 0b. The value of x is 0b1111, which is equivalent to 15 in decimal.
  4. The fourth line declares another variable y with the typeof operator that returns the type of an expression. In this case, the expression is x, so the type of y is the same as the type of x. The initializer of y is a digit separator, which is a feature of C++14 adopted by C23 that allows inserting single quotes between digits in numeric literals to improve readability. The value of y is 1’000’000, which is equivalent to 1000000 in decimal.
  5. The seventh line declares a constant variable N with the constexpr keyword, which is a feature of C++11 adopted by C23 that indicates that the value of the variable can be evaluated at compile time. The value of N is 10. Previously, one would often use a macro to define a compile-time constant (e.g., #define N 10).
  6. The eighth line uses the static_assert keyword, which is a feature of C++11 adopted by C23 that performs a compile-time assertion check. The keyword takes a boolean expression and an optional string message as arguments. If the expression is false, the compiler will emit an error and stop the compilation, displaying the message. If the expression is true, the compiler will do nothing. In this case, the expression is N == 10, which is true, so the compilation continues.
  7. The ninth line declares an array of N booleans named a. An array is a collection of elements of the same type that are stored in contiguous memory locations. The size of the array must be a constant expression, which is why N is declared with constexpr. We also use the keywords true and false which become standard in C23.

There are many more features in C23, but it will take some time for compilers and system librairies to catch up.

My thoughts so far:

  • The introduction of consexpr in C will probably help reduce the dependency on macros, which is a good idea generally. Macros work well in C, but when a bug is introduced, it can be difficult get meaningful error messages. It does not happen too often, but in large code bases, it can be a problem.
  • I personally rarely use auto and typeof in other languages, so I don’t expect to use them very much in C. In some specific cases, it can greatly simply one’s code, however. It is likely going to help reduce the reliance on macros.
  • The idea behind static_assert is great. You run a check that has no impact on the performance of the software, and may even help it. It is cheap and it can catch nasty bugs. It is not new to C, but adopting the C++ syntax is a good idea.
  • The __has_include feature can simplify supporting diverse compilers. It is good idea.

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK