C – Pointers พอยน์เตอร์ ภาษา C

ตัวชี้ในภาษา C นั้นง่ายและสนุกในการเรียนรู้ งานการเขียนโปรแกรม C บางงานสามารถทำได้ง่ายกว่าด้วยพอยน์เตอร์ และงานอื่นๆ เช่น การจัดสรรหน่วยความจำแบบไดนามิก ไม่สามารถทำได้โดยไม่ต้องใช้พอยน์เตอร์ ดังนั้นจึงจำเป็นต้องเรียนรู้คำแนะนำเพื่อเป็นโปรแกรมเมอร์ C ที่สมบูรณ์แบบ มาเริ่มเรียนรู้กันเลยในขั้นตอนที่ง่ายและสะดวก

ดังที่คุณทราบ ทุกตัวแปรคือตำแหน่งหน่วยความจำ และทุกตำแหน่งหน่วยความจำมีที่อยู่ที่กำหนดไว้ ซึ่งสามารถเข้าถึงได้โดยใช้ตัวดำเนินการเครื่องหมายและ (&) ซึ่งหมายถึงที่อยู่ในหน่วยความจำ ลองพิจารณาตัวอย่างต่อไปนี้ ซึ่งพิมพ์ที่อยู่ของตัวแปรที่กำหนดไว้ −

#include <stdio.h>

int main () {

   int  var1;
   char var2[10];

   printf("Address of var1 variable: %x\n", &var1  );
   printf("Address of var2 variable: %x\n", &var2  );

   return 0;
}


เมื่อโค้ดด้านบนถูกคอมไพล์และรัน มันจะให้ผลลัพธ์ดังต่อไปนี้ −

Address of var1 variable: 62fe1c
Address of var2 variable: 62fe10


พอยน์เตอร์คืออะไร?


พอยน์เตอร์ เป็นตัวแปรที่มีค่าที่อยู่ของตัวแปรอื่นเช่นที่อยู่ตรงกับที่ตั้งหน่วยความจำ เช่นเดียวกับตัวแปรหรือค่าคงที่ใดๆ คุณต้องประกาศ พอยน์เตอร์ ก่อนที่จะใช้เพื่อเก็บที่อยู่ของตัวแปรใดๆ รูปแบบทั่วไปของการประกาศตัวแปรพอยน์เตอร์คือ −

type *var-name;


ในที่นี้ type คือประเภทพื้นฐานของพอยน์เตอร์ ต้องเป็นชนิดข้อมูล C ที่ถูกต้อง และ var-name เป็นชื่อของตัวแปรพอยน์เตอร์ เครื่องหมายดอกจัน * ที่ใช้ประกาศตัวชี้เป็นเครื่องหมายดอกจันเดียวกับที่ใช้สำหรับการคูณ อย่างไรก็ตาม ในคำสั่งนี้ เครื่องหมายดอกจันถูกใช้เพื่อกำหนดตัวแปรเป็นพอยน์เตอร์ ดูการประกาศตัวชี้ที่ถูกต้องบางส่วน −

int    *ip;    /* pointer to an integer */
double *dp;    /* pointer to a double */
float  *fp;    /* pointer to a float */
char   *ch     /* pointer to a character */


ชนิดข้อมูลจริงของค่าของพอยน์เตอร์ทั้งหมด ไม่ว่าจะเป็นจำนวนเต็ม ทศนิยม อักขระ หรืออย่างอื่น จะเหมือนกัน คือเป็นเลขฐานสิบหกแบบยาวที่แสดงที่อยู่หน่วยความจำ ความแตกต่างเพียงอย่างเดียวระหว่างพอยน์เตอร์ของชนิดข้อมูลที่แตกต่างกันคือชนิดข้อมูลของตัวแปรหรือค่าคงที่ที่ พอยน์เตอร์ ชี้ไป

วิธีการใช้พอยน์เตอร์?


