Dyzzet|
C++ Data Science Алгоритмы Темы · Блог · YouTube
Хаос в выражениях с инкрементом указателя

Навеяно «мемом»

Задача: записать значение x в нулевой элемент массива p и сместить указатель на следующий элемент.

Самый очевидный и чистый вариант:

p[0] = x;
++p;

Поскольку указатель на массив, используемый без квадратных скобок, означает адрес нулевого элемента, то есть p ≡ &p[0], его разыменование даёт сам нулевой элемент. Это означает, что *p ≡ p[0].

*p = x;
++p;

Эти две операции можно объединить. Но мы должны использовать пост-инкремент (указатель инкрементируется, оператор возвращает старый указатель).

*p++ = x;

Можно (пре)инкрементировать указатель, но затем в выражении получить предыдущее значение.

*(++p - 1) = x;

Или более многословно: *((p += 1) - 1) = x.

Игры с 0

(Пост)инкремент возвращает старый указатель:

(p++)[0] = x;

Поскольку квадратные скобки — синтаксический сахар для суммы и разыменования,*(p++ + 0) ≡ *(0 + p++), отсюда (p++)[0] ≡ (0)[p++]:

0[p++] = x;

Игры с –1

Если сперва инкрементировать указатель, обращаться к элементу p[0] можно с помощью записи p[-1].

++p;
p[-1] = x;

Конечно, их можно объединить (должен быть преинкремент):

++p[-1] = x;

...И переставить:

(-1)[++p] = x;

Отрицательные числа хранятся в дополнительном коде. Прямой код — модуль числа в двоичной системе. Обратный код — прямой код, у которого заменили нули на единицы и единицы на нули. Дополнительный код — обратный код плюс единица. Пример для однобайтового числа –11.

Прямой: 00001011
Обратный: 11110100
Дополнительный: 11110101

Теперь к самому сложному варианту.

(~0)[p = -~(size_t)p] = x;

~ — побитовое отрицание (оно определено стандартом только для беззнаковых целых, поэтому используется преобразование типа). Например, если p = 0x000000E9CD16FA10:

  p 00000000 00000000 00000000 11101001 11001101 00010110 11111010 00010000
 ~p 11111111 11111111 11111111 00010110 00110010 11101001 00000101 11101111

- записывает отрицательное число в дополнительном коде.

 ~p 11111111 11111111 11111111 00010110 00110010 11101001 00000101 11101111 - прямой
    00000000 00000000 00000000 11101001 11001101 00010110 11111010 00010000 - обратный
-~p 00000000 00000000 00000000 11101001 11001101 00010110 11111010 00010001 - дополнительный

Видно, что -~(size_t)p увеличивает p на единицу. Осталось разобраться с ~0.

  0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
 ~0 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111

Чтобы понять, что это за число, переведём дополнительный код в прямой.

 ~0 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 - дополнительный
    11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111110 - обратный
    00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 - прямой

То есть это число –1. Заметим, что (~0)[p = -~(size_t)p] = x будет корректно работать только для типов размером в один байт.

13 апреля 2022
C++ misc
Зарегистрируйтесь и войдите, чтобы оставлять комментарии и голосовать.

Также может быть интересным
© MMXI—MMXXIII. RSS. Поддержать сайт
Светлая тема / тёмная тема