Трудность в многомерных указателей в с? - PullRequest
1 голос
/ 22 июня 2019

У меня есть программа на C, которая использует указатели, но я не могу понять вывод. Почему первый вывод 1, а другой - 210. Оба они являются указателями на трехмерный массив.

Я не могу найти решение

int main() {    
    char arr[5][7][6];    
    char (*p)[5][7][6] = &arr;    
    printf("%d\n", (&arr + 1) - &arr);    
    printf("%d\n", (char *)(&arr + 1) - (char *)&arr);    
    printf("%d\n", (unsigned)(arr + 1) - (unsigned)arr);    
    printf("%d\n", (unsigned)(p + 1) - (unsigned)p);    
    return 0;    
}    

первый вывод 1 и последний 210

Ответы [ 3 ]

3 голосов
/ 22 июня 2019

C выполняет арифметику указателей в единицах типа, на который указывает указатель.

In (&arr + 1) - &arr, &arr - это адрес char [5][7][6] (массив из 5 массивов из 7 массивов по 6 char).Тогда &arr +1 - это адрес одного char [5][7][6] за пределами &arr, а (&arr + 1) - &arr - это расстояние от &arr до &arr + 1, измеренное в единицах char [5][7][6], поэтому расстояние составляет одну единицу.

В (char *)(&arr + 1) - (char *)&arr) два адреса преобразуются в char *, поэтому арифметика выполняется в единицах char.Таким образом, результатом является расстояние от &arr до &arr + 1, измеренное в единицах char.Поскольку расстояние от &arr до &arr + 1 равно одному char [5][7][6], оно равно 5 • 7 • 6 char, что составляет 210 char, поэтому результат равен 210.

Происшествия

Не используйте %d для печати результатов вычитания указателей.Когда вычтены два указателя, тип результата равен ptrdiff_t, и он может быть напечатан с помощью %td, как в printf("%td\n", (&arr + 1) - &arr));.

Для преобразования указателей в целые числа предпочтительно использовать uintptr_t, определенное в <stdint.h>, а не unsigned.

Для печати unsigned значений используйте %u, а не %d.

Для печати uintptr_t значений, включите <inttypes.h> и используйте "%" PRIuPTR, как в printf("%" PRIuPTR "\n", (uintptr_t) (p + 1) - (uintptr_t) p);.

2 голосов
/ 22 июня 2019

Во-первых, небезопасно использовать %d для печати различий указателей, имеющих тип ptrdiff_t (это версия со знаком size_t).

Игнорируя это, у вас есть следующие объявления:

char arr[5][7][6];
char (*p)[5][7][6] = &arr;

При вычитании двух указателей результат делится на размер цели (т. Е. Значение, обратное тому, что происходит при добавлении целого числа к указателю, и в этом случае целое число масштабируется с помощьюsize).

Для первого примера:

(&arr + 1) - &arr

Здесь оба типа &arr и &arr + 1 имеют тип char (*)[5][7][6], поэтому размер, на который они указывают, равен sizeof(char [5][7][6]),Добавление указателя умножает 1 на этот размер, а вычитание указателя делит разницу на этот размер, компенсируя ее.Таким образом, результат равен 1, независимо от целевого размера.

Для второго примера:

(char *)(&arr + 1) - (char *)&arr

Здесь сложение указателя снова умножает 1 на sizeof(char [5][7][6]), что составляет sizeof(char)*5*7*6, то есть 1*5*7*6, что 210.Но вычитание делится на sizeof(char), что составляет 1.Таким образом, результат равен 210.

Для третьего примера:

(unsigned)(arr + 1) - (unsigned)arr

Эффект приведения unsigned аналогичен эффекту приведения char * в предыдущемпример.Тем не менее, в этом два указателя arr и arr + 1.В этом контексте типы массивов «распадаются» на типы указателей char (*)[7][6].Следовательно, размер указателя цели равен sizeof(char)*7*6, т.е. 1*7*6, что составляет 42.Таким образом, результат равен 42.

Наконец, для последнего примера:

(unsigned)(p + 1) - (unsigned)p)

Оба типа p и p + 1 имеют тип char (*)[5][7][6], поэтому целевой размер равен 210.Повторное приведение unsigned приводит к прямому вычитанию адреса без деления на результат.Таким образом, результат 210.

1 голос
/ 22 июня 2019
char (*p)[5][7][6] = &arr;

Здесь p - массив указателей на символы, а не указатель на массив символов.

printf("%d\n", (&arr + 1) - &arr);

& знак возвращает адрес. Вы делаете математику по адресам, а не по значениям! и все, что плюс 1 и минус само по себе приведет к 1

(unsigned)p

такое поведение не гарантируется и небезопасно. и вы нигде не разыменовываете указатель.

Прежде чем делать это, вы должны прочитать больше об указателях, типах, приведениях и приоритетах операторов.

Я рекомендую эти два видео Брайана Уилла:

язык C (часть 2 из 5)

язык C (часть 5 из 5)

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