มีการดำเนินการที่สำคัญสองสามอย่าง ซึ่งเราจะทำโดยใช้ตัวชี้บ่อยครั้งมาก (a) เรากำหนดตัวแปรตัวชี้ (b) กำหนดที่อยู่ของตัวแปรให้กับตัวชี้และ (c) ในที่สุดเข้าถึงค่าที่อยู่ที่มีอยู่ในตัวแปรตัวชี้ ทำได้โดยใช้ตัวดำเนินการเอกพจน์*ที่คืนค่าของตัวแปรที่อยู่ตามที่อยู่ที่ระบุโดยตัวถูกดำเนินการ ตัวอย่างต่อไปนี้ใช้การดำเนินการเหล่านี้ −

#include <stdio.h>

int main () {

   int  var = 20;   /* actual variable declaration */
   int  *ip;        /* pointer variable declaration */

   ip = &var;  /* store address of var in pointer variable*/

   printf("Address of var variable: %x\n", &var  );

   /* address stored in pointer variable */
   printf("Address stored in ip variable: %x\n", ip );

   /* access the value using the pointer */
   printf("Value of *ip variable: %d\n", *ip );

   return 0;
}


เมื่อโค้ดด้านบนถูกคอมไพล์และรัน มันจะให้ผลลัพธ์ดังต่อไปนี้ −

Address of var variable: 62fe14
Address stored in ip variable: 62fe14
Value of *ip variable: 20


ตัวชี้ NULL


แนวทางปฏิบัติที่ดีในการกำหนดค่า NULL ให้กับตัวแปรพอยน์เตอร์ ถือเป็นแนวทางปฏิบัติที่ดีเสมอ ในกรณีที่คุณไม่มีที่อยู่ที่แน่นอนที่จะกำหนด นี้จะทำในเวลาของการประกาศตัวแปร ตัวชี้ที่กำหนดเป็น NULL เรียกว่าตัวชี้ค่า null

ตัวชี้ NULL เป็นค่าคงที่ที่มีค่า 0 ที่กำหนดไว้ในไลบรารีมาตรฐานหลายแห่ง พิจารณาโปรแกรมต่อไปนี้ −

#include <stdio.h>

int main () {

   int  *ptr = NULL;

   printf("The value of ptr is : %x\n", ptr  );
 
   return 0;
}


เมื่อโค้ดด้านบนถูกคอมไพล์และรัน มันจะให้ผลลัพธ์ดังต่อไปนี้ −

The value of ptr is 0

ในระบบปฏิบัติการส่วนใหญ่ โปรแกรมไม่ได้รับอนุญาตให้เข้าถึงหน่วยความจำที่ที่อยู่ 0 เนื่องจากระบบปฏิบัติการสงวนหน่วยความจำนั้นไว้ อย่างไรก็ตาม ที่อยู่หน่วยความจำ 0 มีความสำคัญเป็นพิเศษ เป็นสัญญาณว่าตัวชี้ไม่ได้ตั้งใจให้ชี้ไปที่ตำแหน่งหน่วยความจำที่สามารถเข้าถึงได้ แต่ตามแบบแผน หากตัวชี้มีค่าว่าง (ศูนย์) จะถือว่าไม่ชี้ไปที่ค่าใดเลย

ในการตรวจสอบหาตัวชี้ null คุณสามารถใช้คำสั่ง ‘if’ ดังนี้ −

if(ptr)     /* succeeds if p is not null */
if(!ptr)    /* succeeds if p is null */


รายละเอียดพอยน์เตอร์


พอยน์เตอร์มีแนวคิดมากมายแต่ง่าย และมีความสำคัญต่อการเขียนโปรแกรม C แนวคิดตัวชี้ที่สำคัญต่อไปนี้ควรมีความชัดเจนสำหรับโปรแกรมเมอร์ C ทุกคน −

พอยน์เตอร์เลขคณิต


