D编程 指针(Pointers)

2021-09-01 11:09 更新

D编程指针既简单又有趣,使用指针可以更轻松地执行某些任务,而没有它们则无法执行其他任务(如动态内存分配),一个简单的指针如下所示。

Pointer in 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

指针

指针是一个变量,其值是另一个变量的地址,像任何变量或常量一样,必须先声明一个指针,然后才能使用它。指针变量声明的一般形式是-

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

所有指针的值的实际数据类型(无论是整数,浮点数,字符还是其他形式)都是相同的,即表示内存地址的十六进制数字,不同数据类型的指针之间的唯一区别是指针指向的变量或常量的数据类型。

使用指针

当我们非常频繁地使用指针时,很少有重要的操作。

  • 我们定义了一个指针变量
  • 将变量的地址分配给指针
  • 最终访问指针变量中可用地址处的值。

这是通过使用一元运算符 * 完成的,该运算符返回位于变量操作数指定地址处的变量的值。以下示例利用这些操作-

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

因此,如果为所有未使用的指针赋予了空值,并且避免了使用空指针,则可以避免意外误用未初始化的指针。

指针算术

可以在指针上使用四种算术运算符:++,--,+和-

为了理解指针算术,让我们考虑一个名为 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

指针到指针

指向指针的指针是多种间接形式或指针链的形式。通常,指针包含变量的地址,当我们定义一个指向指针的指针时,第一个指针包含第二个指针的地址,该地址指向包含实际值的位置,如下所示。

C++ 指针到指针

作为指针的指针的变量必须这样声明。这是通过在其名称前面放置一个额外的星号*来完成的。如以下是声明指向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;

将数组名称用作常量指针是合法的,反之亦然。因此,*(balance + 4)是访问balance [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


以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号