Why does calloc exist?

Explain the reasoning behind the calloc function in C. The use of calloc ensures the allocated memory is zeroed. Compared with the malloc + memset method, calloc offers overflow checking and is more efficient than the malloc because it relies on the fact that 1) new memory returned from OS is zeroed and 2) the actual memory allocation (page fault) is delayed until the application starts to access the memory.

C++ Standard Parallelism

It reminds me of certain ways to implement an efficient wc. Given a std::string content,

  • to count the number of chars, simply call content.size();
  • to count the number of words, use the algorithm above;
  • to count the number of lines, use std::count with par_unseq

My mental model for

  • seq: normal sequential program
  • unseq: SIMD instructions
  • par: multithreading
  • par_unseq: multithreading with SIMD instructions

Empty Base Optimization

Why Empty class requires to have one byte? It’s worth remembering that in C++, “the addresses of distinct objects of the same type are always distinct”.

  • At first glance, it makes no sense that we can have two objects of the same type that are in the same address.
  • But this rule is relaxed in C++20 to further promote EBO

When can we apply [[no_unique_address]]? We use it typically when we need to put some empty class inside some classes.

EBO application: std::pair (reduce the size of the pair), any allocator-awared container (avoid some extra bytes caused by the storage of the allocator).

A corner case of EBO:

Empty base optimization is prohibited if one of the empty base classes is also the type or the base of the type of the first non-static data member, since the two base subobjects of the same type are required to have different addresses within the object representation of the most derived type.

Trivia

  • Discussions about TCP_NODELAY

  • Pipe mechanism

  • Recursive Lambda in C++

    • The Surprising Costs of void() (and Other Not-Quite-Innocuous Evils)
    • We cannot capture lambda itself when creating the lambda. Neither by value or by reference will work. By value definitely cannot work due to recursive definition. By reference has the potential to work, but as we cannot during the type of the lambda when capturing it, it doesn’t work.
    • There are two solutions to build a recursive lambda. The first one is to take another auto parameter which we will use to accept lambda. The second one is to use declare a variable with type std::function, which allows us to capture this variable when declaring lambda (as shown in video). However, we need to take care of its lifetime as we are capturing by reference here.
  • Why can’t variables be declared in a switch statement?

    • Cases are just labels. (That’s why we could have tricks like Duff’s device).
    • The best way to deal with them is to treat them as labels (like those in jump). We could avoid the uninitialization errors by using a {} bracket in each case (therefore, each case is not in the same scope).
  • Rust reference

  • The Blank Sheet

  • Inline namespace

    • C++ Weekly - Ep 320 - Using inline namespace To Save Your ABI
    • How the object file is linked when we apply inline namespace: a call to A::f is linked to A::inline_namespace_name::f
    • Therefore, we could use different inline namespace names for different versions of our library. As a result of that, the ABI break will actually cause linker errors.
  • Order of destruction for stack/heap allocated arrays

    • The array elements will be destructed in reverse order of construction, with element 99 being the first destructed, then element 98, 97, 96… etc. and element 0 being the last.
    • It’s reasonable once you think about how we declare variables in program order like this: A a; B b; C c;. The construction order is A, B, C, and the destruction order is C, B, A. This corresponds to how the array is constructed/destructed.