Арифметика указателя, в этом случае добавляющая целочисленное значение к значению указателя, увеличивает значение указателя в единицах типа, на который указывает. Если у вас есть указатель на 8-байтовый тип, добавление 1 к этому указателю будет продвигать указатель на 8 байтов.
Арифметика указателя действительна только в том случае, если как исходный указатель, так и результат добавления указывают на элементы одного и того же объекта массива или только за его конец.
Способ стандарт С описывает это (параграф 6.5.6):
Когда выражение, которое имеет целочисленный тип добавляется или вычитается из указателя, результат имеет тип операнд указателя.Если операнд указателя указывает на элемент объекта массива, а массив достаточно велик, результат указывает на смещение элемента из исходного элемента таким образом, что разница в нижних индексах элементов и элементов массива 0 равна целочисленное выражение.
[...]
Если оба указатель операнд и точка результата элементам же массив объектов, или один за последний элемент массива объектов, оценки не должен производить Чрезмерно поток; в противном случае поведение не определено. Если результат указывает один за последним элементом объекта массива , он не должен использоваться в качестве операнда унарного оператора, который оценивается.
Указатель, расположенный за концом массива, действителен, но вы не можете его разыменовать. Один объект без массива рассматривается как 1-элементный массив.
Ваша программа имеет неопределенное поведение. Вы добавляете 1
к нулевому указателю. Поскольку нулевой указатель не указывает на какой-либо объект, арифметика указателя на нем не определена.
Но компиляторы не обязаны обнаруживать неопределенное поведение, и ваша программа будет , вероятно, относиться к нулевому указателю точно так же, как к любому действительному значению указателя, и выполнять арифметику на нем одинаково. Таким образом, если нулевые указатель указывает на адрес 0
(это не гарантировала, кстати, но это очень часто), а затем добавить 1
к нему будет вероятно дать вам указатель для решения N, где N является размер в байтах типа, на который он указывает.
Вы затем преобразовать полученный указатель на int
(который в лучшем случае определяется реализация, потеряет информацию, если указатели больше int
, и могут дать ловушку представления) и распечатать значение int
. Результат, на большинстве систем, вероятно, покажет вам размеры char
, int
, float
и double
, которые обычно равны 1, 4, 4 и 8 байтам соответственно.
Поведение вашей программы не определено, но то, как оно на самом деле ведет себя в вашей системе, типично и неудивительно.
Вот программа, которая не имеют неопределенное поведение, которое иллюстрирует ту же точку:
#include <stdio.h>
int main(void) {
char c;
int i;
float f;
double d;
char *p = &c;
int *q = &i;
float *r = &f;
double *s = &d;
printf("char: %p --> %p\n", (void*)p, (void*)(p + 1));
printf("int: %p --> %p\n", (void*)q, (void*)(q + 1));
printf("float: %p --> %p\n", (void*)r, (void*)(r + 1));
printf("double: %p --> %p\n", (void*)s, (void*)(s + 1));
return 0;
}
и выход на моей системе:
char: 0x7fffa67dc84f --> 0x7fffa67dc850
int: 0x7fffa67dc850 --> 0x7fffa67dc854
float: 0x7fffa67dc854 --> 0x7fffa67dc858
double: 0x7fffa67dc858 --> 0x7fffa67dc860
Выход не ясно, как выход вашей программы, но если вы внимательно изучите результаты, вы увидите, что добавление 1 к char*
продвигает его на 1 байт, int*
или float*
на 4 байта и double*
- 8 байтов. (Кроме char
, которые по определению имеют размер 1 байт, они могут различаться в некоторых системах.)
Обратите внимание, что выход формата "%p"
определяется реализацией и может или не может отражать вид арифметических отношений, которые можно ожидать. Я работал над системами (векторными компьютерами Cray), где приращение указателя char*
фактически обновляло бы смещение байтов, хранящееся в 3-х разрядах старшего разряда 64-битного слова. В такой системе выход моей программы (и вашей) будет намного сложнее интерпретировать, если вы не знаете детали низкого уровня работы машины и компилятора.
Но для большинства целей вам не нужно знать эти детали низкого уровня. Важно то, что арифметика указателя работает так, как описано в стандарте C. Знание того, как это делается на уровне бит, может быть полезно для отладки (это в значительной степени то, что для %p
), но не нужно писать правильный код.
Полностью неопределенное поведение – CoryKramer
@CoryKramer Хотя я согласен с тем, что это UB, я думаю, что вопрос связан скорее с фактической арифметикой указателя (добавление 1 к указателю int увеличит адрес по размеру int) –
Связанный: http: //stackoverflow.com/questions/4772932 –