N.B. Здесь нет претензий к стандартам С.
В качестве краткого добавления к ответу @Brian Hooper, "[t] сумма двух указателей означает ... э ... ничего", однако сумма указателя и целого числа позволяет вам сместить от начального указателя.
Вычитание указателя с более высоким значением из указателя с более низким значением дает смещение между ними. Обратите внимание, что здесь я не учитываю подкачку памяти; Я предполагаю, что оба значения памяти находятся в пределах доступного объема процесса.
Так что, если у вас есть указатель на последовательность последовательных областей памяти в куче или массив областей памяти в стеке (имя переменной которого уменьшается до указателя), эти указатели (действительный указатель и тот, который распадается на указатель) будет указывать на первый вопрос о расположении памяти (т. е. элемент [0]
). Добавление целочисленного значения к указателю эквивалентно увеличению индекса в скобках на то же число.
#include <stdio.h>
#include <stdlib.h>
int main()
{
// This first declaration does several things (this is conceptual and not an exact list of steps the computer takes):
// 1) allots space on the stack for a variable of type pointer
// 2) allocates number of bytes on the heap necessary to fit number of chars in initialisation string
// plus the NULL termination '\0' (i.e. sizeof(char) * <characters in string> + 1 for '\0')
// 3) changes the value of the variable from step 1 to the memory address of the beginning of the memory
// allocated in step 2
// The variable iPointToAMemoryLocationOnTheHeap points to the first address location of the memory that was allocated.
char *iPointToAMemoryLocationOnTheHeap = "ABCDE";
// This second declaration does the following:
// 1) allots space on the stack for a variable that is not a pointer but is said to decay to a pointer allowing
// us to so the following iJustPointToMemoryLocationsYouTellMeToPointTo = iPointToAMemoryLocationOnTheHeap;
// 2) allots number of bytes on the stack necessary to fit number of chars in initialisation string
// plus the NULL termination '\0' (i.e. sizeof(char) * <characters in string> + 1 for '\0')
// The variable iPointToACharOnTheHeap just points to first address location.
// It just so happens that others follow which is why null termination is important in a series of chars you treat
char iAmASeriesOfConsecutiveCharsOnTheStack[] = "ABCDE";
// In both these cases it just so happens that other chars follow which is why null termination is important in a series
// of chars you treat as though they are a string (which they are not).
char *iJustPointToMemoryLocationsYouTellMeToPointTo = NULL;
iJustPointToMemoryLocationsYouTellMeToPointTo = iPointToAMemoryLocationOnTheHeap;
// If you increment iPointToAMemoryLocationOnTheHeap, you'll lose track of where you started
for( ; *(++iJustPointToMemoryLocationsYouTellMeToPointTo) != '\0' ; ) {
printf("Offset of: %ld\n", iJustPointToMemoryLocationsYouTellMeToPointTo - iPointToAMemoryLocationOnTheHeap);
printf("%s\n", iJustPointToMemoryLocationsYouTellMeToPointTo);
printf("%c\n", *iJustPointToMemoryLocationsYouTellMeToPointTo);
}
printf("\n");
iJustPointToMemoryLocationsYouTellMeToPointTo = iPointToAMemoryLocationOnTheHeap;
for(int i = 0 ; *(iJustPointToMemoryLocationsYouTellMeToPointTo + i) != '\0' ; i++) {
printf("Offset of: %ld\n", (iJustPointToMemoryLocationsYouTellMeToPointTo + i) - iPointToAMemoryLocationOnTheHeap);
printf("%s\n", iJustPointToMemoryLocationsYouTellMeToPointTo + i);
printf("%c\n", *iJustPointToMemoryLocationsYouTellMeToPointTo + i);
}
printf("\n");
iJustPointToMemoryLocationsYouTellMeToPointTo = iAmASeriesOfConsecutiveCharsOnTheStack;
// If you increment iAmASeriesOfConsecutiveCharsOnTheStack, you'll lose track of where you started
for( ; *(++iJustPointToMemoryLocationsYouTellMeToPointTo) != '\0' ; ) {
printf("Offset of: %ld\n", iJustPointToMemoryLocationsYouTellMeToPointTo - iAmASeriesOfConsecutiveCharsOnTheStack);
printf("%s\n", iJustPointToMemoryLocationsYouTellMeToPointTo);
printf("%c\n", *iJustPointToMemoryLocationsYouTellMeToPointTo);
}
printf("\n");
iJustPointToMemoryLocationsYouTellMeToPointTo = iAmASeriesOfConsecutiveCharsOnTheStack;
for(int i = 0 ; *(iJustPointToMemoryLocationsYouTellMeToPointTo + i) != '\0' ; i++) {
printf("Offset of: %ld\n", (iJustPointToMemoryLocationsYouTellMeToPointTo + i) - iAmASeriesOfConsecutiveCharsOnTheStack);
printf("%s\n", iJustPointToMemoryLocationsYouTellMeToPointTo + i);
printf("%c\n", *iJustPointToMemoryLocationsYouTellMeToPointTo + i);
}
return 1;
}
Первое, что мы делаем в этой программе, это копируем значение указателя iPointToAMemoryLocationOnTheHeap
в iJustPointToMemoryLocationsYouTellMeToPointTo
. Так что теперь оба они указывают на одну и ту же область памяти в куче. Мы делаем это, чтобы не потерять след начала.
В первом цикле for
мы увеличиваем значение, которое мы только что скопировали, до iJustPointToMemoryLocationsYouTellMeToPointTo
(увеличение его на 1 означает, что оно указывает на одну ячейку памяти дальше от iPointToAMemoryLocationOnTheHeap
).
Второй цикл аналогичен, но я хотел бы более четко показать, как увеличение значения связано со смещением и как работает арифметика.
Третий и четвертый циклы повторяют процесс, но работают с массивом в стеке, а не с выделенной памятью в куче.
Обратите внимание на звездочку *
при печати индивидуального char
. Это заставляет printf выводить все, на что указывает переменная, а не содержимое самой переменной. Это отличается от строки выше, где печатается остаток строки, и перед переменной нет звездочки, потому что printf () просматривает серию ячеек памяти полностью, пока не будет достигнут NULL.
Вот вывод на ubuntu 15.10, работающем на i7 (вывод первого и третьего циклов начинается со смещением 1, потому что мой выбор цикла for
увеличивается в начале цикла в отличие от do{}while()
; Я просто хотел, чтобы все было просто):
Offset of: 1
BCDE
B
Offset of: 2
CDE
C
Offset of: 3
DE
D
Offset of: 4
E
E
Offset of: 0
ABCDE
A
Offset of: 1
BCDE
B
Offset of: 2
CDE
C
Offset of: 3
DE
D
Offset of: 4
E
E
Offset of: 1
BCDE
B
Offset of: 2
CDE
C
Offset of: 3
DE
D
Offset of: 4
E
E
Offset of: 0
ABCDE
A
Offset of: 1
BCDE
B
Offset of: 2
CDE
C
Offset of: 3
DE
D
Offset of: 4
E
E