พอยน์เตอร์ ใน c คือที่อยู่ ซึ่งเป็นค่าตัวเลข ดังนั้น คุณสามารถดำเนินการทางคณิตศาสตร์บนพอยน์เตอร์ได้เช่นเดียวกับที่คุณทำกับค่าตัวเลข มีตัวดำเนินการเลขคณิตสี่ตัวที่สามารถใช้กับพอยน์เตอร์ได้: ++, –, + และ –

เพื่อให้เข้าใจเลขคณิตของพอยน์เตอร์ ให้เราพิจารณาว่า ptr เป็นตัวชี้จำนวนเต็มที่ชี้ไปยังที่อยู่ 1000 สมมติว่าเป็นจำนวนเต็ม 32 บิต ให้เราดำเนินการเลขคณิตต่อไปนี้บนตัวชี้:

ptr++


หลังจากการดำเนินการข้างต้น ptr จะชี้ไปที่ตำแหน่ง 1004 เนื่องจากทุกครั้งที่ ptr เพิ่มขึ้น ptr จะชี้ไปที่ตำแหน่งจำนวนเต็มถัดไปซึ่งมีขนาด 4 ไบต์ถัดจากตำแหน่งปัจจุบัน การดำเนินการนี้จะย้ายตัวชี้ไปยังตำแหน่งหน่วยความจำถัดไปโดยไม่กระทบต่อค่าจริงที่ตำแหน่งหน่วยความจำ หาก ptr ชี้ไปที่อักขระที่มีที่อยู่คือ 1000 การดำเนินการข้างต้นจะชี้ไปที่ตำแหน่ง 1001 เนื่องจากอักขระถัดไปจะอยู่ที่ 1001

การเพิ่มพอยน์เตอร์


เราชอบใช้ พอยน์เตอร์ ในโปรแกรมของเรามากกว่าในอาร์เรย์ เนื่องจากตัวชี้ตัวแปรสามารถเพิ่มได้ ซึ่งแตกต่างจากชื่ออาร์เรย์ที่ไม่สามารถเพิ่มได้เนื่องจากเป็น พอยน์เตอร์ แบบคงที่ โปรแกรมต่อไปนี้จะเพิ่มตัวชี้ตัวแปรเพื่อเข้าถึงแต่ละองค์ประกอบที่ประสบความสำเร็จของอาร์เรย์ −

#include <stdio.h>

const int MAX = 3;

int main () {

   int  var[] = {10, 100, 200};
   int  i, *ptr;

   /* let us have array address in pointer */
   ptr = var;
	
   for ( i = 0; i < MAX; i++) {

      printf("Address of var[%d] = %x\n", i, ptr );
      printf("Value of var[%d] = %d\n", i, *ptr );

      /* move to the next location */
      ptr++;
   }
	
   return 0;
}


เมื่อโค้ดด้านบนถูกคอมไพล์และรัน มันจะให้ผลลัพธ์ดังต่อไปนี้ −

Address of var[0] = 62fe00
Value of var[0] = 10
Address of var[1] = 62fe04
Value of var[1] = 100
Address of var[2] = 62fe08
Value of var[2] = 200


การลดค่าพอยน์เตอร์


ข้อควรพิจารณาเดียวกันนี้ใช้กับการลดค่าพอยน์เตอร์ ซึ่งจะลดค่าของตัวชี้ด้วยจำนวนไบต์ของประเภทข้อมูลดังที่แสดงด้านล่าง:

#include <stdio.h>

const int MAX = 3;

int main () {

   int  var[] = {10, 100, 200};
   int  i, *ptr;

   /* let us have array address in pointer */
   ptr = &var[MAX-1];
	
   for ( i = MAX; i > 0; i--) {

      printf("Address of var[%d] = %x\n", i-1, ptr );
      printf("Value of var[%d] = %d\n", i-1, *ptr );

      /* move to the previous location */
      ptr--;
   }
	
   return 0;
}


เมื่อโค้ดด้านบนถูกคอมไพล์และรัน มันจะให้ผลลัพธ์ดังต่อไปนี้ −

Address of var[2] = 62fe08
Value of var[2] = 200
Address of var[1] = 62fe04
Value of var[1] = 100
Address of var[0] = 62fe00
Value of var[0] = 10


