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

정수, 부동 소수점, 문자 등 모든 포인터 값의 실제 데이터 유형은 메모리 주소를 나타내는 긴 16 진수와 동일합니다. 서로 다른 데이터 유형의 포인터 간의 유일한 차이점은 포인터가 가리키는 변수 또는 상수의 데이터 유형입니다.

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 바늘.

널 포인터는 iostream을 포함한 여러 표준 라이브러리에 정의 된 값이 0 인 상수입니다. 다음 프로그램을 고려하십시오-

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++

다음 ptrptr이 증가 할 때마다 다음 정수를 가리 키기 때문에 1004 위치를 가리 킵니다. 이 작업은 메모리 위치의 실제 값에 영향을주지 않고 포인터를 다음 메모리 위치로 이동합니다.

만약 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]와 0 번째 요소를 설정하는 데 사용되는 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]에 대한 포인터입니다. 따라서 다음 프로그램 조각은p 첫 번째 요소의 주소 balance

double *p; 
double balance[10]; 
 
p = 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