Если бы вы спросили о преобразовании int **
в int *
, ответ был бы другим.Давайте сначала рассмотрим это, потому что я подозреваю, что он более репрезентативен для вопроса, который вы намеревались задать, и дело char *
более сложное, потому что в этом есть особая цель.
Предположим, у вас есть несколько int
: int a, b, c, d;
.Вы можете сделать массив указателей на них: int *p[] = { &a, &b, &c, &d };
.Вы также можете сделать указатель на один из следующих указателей: int **q = &p[1];
.Теперь q
указывает на p[1]
, который содержит адрес b
.
Когда вы пишете *q
, компилятор знает, что q
указывает на указатель на int
, поэтомузнает *q
указывает на int
.Если вы напишите **q
, компилятор, зная, что *q
указывает на int
, получит *q
из памяти и использует его в качестве адреса для получения int
.
Что происходитесли вы конвертируете q
в int *
и пытаетесь использовать его, как в printf("%d\n", * (int *) q);
?Когда вы конвертируете q
в int *
, вы (ложно) говорите компилятору рассматривать его как указатель на int
.Затем * (int *) q
говорит компилятору перейти по этому адресу и получить int
.
Это неверный код - его поведение не определено стандартом C.В частности, он нарушает C 2018 6.5 7, в котором говорится, что доступ к объекту должен иметь только выражение lvalue правильного типа - либо тип, совместимый с типом фактического объекта, либо некоторые другие случаи, ни один из которых здесь не применим.В точке q
указывает указатель на int
, но вы пытались получить к нему доступ, как если бы это был int
, а это недопустимо.
Теперь давайте рассмотрим char **
до char *
чехол.Как и раньше, вы можете взять char **q
и преобразовать его в char *
.Теперь вы говорите компилятору перейти в точку q
, где есть указатель на char
, и получить доступ к этой области памяти, как если бы там было char
.
C имеет специальные правила для этого случая.Вам разрешено проверять байты, составляющие объекты, путем доступа к ним через char *
.Итак, если вы преобразуете char **
в char *
и используете его, как в * (char *) q
, результатом будет первый (адрес с наименьшим адресом) байт, который составляет указатель там.Вы даже можете посмотреть на остальные байты, используя такой код:
char *t = (char *) q;
printf("%d\n", t[0]);
printf("%d\n", t[1]);
printf("%d\n", t[2]);
printf("%d\n", t[3]);
Этот код покажет вам десятичные значения для первых четырех байтов, которые составляют указатель на char
, который находится наместоположение, указанное q
.
В итоге, преобразование char **
в char *
позволит вам исследовать байты, представляющие char *
.Однако в общем случае не следует преобразовывать указатели одного уровня косвенности в указатели другого уровня косвенности.