การเปรียบเทียบตัวชี้


พอยน์เตอร์ อาจถูกเปรียบเทียบโดยใช้ตัวดำเนินการเชิงสัมพันธ์ เช่น ==, < และ > หาก p1 และ p2 ชี้ไปที่ตัวแปรที่เกี่ยวข้องกัน เช่น อิลิเมนต์ของอาร์เรย์เดียวกัน p1 และ p2 ก็สามารถเปรียบเทียบได้อย่างมีความหมาย

โปรแกรมต่อไปนี้แก้ไขตัวอย่างก่อนหน้านี้ – อย่างใดอย่างหนึ่งโดยการเพิ่มตัวชี้ตัวแปรตราบใดที่ที่อยู่ซึ่งชี้นั้นน้อยกว่าหรือเท่ากับที่อยู่ขององค์ประกอบสุดท้ายของอาร์เรย์ซึ่งก็คือ &var[MAX – 1]

#include <stdio.h>

const int MAX = 3;

int main () {

   int  var[] = {10, 100, 200};
   int  i, *ptr;

   /* let us have array address in pointer */
   ptr = &var[MAX-1];
	
   for ( i = MAX; i > 0; i--) {

      printf("Address of var[%d] = %x\n", i-1, ptr );
      printf("Value of var[%d] = %d\n", i-1, *ptr );

      /* move to the previous location */
      ptr--;
   }
	
   return 0;
}


เมื่อโค้ดด้านบนถูกคอมไพล์และรัน มันจะให้ผลลัพธ์ดังต่อไปนี้ −

Address of var[2] = 62fe08
Value of var[2] = 200
Address of var[1] = 62fe04
Value of var[1] = 100
Address of var[0] = 62fe00
Value of var[0] = 10


อาร์เรย์ของพอยน์เตอร์


ก่อนที่เราจะเข้าใจแนวคิดของอาร์เรย์ของพอยน์เตอร์ ให้เราพิจารณาตัวอย่างต่อไปนี้ซึ่งใช้อาร์เรย์ของจำนวนเต็ม 3 ตัว:

#include <stdio.h>
 
const int MAX = 3;
 
int main () {

   int  var[] = {10, 100, 200};
   int i;
 
   for (i = 0; i < MAX; i++) {
      printf("Value of var[%d] = %d\n", i, var[i] );
   }
   
   return 0;
}


เมื่อโค้ดด้านบนถูกคอมไพล์และรัน มันจะให้ผลลัพธ์ดังต่อไปนี้ −

Value of var[0] = 10
Value of var[1] = 100
Value of var[2] = 200

อาจมีสถานการณ์เมื่อเราต้องการรักษาอาร์เรย์ ซึ่งสามารถเก็บพอยน์เตอร์ไปยัง int หรือ char หรือประเภทข้อมูลอื่นๆ ที่มีอยู่ ต่อไปนี้เป็นการประกาศอาร์เรย์ของพอยน์เตอร์เป็นจำนวนเต็ม −

int *ptr[MAX];

มันประกาศ ptr เป็นอาร์เรย์ของพอยน์เตอร์จำนวนเต็ม MAX ดังนั้น แต่ละองค์ประกอบใน ptr จะถือตัวชี้ไปที่ค่า int ตัวอย่างต่อไปนี้ใช้จำนวนเต็ม 3 จำนวนซึ่งเก็บไว้ในอาร์เรย์ของพอยน์เตอร์ดังนี้ −

#include <stdio.h>
 
const int MAX = 3;
 
int main () {

   int  var[] = {10, 100, 200};
   int i, *ptr[MAX];
 
   for ( i = 0; i < MAX; i++) {
      ptr[i] = &var[i]; /* assign the address of integer. */
   }
   
   for ( i = 0; i < MAX; i++) {
      printf("Value of var[%d] = %d\n", i, *ptr[i] );
   }
   
   return 0;
}


