Mal etwas grundsätzliche Semantik.
Wenn Du etwas integer durchzählst, hat das erste Existierende die Nummer 1. Erstes Bit, zweites Bit, drittes Bit. Integer heißt zählbar. Ein Mensch ist integer, wenn man auf ihn zählen kann. Es gibt keine halben Schritte, entweder man geht einen ganzen Schritt oder man geht keinen.
Man kann Dinge zählen, also eine Menge bestimmen, oder man kann Distanzen zwischen Dingen ausdrücken. Das ist ein Unterschied. Wenn Du zählst ist ein Existierendes 1 Element. Und so zählst Du jedes Element beginnend mit 1.
Wenn Du etwas per Offset indizierst, also eine Distanz ausdrückst, dann stellt sich die Frage, wie weit ist etwas vom einer Startposition entfernt. Die Startposition ist häufig der Anfang einer Reihe existierender Objekte. Das Erste ist also der Anfang. Und das Erste ist 0 Schritte vom Anfang entfernt. Um vom ersten Haus am Platz zur Hausnummer 1 zu kommen, musst Du Dich 0 Häuser weit bewegen. Die 1 ist Zählnummer, die 0 ist die Entfernung vom ersten Haus.
Um vom ersten Byte des Arrays zum ersten Byte zu kommen, musst Du Dich 0 Byte bewegen. array ist der Zeiger auf das erste Byte. Das Offset ist 0. array[0] gibt das Byte zurück, welches 0 Byte vom ersten Byte entfernt ist. Intern wird einfach Zeiger auf erstes Element + 0 gerechnet - schon hat man den Zeiger auf das Element, was 0 Byte vom ersten Entfernt ist. Anschließend wird das Element zurückgegeben. Und das darfst Du auch so schreiben: *(array+0). Inhaltlich ist das gleichbedeutend.
Bei den Bits ist es noch ein wenig komplizierter. Da sitzt das erste Bit ganz rechts und repräsentiert die 1. Um an das erste Bit zu kommen, musst Du die 1 um 0 Bits verschieben. 1 << 0 ist also die Maske für das erste Bit. Um das zweite Bit zu maskieren musst Du die 1 um 1 Bit verschieben: 1 << 1. Das Verschieben gibt die Distanz an, wie weit verschoben wird.
In Diagrammen wird häufig von den Bits 0-7 gesprochen, aber hier handelt es sich nicht um gezählte Bits, sondern um deren Position bezogen zum ersten - also eben eine Distanz.
Gezählt wird immer von 1. Eine Strecke, um die man etwas verschiebt beginnt immer bei 0.
Es gilt also immer zwischen einer Menge (std::size_t) und einer Strecke (std::ptrdiff) zu unterscheiden. Eine Menge kann nur positiv sein, es kann keine Adresse existieren, die vor dem Speicher liegt: Man kann nicht in negativen Speicher zeigen, da existiert kein Speicher. In einer Straße kann keine negative Menge an Häusern existieren.
Eine Strecke kann jedoch positiv, wie auch negativ sein. Geht man vom 5. Haus -3 drei Häuser nach vorne, so landet man an Haus 2.
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.
Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.