Ein Blog

Posts mit Tag "cpp"

On finding the average of two unsigned integers without overflow.

Enthält auch eine (ehemalig) patentierte Variante.

Gut gefällt mir auch der untere Part:

Bonus chatter: C++20 adds a std::midpoint function that calculates the average of two values (rounding toward a). Bonus viewing: std::midpoint? How hard could it be? (Vortrag geht eine Stunde)

In C kann man Arrays mit Enum-Membern initialisieren. Auszug:

enum Fruit_t {
    APPLES,
    ORANGES,
    STRAWBERRIES = 8
};

void foo()
{
    static const int price_lookup[] = {
        [APPLES] = 6,
        [ORANGES] = 10,
        [STRAWBERRIES] = 55
    };
}

Noch ein Feature, das nicht viele kennen: Statische Größenangaben bei Arrays in Funktionsparametern:

void bar(int myArray[static 10]);

Checkt auf NULL-Pointer und prüft, ob die Größe des Arrays mindestens die geforderte Größe hat:

bar(NULL);
// warning: null passed to a callee which requires a non-null argument [-Wnonnull]
//     bar(NULL);
//     ^   ~~~~

int a[9];
bar(a);
// warning: array argument is too small; contains 9 elements, callee requires at least 10 [-Warray-bounds]
//     bar(a);
//     ^   ~

int b[11];
bar(b);

Noch ein Feature, das wenige kennen: Man kann angeben, dass ein Array so viele Elemente wie ein anderer Parameter der Funktion haben soll:

#include <stddef.h>

void baz(size_t n, int b[static n]);

int main() {
    int a[] = {1, 2, 3, 4};
    baz(4, a); // ok

    baz(9, a); // warning: 'baz' accessing 36 bytes in a region of size 16 [-Wstringop-overflow=]x86-64 gcc 11.2 #1

    return 0;
}

Zumindest die Warnung ist GCC-spezifisch. Clang zeigt keine Warnung an. Konnte leider nicht rausfinden, ob dieses Feature generell eine Extension ist. Ich glaube, das war im Standard, allerdings nur für Dokumentationszwecke angedacht. Um das “richtig” zu Type-Checken fehlt C das notwendige Typsystem.

Ein sehr interessanter Talk über undefined behaviour in C und C++: Garbage In, Garbage Out: Arguing about Undefined Behavior von Chandler Carruth; entwickelt für Google an LLVM und Clang.

Er erklärt, warum UB im Kontext von C und C++ Sinn ergibt, gibt ein paar Beispiele und reflektiert, was für Fehler man im Standard und bei den Implementierungen gemacht hat.

Es ist komplett an mir vorbei gegangen, dass C23 verabschieded wurde.

Meine Highlights:

  • signed Integer sind jetzt definiert als Zweierkomplement
  • #embed, womit man Rohdaten aus anderen Dateien inkludieren kann, z. B. in einem C-Array. Das wird so einige Buildprozesse vereinfachen.
  • Enums können jetzt einen zugrundeliegenden Typen haben: enum e : unsigned short { x };
  • Es gibt jetzt nullptr (und nullptr_t) auch in C
  • true und false sind jetzt Keywords
  • 0b-Literale

Außerdem gibt es noch bessere Kompatibilität zwischen gleichförmigen Structs. Mit N3003 wird dieser Generics-Hack nun viable (Codebeispiel von Reddit übernommen):

#include <stdio.h>
#include <stdlib.h>

#define Vec(T) struct Vec__##T { T *at; size_t _len; }

#define vec_push(a,v) ((a)->at = realloc((a)->at, ++(a)->_len), (a)->at[(a)->_len - 1] = (v))
#define vec_len(a) ((a)._len)

void fill(Vec(int) *vec) {
    for (int i = 0; i < 10; i += 2)
        vec_push(vec, i);
}

int main() {
    Vec(int) x = { 0 }; // or = {} in C2x
    // pre C2x you'd need to typedef Vec(int) to make the pointers compatible and use it for `x` and in fill:
    // --v
    fill(&x);
    for (size_t i = 0; i < vec_len(x); ++i)
        printf("%d\n", x.at[i]);
}

Auf cppreference gibt es auch noch eine kleine Übersicht der Änderungen.

cppinsights: See your source code with the eyes of a compiler.

Beispiel aus dem Readme:

class Base {};
class Derived : public Base {};
int main() {
  Derived d;
  Derived d2 = d;
  d2 = d;
  Base& b = d;
}

Wird zu:

class Base
{
  public:
  // inline constexpr Base() noexcept = default;
  // inline constexpr Base(const Base &) noexcept = default;
  // inline constexpr Base & operator=(const Base &) noexcept = default;
};
class Derived : public Base
{
  public:
  // inline constexpr Derived() noexcept = default;
  // inline constexpr Derived(const Derived &) noexcept = default;
  // inline constexpr Derived & operator=(const Derived &) noexcept = default;
};
int main()
{
  Derived d;
  Derived d2 = Derived(d);
  d2.operator=(d);
  Base & b = static_cast<Base&>(d);
  return 0;
}

Andrew Kelley bei CppCast über Zig. Hier ist er auch noch bei einem anderen Podcast.

C++ 26 ist fertig. Hier noch ein Video.