เมื่อโค้ดด้านบนถูกคอมไพล์และรัน มันจะให้ผลลัพธ์ดังต่อไปนี้ −

Value of var[0] = 10
Value of var[1] = 100
Value of var[2] = 200


คุณสามารถใช้อาร์เรย์ของพอยน์เตอร์ ไปยังอักขระเพื่อจัดเก็บรายการสตริงได้ดังนี้ −

#include <stdio.h>
 
const int MAX = 4;
 
int main () {

   char *names[] = {
      "Zara Ali",
      "Hina Ali",
      "Nuha Ali",
      "Sara Ali"
   };
   
   int i = 0;

   for ( i = 0; i < MAX; i++) {
      printf("Value of names[%d] = %s\n", i, names[i] );
   }
   
   return 0;
}

เมื่อโค้ดด้านบนถูกคอมไพล์และรัน มันจะให้ผลลัพธ์ดังต่อไปนี้ −

Value of names[0] = Zara Ali
Value of names[1] = Hina Ali
Value of names[2] = Nuha Ali
Value of names[3] = Sara Ali

Pointer to Pointer


พอยน์เตอร์ไปยังพอยน์เตอร์เป็นรูปแบบของทางอ้อมหลายทางหรือสายของ พอยน์เตอร์ โดยปกติ พอยน์เตอร์จะมีที่อยู่ของตัวแปร เมื่อเรากำหนดตัวชี้ไปยังพอยน์เตอร์ พอยน์เตอร์ตัวแรกจะมีที่อยู่ของพอยน์เตอร์ ตัวที่สอง ซึ่งชี้ไปยังตำแหน่งที่มีค่าจริงดังที่แสดงด้านล่าง

Pointer to Pointer


ตัวแปรที่เป็นตัวชี้ไปยัง พอยน์เตอร์ จะต้องประกาศเช่นนั้น ทำได้โดยวางเครื่องหมายดอกจันเพิ่มเติมหน้าชื่อ ตัวอย่างเช่น ประกาศต่อไปนี้ประกาศตัวชี้ไปยังตัวชี้ประเภท int −

int **var;


เมื่อค่าเป้าหมายถูกชี้ไปทางอ้อมโดยตัวชี้ไปยังตัวชี้ การเข้าถึงค่านั้นต้องใช้ตัวดำเนินการเครื่องหมายดอกจันสองครั้ง ดังแสดงในตัวอย่างด้านล่าง:

#include <stdio.h>
 
int main () {

   int  var;
   int  *ptr;
   int  **pptr;

   var = 3000;

   /* take the address of var */
   ptr = &var;

   /* take the address of ptr using address of operator & */
   pptr = &ptr;

   /* take the value using pptr */
   printf("Value of var = %d\n", var );
   printf("Value available at *ptr = %d\n", *ptr );
   printf("Value available at **pptr = %d\n", **pptr);

   return 0;
}


เมื่อโค้ดด้านบนถูกคอมไพล์และรัน มันจะให้ผลลัพธ์ดังต่อไปนี้ −

Value of var = 3000
Value available at *ptr = 3000
Value available at **pptr = 3000


การส่ง พอยน์เตอร์ไปยังฟังก์ชันใน C


การเขียนโปรแกรม C อนุญาตให้ส่ง พอยน์เตอร์ ไปยังฟังก์ชัน ในการดำเนินการดังกล่าว ให้ประกาศพารามิเตอร์ของฟังก์ชันเป็นประเภทตัวชี้

ต่อไปนี้คือตัวอย่างง่ายๆ ที่เราส่ง unsigned long pointer ไปยังฟังก์ชันและเปลี่ยนค่าภายในฟังก์ชันซึ่งสะท้อนกลับมาในฟังก์ชันการเรียก:

#include <stdio.h>
#include <time.h>
 
void getSeconds(unsigned long *par);

int main () {

   unsigned long sec;
   getSeconds( &sec );

   /* print the actual value */
   printf("Number of seconds: %ld\n", sec );

   return 0;
}

