Ответ 1
Если вы следуете стандарту языка в письме, то арифметика указателя определяется только при указании на массив, а не в любом другом случае.
Указатель может указывать на любой элемент массива или на один шаг за конец массива.
Я думаю, что я правильно понимаю семантику арифметики указателя, но я когда-либо видел примеры при работе с массивами. Есть ли у него какие-либо другие виды использования, которые не могут быть достигнуты с помощью менее непрозрачных средств? Я уверен, что вы могли бы найти способ с умным кастингом, чтобы использовать его для доступа к членам структуры, но я не уверен, почему вы беспокоитесь. Меня больше всего интересует C, но я буду отмечать с помощью С++, потому что ответ, вероятно, применим и там.
Изменить, на основе полученных ответов: Я знаю, что указатели могут использоваться во многих контекстах без массива. Я специально интересуюсь арифметикой на указателях, например. увеличивая, принимая разницу и т.д.
Если вы следуете стандарту языка в письме, то арифметика указателя определяется только при указании на массив, а не в любом другом случае.
Указатель может указывать на любой элемент массива или на один шаг за конец массива.
Арифметика указателя по определению в C происходит только на массивах. Однако, поскольку каждый объект имеет представление, состоящее из наложенного массива unsigned char [sizeof object]
, он также действителен для выполнения арифметики указателя на этом представлении. Например:
struct foo {
int a, b, c;
} bar;
/* Equivalent to: bar.c = 1; */
*(int *)((unsigned char *)&bar + offsetof(struct foo, c)) = 1;
Фактически char *
будет работать так же хорошо.
Из верхней части моей главы я знаю, что он использовался в XOR-связанных списках (очень изящный), и я видел, как он использовался в очень хакерские рекурсии.
С другой стороны, очень трудно найти использование, поскольку в соответствии со стандартным указателем арифмический определяется только в пределах массива.
a[n]
является "просто" синтаксическим сахаром для *(a + n)
. Для lulz попробуйте следующее
int a[2];
0[a] = 10;
1[a] = 20;
Таким образом, можно утверждать, что индексирование и арифметика указателей являются просто взаимозаменяемым синтаксисом.
Арифметика указателя определяется только для массивов. Добавление целого числа к указателю, который не указывает на элемент массива, вызывает поведение undefined.
В встроенных системах указатели используются для представления адресов или местоположений. Не может быть определен массив. (Хотя можно сказать, что вся память - это один огромный массив.)
Например, стек (удерживающие переменные и адреса) управляется путем добавления или вычитания значений из указателя стека. (В этом случае стек можно назвать стеком на основе массива.)
Здесь случай для арифметики указателя вне (строго определенных) массивов:
double d = 0.5;
unsigned char *bytes = (void *)&d;
for(size_t i = 0; i < sizeof d; i++)
printf("Byte %zu of d is %hhu\n", i, bytes[i]);
Зачем вам это делать? Я не знаю. Но если вы хотите посмотреть побитовое представление объекта (полезно для таких вещей, как memcpy
и memcmp
), вам нужно будет указать свои адреса на unsigned char *
(или signed char *s
, если хотите), и работать с ними побайтно. (Если ваша задача не слишком сложна, вы даже можете написать код для работы по слову, который будет реализован в большинстве реализаций memcpy
. Однако тот же принцип просто заменит char
на int32_t
.)
Обратите внимание, что в стандарте точные значения (или количество значений), которые печатаются, определяются по реализации, но это всегда будет работать как средство доступа к внутреннему представлению объекта. (Не требуется работать для более крупных целочисленных типов, но почти всегда будет - никакой процессор, о котором я знаю, имел довольно ловушечные представления для целых чисел в течение некоторого времени).