рассмотрим следующий фрагмент кода указателя - PullRequest
1 голос
/ 09 апреля 2020
#include <stdio.h>
int main(void)
{
    char x[5] = { 1, 2, 3, 4, 5 };
    char *ptr = (char*)(&x + 1);
    printf("%d %d\n", *(x + 1), *(ptr - 1));
    return 0;
}

Я не знаю, почему он печатает 2 5, здесь я запутался с работой указателя. Может ли кто-нибудь помочь мне визуализировать концепцию здесь?

Ответы [ 4 ]

3 голосов
/ 09 апреля 2020

about * (x + 1) : x - это адрес первого элемента в массиве, при добавлении 1 это указатель на следующий элемент, поэтому разыменовываем с помощью ' * 'Вы извлекаете второй элемент: 2

о char * ptr = (char *) (& x + 1); : & x - адрес массива само по себе (то же самое для x , но здесь важен тип), добавляя 1, вы получаете указатель сразу после массива, поэтому ptr указывает после массива

о * (ptr - 1) : ptr используется как char *, поэтому ptr - 1 указывает на символ непосредственно перед тем, как для символа со значением 5, разыменовывая его с помощью '*', вы извлекаете 5

Когда вы добавляете 1 к указателю, тип указателя важен, потому что вы прогрессируете по размеру указанного типа. Если вы добавляете 1 к указателю на символ, вы прогрессируете на 1 (по определению sizeof (char) равно 1), но в & x + 1 размер указанного элемента равен 5 Короче говоря указатель + 1 идет сразу после указанного элемента

1 голос
/ 09 апреля 2020

Визуализация *(x + 1)

+-----+-----+-----+-----+-----+-----+
|  1  |  2  |  3  |  4  |  5  |  ?  |
+-----+-----+-----+-----+-----+-----+
         |
         |
         +--> x + 1 points to here

Таким образом, вы получите число 2.

+-----+-----+-----+-----+-----+-----+
|  1  |  2  |  3  |  4  |  5  |  ?  |
+-----+-----+-----+-----+-----+-----+
                                 |
                                 |
                                 +--> &x + 1 points to here

&x + 1 указывает на 5 байт, поскольку тип &x равен char (*)[5]. Вы конвертируете это в char *. С помощью ptr - 1 вы укажете 1 байт назад от вышеуказанного местоположения, поэтому

+-----+-----+-----+-----+-----+-----+
|  1  |  2  |  3  |  4  |  5  |  ?  |
+-----+-----+-----+-----+-----+-----+
                           |
                           |
                           +--> ptr - 1 points to here

Таким образом, с помощью *(ptr - 1) вы получите доступ к вышеуказанному элементу, который является 5.

1 голос
/ 09 апреля 2020

Да, давайте представим. Давайте предположим, что базовый адрес массива начинается со значения 1000 (абсолютно гипотетическое значение), и мы знаем, что sizeof(char) равно 1 в C.

+----------+----------+-----------+-----------+-----------+
|          |          |           |           |           |
|   x[0]   |   x[1]   |    x[2]   |    x[3]   |    x[4]   |
|          |          |           |           |           |
+----------+----------+-----------+-----------+-----------+
1000     1001        1002        1003         1004        1005

Вот как ваш массив похоже. Итак, когда вы говорите

char *ptr = (char*)(&x + 1);

x (который является массивом), имеет тип char[5], а &x имеет тип char(*)[5], поэтому любая арифметика указателя c на &x будет учитывать тип данных, и на основании этого будут происходить увеличение и уменьшение.

Итак, ptr фактически указывает на адрес 1005, это один из типов массива, char[5].

Затем, когда вы говорите

 printf("%d %d\n", *(x + 1), *(ptr - 1));
  • x - то же самое, что и &(x[0]), добавление 1 продвигает это к следующему указателю, местоположению 1001, согласно арифметике указателя c, сохраняя тип.
  • Таким же образом , ptr, то есть char*, после вычитания 1, указывает на &(x[4]) и разыменования, которое дает значение.
1 голос
/ 09 апреля 2020

Во-первых, *(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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...