Вы все время «используете указатели» в C #, он просто скрыт от вас.
Лучший способ решить проблему - подумать о том, как работает компьютер. Забудьте все причудливые вещи .NET: у вас есть память, которая просто содержит байтовые значения, и процессор, который просто делает вещи с этими байтовыми значениями.
Значение данной переменной хранится в памяти, поэтому связано с адресом памяти. Вместо того, чтобы постоянно использовать адрес памяти, компилятор позволяет вам читать с него и записывать его, используя имя.
Кроме того, вы можете интерпретировать значение как адрес памяти, по которому вы хотите найти другое значение. Это указатель.
Например, предположим, что наша память содержит следующие значения:
Address [0] [1] [2] [3] [4] [5] [6] [7]
Data 5 3 1 8 2 7 9 4
Давайте определим переменную x
, которую компилятор выбрал для размещения по адресу 2. Видно, что значение x
равно 1.
Давайте теперь определим указатель p
, который компилятор выбрал для размещения по адресу 7. Значение p
равно 4
. Значение , указанное на p
, является значением по адресу 4, которое является значением 2
. Получение значения называется разыменование .
Важно отметить, что в отношении памяти не существует такой вещи, как тип : существуют только байтовые значения. Вы можете интерпретировать эти байтовые значения так, как вам нравится. Например, разыменование указателя на символ только получит 1 байт, представляющий код ASCII, но разыменование указателя на int может получить 4 байта, составляющих 32-битное значение.
Глядя на другой пример, вы можете создать строку в C со следующим кодом:
char *str = "hello, world!";
Что говорит следующее:
- Отложите несколько байтов в нашем стековом кадре для переменной, которую мы назовем
str
.
- Эта переменная будет содержать адрес памяти, который мы хотим интерпретировать как символ.
- Скопируйте адрес первого символа строки в переменную.
- (строка «привет, мир!» Будет сохранена в исполняемом файле и, следовательно, будет загружена в память при загрузке программы)
Если вы посмотрите на значение str
, вы получите целочисленное значение, представляющее адрес первого символа строки. Однако, если мы разыменовываем указатель (то есть посмотрим, на что он указывает), мы получим букву 'h'.
Если вы увеличите указатель, str++;
, он теперь будет указывать на следующий символ. Обратите внимание, что арифметика указателя масштабируется . Это означает, что когда вы выполняете арифметику с указателем, эффект умножается на размер типа, на который, по его мнению, он указывает. Таким образом, предполагая, что int
имеет ширину 4 байта в вашей системе, следующий код фактически добавит 4 к указателю:
int *ptr = get_me_an_int_ptr();
ptr++;
Если вы в конечном итоге пройдете конец строки, вы не сможете сказать, на что вы будете указывать; но ваша программа все равно будет старательно пытаться интерпретировать его как символ, даже если значение, как предполагается, фактически представляет целое число, например. Однако вы, возможно, пытаетесь получить доступ к памяти, которая не выделена для вашей программы, и ваша программа будет уничтожена операционной системой.
Последний полезный совет: массивы и арифметика указателей - это одно и то же, это просто синтаксический сахар. Если у вас есть переменная, char *array
, то
array[5]
полностью эквивалентно
*(array + 5)