Во-первых, *(x + 1)
легко. Это то же самое, что и x[1]
, то есть 2
. Ничего необычного там нет, хотя лучше написать x[1]
, чем *(x + 1)
.
Задание ptr
более сложное. Он берет адрес x
, добавляет к нему 1
и приводит результат к (char *)
. Это эквивалентно char *ptr = *(&x + 1);
Даже более кратко, можно просто написать char *ptr = (&x)[1];
Любой из них лучше, чем использовать ненужное приведение, которого вы всегда должны стараться избегать при выполнении арифметики с указателями c, если это не является абсолютно необходимым.
&x
- указатель на массив типа char [5]
, поэтому он имеет тип char (*)[5]
. Это обычно используется для доступа к 2-мерному массиву. Поскольку sizeof(char [5])
равно 5
, добавление 1
к &x
фактически добавляет 5
к указателю, поскольку он масштабируется по размеру элемента, на который он указывает. Таким образом, &x + 1
имеет тип char (*)[5]
и указывает на первый символ после массива x
. Приведение его к char *
равносильно разыменованию - оно просто меняет тип на char *
.
Таким образом, присвоение ptr
эквивалентно char *ptr = x + 5;
или эквивалентно char *ptr = &x[5];
. Это указывает на конец x
. Но код ссылается на него как *(ptr - 1)
, что эквивалентно ptr[-1]
, т.е. это символ перед , на который указывает ptr
, то есть это 5
.
Так что в итоге получается печать 2 5
.