Программирование на языке D - указатели
Указатели программирования D легко и весело изучать. Некоторые задачи программирования на языке D легче выполнять с помощью указателей, а другие задачи программирования на языке D, такие как распределение динамической памяти, не могут выполняться без них. Ниже показан простой указатель.
Вместо прямого указания на переменную указатель указывает на адрес переменной. Как вы знаете, каждая переменная является ячейкой памяти, и каждая ячейка памяти имеет свой адрес, доступ к которому можно получить с помощью оператора амперсанда (&), который обозначает адрес в памяти. Рассмотрим следующее, которое печатает адрес определенных переменных:
import std.stdio;
void main () {
int var1;
writeln("Address of var1 variable: ",&var1);
char var2[10];
writeln("Address of var2 variable: ",&var2);
}
Когда приведенный выше код компилируется и выполняется, он дает следующий результат:
Address of var1 variable: 7FFF52691928
Address of var2 variable: 7FFF52691930
Что такое указатели?
А pointer- переменная, значение которой является адресом другой переменной. Как и любую переменную или константу, вы должны объявить указатель, прежде чем вы сможете с ним работать. Общая форма объявления переменной-указателя -
type *var-name;
Вот, type- базовый тип указателя; это должен быть допустимый тип программирования иvar-nameэто имя переменной-указателя. Звездочка, которую вы использовали для объявления указателя, - это та же звездочка, которую вы используете для умножения. Тем не мение; в этом заявлении звездочка используется для обозначения переменной как указателя. Ниже приведено действительное объявление указателя -
int *ip; // pointer to an integer
double *dp; // pointer to a double
float *fp; // pointer to a float
char *ch // pointer to character
Фактический тип данных значения всех указателей, будь то целое число, число с плавающей запятой, символ или иное, одинаков, длинное шестнадцатеричное число, представляющее адрес памяти. Единственное различие между указателями разных типов данных - это тип данных переменной или константы, на которые указывает указатель.
Использование указателей в программировании на языке D
Когда мы очень часто используем указатели, есть несколько важных операций.
мы определяем переменные указателя
назначить адрес переменной указателю
наконец, получить доступ к значению по адресу, доступному в переменной указателя.
Это делается с помощью унарного оператора *который возвращает значение переменной, расположенной по адресу, указанному ее операндом. В следующем примере используются эти операции -
import std.stdio;
void main () {
int var = 20; // actual variable declaration.
int *ip; // pointer variable
ip = &var; // store address of var in pointer variable
writeln("Value of var variable: ",var);
writeln("Address stored in ip variable: ",ip);
writeln("Value of *ip variable: ",*ip);
}
Когда приведенный выше код компилируется и выполняется, он дает следующий результат:
Value of var variable: 20
Address stored in ip variable: 7FFF5FB7E930
Value of *ip variable: 20
Нулевые указатели
Всегда рекомендуется присвоить указатель NULL переменной-указателю, если у вас нет точного адреса для назначения. Это делается во время объявления переменной. Указатель, которому присвоено значение null, называетсяnull указатель.
Нулевой указатель - это константа со значением нуля, определенная в нескольких стандартных библиотеках, включая iostream. Рассмотрим следующую программу -
import std.stdio;
void main () {
int *ptr = null;
writeln("The value of ptr is " , ptr) ;
}
Когда приведенный выше код компилируется и выполняется, он дает следующий результат:
The value of ptr is null
В большинстве операционных систем программам не разрешен доступ к памяти по адресу 0, поскольку эта память зарезервирована операционной системой. Тем не мение; адрес памяти 0 имеет особое значение; он сигнализирует, что указатель не предназначен для указания доступной области памяти.
По соглашению, если указатель содержит нулевое (нулевое) значение, предполагается, что он ни на что не указывает. Чтобы проверить нулевой указатель, вы можете использовать оператор if следующим образом:
if(ptr) // succeeds if p is not null
if(!ptr) // succeeds if p is null
Таким образом, если всем неиспользуемым указателям присваивается значение NULL и вы избегаете использования нулевого указателя, вы можете избежать случайного неправильного использования неинициализированного указателя. Часто неинициализированные переменные содержат ненужные значения, что затрудняет отладку программы.
Указатель арифметики
Есть четыре арифметических оператора, которые можно использовать с указателями: ++, -, + и -
Чтобы понять арифметику указателей, давайте рассмотрим целочисленный указатель с именем ptr, который указывает на адрес 1000. Предполагая 32-битные целые числа, давайте выполним следующую арифматическую операцию с указателем:
ptr++
затем ptrбудет указывать на местоположение 1004, потому что каждый раз, когда ptr увеличивается, он указывает на следующее целое число. Эта операция переместит указатель на следующую ячейку памяти, не влияя на фактическое значение в этой ячейке памяти.
Если ptr указывает на символ, адрес которого равен 1000, то вышеуказанная операция указывает на местоположение 1001, потому что следующий символ будет доступен в 1001.
Увеличение указателя
Мы предпочитаем использовать указатель в нашей программе вместо массива, потому что указатель переменной может увеличиваться, в отличие от имени массива, которое не может быть увеличено, потому что это постоянный указатель. Следующая программа увеличивает указатель переменной для доступа к каждому последующему элементу массива:
import std.stdio;
const int MAX = 3;
void main () {
int var[MAX] = [10, 100, 200];
int *ptr = &var[0];
for (int i = 0; i < MAX; i++, ptr++) {
writeln("Address of var[" , i , "] = ",ptr);
writeln("Value of var[" , i , "] = ",*ptr);
}
}
Когда приведенный выше код компилируется и выполняется, он дает следующий результат:
Address of var[0] = 18FDBC
Value of var[0] = 10
Address of var[1] = 18FDC0
Value of var[1] = 100
Address of var[2] = 18FDC4
Value of var[2] = 200
Указатели против массива
Указатели и массивы сильно связаны. Однако указатели и массивы не полностью взаимозаменяемы. Например, рассмотрим следующую программу -
import std.stdio;
const int MAX = 3;
void main () {
int var[MAX] = [10, 100, 200];
int *ptr = &var[0];
var.ptr[2] = 290;
ptr[0] = 220;
for (int i = 0; i < MAX; i++, ptr++) {
writeln("Address of var[" , i , "] = ",ptr);
writeln("Value of var[" , i , "] = ",*ptr);
}
}
В приведенной выше программе вы можете увидеть var.ptr [2] для установки второго элемента и ptr [0], который используется для установки нулевого элемента. Оператор инкремента можно использовать с ptr, но не с var.
Когда приведенный выше код компилируется и выполняется, он дает следующий результат:
Address of var[0] = 18FDBC
Value of var[0] = 220
Address of var[1] = 18FDC0
Value of var[1] = 100
Address of var[2] = 18FDC4
Value of var[2] = 290
Указатель на указатель
Указатель на указатель - это форма множественного косвенного обращения или цепочки указателей. Обычно указатель содержит адрес переменной. Когда мы определяем указатель на указатель, первый указатель содержит адрес второго указателя, который указывает на место, которое содержит фактическое значение, как показано ниже.
Переменная, являющаяся указателем на указатель, должна быть объявлена как таковая. Это делается путем добавления дополнительной звездочки перед его названием. Например, следующий синтаксис для объявления указателя на указатель типа int:
int **var;
Когда на целевое значение косвенно указывает указатель на указатель, то для доступа к этому значению требуется, чтобы оператор звездочки применялся дважды, как показано ниже в примере:
import std.stdio;
const int MAX = 3;
void main () {
int var = 3000;
writeln("Value of var :" , var);
int *ptr = &var;
writeln("Value available at *ptr :" ,*ptr);
int **pptr = &ptr;
writeln("Value available at **pptr :",**pptr);
}
Когда приведенный выше код компилируется и выполняется, он дает следующий результат:
Value of var :3000
Value available at *ptr :3000
Value available at **pptr :3000
Передача указателя на функции
D позволяет передавать указатель на функцию. Для этого он просто объявляет параметр функции как тип указателя.
В следующем простом примере передается указатель на функцию.
import std.stdio;
void main () {
// an int array with 5 elements.
int balance[5] = [1000, 2, 3, 17, 50];
double avg;
avg = getAverage( &balance[0], 5 ) ;
writeln("Average is :" , avg);
}
double getAverage(int *arr, int size) {
int i;
double avg, sum = 0;
for (i = 0; i < size; ++i) {
sum += arr[i];
}
avg = sum/size;
return avg;
}
Когда приведенный выше код компилируется и выполняется, он дает следующий результат:
Average is :214.4
Указатель возврата из функций
Рассмотрим следующую функцию, которая возвращает 10 чисел с помощью указателя, что означает адрес первого элемента массива.
import std.stdio;
void main () {
int *p = getNumber();
for ( int i = 0; i < 10; i++ ) {
writeln("*(p + " , i , ") : ",*(p + i));
}
}
int * getNumber( ) {
static int r [10];
for (int i = 0; i < 10; ++i) {
r[i] = i;
}
return &r[0];
}
Когда приведенный выше код компилируется и выполняется, он дает следующий результат:
*(p + 0) : 0
*(p + 1) : 1
*(p + 2) : 2
*(p + 3) : 3
*(p + 4) : 4
*(p + 5) : 5
*(p + 6) : 6
*(p + 7) : 7
*(p + 8) : 8
*(p + 9) : 9
Указатель на массив
Имя массива - это постоянный указатель на первый элемент массива. Поэтому в декларации -
double balance[50];
balance- указатель на & balance [0], который является адресом первого элемента массива balance. Таким образом, следующий фрагмент программы назначаетp адрес первого элемента balance -
double *p;
double balance[10];
p = balance;
Допустимо использовать имена массивов в качестве указателей на константы, и наоборот. Следовательно, * (баланс + 4) - это законный способ доступа к данным на балансе [4].
Как только вы сохраните адрес первого элемента в p, вы можете получить доступ к элементам массива, используя * p, * (p + 1), * (p + 2) и так далее. В следующем примере показаны все концепции, обсужденные выше -
import std.stdio;
void main () {
// an array with 5 elements.
double balance[5] = [1000.0, 2.0, 3.4, 17.0, 50.0];
double *p;
p = &balance[0];
// output each array element's value
writeln("Array values using pointer " );
for ( int i = 0; i < 5; i++ ) {
writeln( "*(p + ", i, ") : ", *(p + i));
}
}
Когда приведенный выше код компилируется и выполняется, он дает следующий результат:
Array values using pointer
*(p + 0) : 1000
*(p + 1) : 2
*(p + 2) : 3.4
*(p + 3) : 17
*(p + 4) : 50