void getSeconds(unsigned long *par) {
   /* get the current number of seconds */
   *par = time( NULL );
   return;
}


เมื่อโค้ดด้านบนถูกคอมไพล์และรัน มันจะให้ผลลัพธ์ดังต่อไปนี้ −

Number of seconds: 1637062225


ฟังก์ชันซึ่งรับพอยน์เตอร์ ยังสามารถยอมรับอาร์เรย์ดังแสดงในตัวอย่างต่อไปนี้ −

#include <stdio.h>
 
/* function declaration */
double getAverage(int *arr, int size);
 
int main () {

   /* an int array with 5 elements */
   int balance[5] = {1000, 2, 3, 17, 50};
   double avg;
 
   /* pass pointer to the array as an argument */
   avg = getAverage( balance, 5 ) ;
 
   /* output the returned value  */
   printf("Average value is: %f\n", avg );
   return 0;
}

double getAverage(int *arr, int size) {

   int  i, sum = 0;       
   double avg;          
 
   for (i = 0; i < size; ++i) {
      sum += arr[i];
   }
 
   avg = (double)sum / size;
   return avg;
}


เมื่อโค้ดข้างต้นถูกคอมไพล์และดำเนินการ โค้ดดังกล่าวจะทำให้เกิดผลลัพธ์ดังต่อไปนี้ −

Average value is: 214.400000


ส่งคืนพอยน์เตอร์จากฟังก์ชันใน C


เราได้เห็นแล้วว่าการเขียนโปรแกรม C อนุญาตให้ส่งคืนอาร์เรย์จากฟังก์ชันได้อย่างไรในบทที่แล้ว ในทำนองเดียวกัน C ยังอนุญาตให้ส่งคืน พอยน์เตอร์ จากฟังก์ชัน ในการทำเช่นนั้น คุณจะต้องประกาศฟังก์ชันที่ส่งคืน พอยน์เตอร์ ดังตัวอย่างต่อไปนี้ −

int * myFunction() {
   .
   .
   .
}


จุดที่สองที่ต้องจำไว้คือ ไม่ควรส่งคืนที่อยู่ของตัวแปรโลคอลนอกฟังก์ชัน ดังนั้น คุณจะต้องกำหนดตัวแปรโลคอลเป็นตัวแปรสแตติก

ตอนนี้ ให้พิจารณาฟังก์ชันต่อไปนี้ซึ่งจะสร้างตัวเลขสุ่ม 10 ตัวและส่งกลับโดยใช้ชื่ออาร์เรย์ซึ่งแสดงถึงตัวชี้ กล่าวคือ ที่อยู่ขององค์ประกอบอาร์เรย์แรก

#include <stdio.h>
#include <time.h>
 
/* function to generate and return random numbers. */
int * getRandom( ) {

   static int  r[10];
   int i;
 
   /* set the seed */
   srand( (unsigned)time( NULL ) );
	
   for ( i = 0; i < 10; ++i) {
      r[i] = rand();
      printf("%d\n", r[i] );
   }
 
   return r;
}
 
/* main function to call above defined function */
int main () {

   /* a pointer to an int */
   int *p;
   int i;

   p = getRandom();
	
   for ( i = 0; i < 10; i++ ) {
      printf("*(p + [%d]) : %d\n", i, *(p + i) );
   }
 
   return 0;
}


เมื่อโค้ดข้างต้นถูกคอมไพล์และดำเนินการ โค้ดดังกล่าวจะทำให้เกิดผลลัพธ์ดังต่อไปนี้ −

22743
5361
163
3199
12097
6761
11103
23331
22033
28678
*(p + [0]) : 22743
*(p + [1]) : 5361
*(p + [2]) : 163
*(p + [3]) : 3199
*(p + [4]) : 12097
*(p + [5]) : 6761
*(p + [6]) : 11103
*(p + [7]) : 23331
*(p + [8]) : 22033
*(p + [9]) : 28678

Leave a Reply

Your email address will not be published. Required fields are marked *