نکات برنامه نویسی پیشرفته C و ++C – اشاره گر های تابعی، typedef ، const

نکات برنامه نویسی پیشرفته C و ++C  ، شاید برای شما هم اتفاق افتاده باشد که در زبان C یا ++C با عبارت های عجیبی مانند زیر رو به رو شده اید :

int * (* (*fp1) (int) ) [10];

یا عبارت های مشابهی که درک آن برای شما سخت بوده باشد. در این آموزش برنامه نویسی پیشرفته میخواهیم تحلیل و تفسیر این عبارات پیچیده و دستورات typedef ، const و اشاره گرهای تابعی را بررسی کنیم.

نکات برنامه نویسی پیشرفته C و ++C

مقدمه ای بر نکات برنامه نویسی پیشرفته

همان طور که اشاره شد در این آموزش میخواهیم به تحلیل و تفسیر عبارات به ظاهر پیچیده برنامه نویسی C/C++  بپردازیم. در ابتدا از عبارت های ساده تر شروع خواهیم کرد و در ادامه به عبارت های به ظاهر پیچیده تر میپردازیم. در واقع از مثال های روزمره که در اکثر برنامه ها دیده می شوند شروع خواهیم کرد و سپس به عبارت های const و typedef و اشاره گر های تابع خواهیم پرداخت و قانون راست-چپ را نیز بررسی خواهیم کرد که توسط آن میتوانید هر عبارت پیچیده ای را تحلیل نمایید.

عبارت های ابتدایی

اجازه دهید با یک مثال بسیار ساده شروع کنیم. عبارت زیر را در نظر بگیرید :

int n;

این عبارت n از نوع int تعریف می کند.

حال تعریف یک اشاره گر را بررسی می کنیم :

int *p;

این عبارت p را از نوع اشاره گر به نوع int تعریف می کند یا در واقع این اشاره گر به نوع int اشاره می کند. در اینجا میخواهم به این نکته هم اشاره کنم که بهتر است همیشه هنگام تعریف یا استفاده از اشاره گر یا ریفرنس از علامت های * و & استفاده نمایید تا از اشتباه جلوگیری شود. به عنوان مثال :

int* p,q;

در ابتدا ممکن است با دیدن این عبارت تصور شود که p و q دو اشاره گر از نوع int هستند اما در واقع فقط p اشاره گر است و q یک متغیر معمولی از نوع int است.

حال به سراغ تعریف اشاره گر به اشاره گر میرویم :

char **argv;

از لحاظ قوانین برنامه نویسی در این تعریف محدودیتی وجود ندارد، یعنی میتوانید تعریف کنید اشاره گر به اشاره گر به اشاره گر به اشاره گر … به نوع اعشاری داشته باشید.

تعریف زیر را در نظر بگیرید :

int RollNum[30][4];
int (*p)[4]=RollNum;
int *q[5];

در اینجا p اشاره گری به آرایه ای از نوع int با 4 خانه تعریف شده است، در حالی که q به عنوان آرایه ای 5 تایی از نوع اشاره گر به int تعریف شده است.

میتوانیم عبارتی شامل ترکیبی از *s و &s داشته باشیم، به عنوان مثال :

int **p1; // p1 is a pointer to a pointer to an int.
int *&p2; // p2 is a reference to a pointer to an int.
int &*p3; // ERROR: Pointer to a reference is illegal.
int &&p4; // ERROR: Reference to a reference is illegal.

دستور const

از دستور const برای تعریف کردن متغیرهایی استفاده می شود که مقدار آنها نباید تغییر کند. هنگام استفاده از const باید به متغیر مقدار اولیه بدهیم چون دیگر نمیتوانیم مقدار آن را تغییر دهیم. به عنوان مثال :

const int n=5;
int const m=10;

هر دو متغیر n و m از نوع int ثابت هستند. توجه شود که طبق قوانین ++C میتوانیم const را جلو یا عقب نوع متغیر بنویسیم. البته نوع اول توصیه میشود چون نوع ثابت آن صریح تر برجسته می شود.

مطلب پیشنهادی:  مفهوم کلاس Class و شیء Object در برنامه نویسی ++C

زمانی که const و اشاره گر ترکیب می شوند ممکن است عبارت کمی گمراه کننده باشد. به عنوان مثال عبارت زیر را در نظر بگیرید :

const int *p;
int const *q;

در این عبارات کدامیک اشاره گری ثابت به نوع int است و کدامیک اشاره گیری به نوع cons tint است؟ هر دو عبارت اشاره گری به نوع cons tint است. در واقع معنای آن این است که نمیتوانیم مقدار *p یا *q را تغییر دهیم. برای تعریف اشاره گر ثابت مطابق زیر عمل می کنیم :

int * const r= &n; // n has been declared as an int

در این حالت چون اشاره گر از نوع ثابت است نمیتوانیم بنویسیم r=&m (m متغیری از نوع int است) اما مقدار *r قابل تغییر است.

برای ترکیب این دو حالت و تعریف اشاره گری ثابت به cons tint :

const int * const p=&n // n has been declared as const int

مثال های زیر به خوبی نحوه تحلیل دستورات شامل const را نشان می دهد. توجه شود که در برخی از این عبارات باید حتما مقداردهی اولیه نیز صورت گیرد که برای سادگی در این مثال های صرف نظر شده است :

char ** p1; // pointer to pointer to char
const char **p2; // pointer to pointer to const char
char * const * p3; // pointer to const pointer to char
const char * const * p4; // pointer to const pointer to const char
char ** const p5; // const pointer to pointer to char
const char ** const p6; // const pointer to pointer to const char
char * const * const p7; // const pointer to const pointer to char
const char * const * const p8; // const pointer to const pointer to const char

دستور typedef

از دستور typedef به منظور تغییر نام برخی از keyword های برنامه نویسی و همچنین ساده سازی عبارات استفاده می شود. به عنوان مثال :

typedef char * PCHAR;
PCHAR p,q;

در اینجا هم p و هم q از نوع اشاره گر تعریف شده اند. توجه شود اگر از typedef استفاده نشده بود q از نوع char تعریف میشد.

مطلب پیشنهادی:  بهترین زبان های برنامه نویسی در الکترونیک

به چند مثال دیگر از typedef دقت نمایید :

typedef char * a; // a is a pointer to a char

typedef a b(); // b is a function that returns
// a pointer to a char

typedef b *c; // c is a pointer to a function
// that returns a pointer to a char

typedef c d(); // d is a function returning
// a pointer to a function
// that returns a pointer to a char

typedef d *e; // e is a pointer to a function 
// returning a pointer to a 
// function that returns a 
// pointer to a char

e var[10]; // var is an array of 10 pointers to 
// functions returning pointers to 
// functions returning pointers to chars.

Typedef مطابق مثال زیر معمولا به همراه تعریف ساختارها استفاده می شود. در این روش در زبان C مشابه زبان C++ نیازی به استفاده از واژه struct هنگام تعریف ساختارها وجود ندارد.

typedef struct tagPOINT
{
int x;
int y;
}POINT;

POINT p; /* Valid C code */

اشاره گر های تابعی

اشاره گرهای تابعی احتمالا جزء گیج کننده ترین بخش اعلانات و متغیرها در برنامه نویسی می باشد. اشاره گرهای تابعی در گذشته برای برنامه نویسی TSR در سیستم عامل DOS ، Win32 و X-Windows برای توابع با فراخوان بازگشتی (callback) استفاده میشده است. همچنین از این توابع در کاربردهای دیگری مانند  ساختن جدول توابع مجازی، ساختن قالب های آماده در STL و سرویس های Win NT/2K/XP استفاده می شود. با هم چند مثال از اشاره گرهای تابعی را بررسی میکنیم :

int (*p)(char);

در این مثال p از نوع اشاره گری به تابع تعریف می شود که ورودی تابع char و خروجی آن int است.

char ** (*p)(float, float);

در مثال بعدی اشاره گری به تابعی با دو ورودی اعشاری تعریف می شود که در خروجی آن اشاره گر به اشاره گر به نوع char تعریف شده است.

void * (*a[5])(char * const, char * const);

و در این مثال بعدی آرایه ای از 5 اشاره گر به تابعی با دو ورودی اشاره گر ثابت به char تعریف شده است که خروجی آن به اشاره گر تهی تعریف شده است.

قانون راست به چپ (مهم) :

این قانون به سادگی نحوه تحلیل تعاریف متغیرها و اشاره گرهای پیچیده را به شما یاد می دهد. این قانون عبارت است از :

“خواندن را از داخلی ترین پرانتز شروع کنید، به راست و سمت به چپ حرکت کنید. زمانی که به پرانتز برخورد کردید، جهت حرکت خود را عوض نمایید. زمانی که تمام بخش های داخل پرانتز را خواندید، از پرانتز خارج شوید و خواندن را تا انتهای کد ادامه دهید.”

نکته : در هنگام شروع به خواندن، به جای شروع از داخلی ترین پرانتز، از نام متغیر یا اشاره گر شروع کنید.

حالا چند مثال را با هم بررسی کنیم :

int * (* (*fp1) (int) ) [10];

این کد را اینگونه تحلیل می کنیم :

  • از نام متغیر شروع می کنیم : fp1
  • سمت راست به پرانتز برمیخوریم، پس به سمت چپ می رویم و * را میبینیم : اشاره گر
  • از پرانتز خارج می شویم و به int برمیخوریم : به تابعی با ورودی int
  • به چپ میرویم و به * برمیخوریم : خروجی تابع به اشاره گر
  • از پرانتز خارج می شویم، به سمت راست حرکت میکنیم و به [10] برمیخوریم : به آرایه ای 10 تایی
  • به سمت چپ حرکت میکنیم و به * برمیخوریم : اشاره گری به
  • مجددا به سمت چپ حرکت میکنیم و به int برمیخوریم : خروجی int
مطلب پیشنهادی:  برنامه نویسی با کیوت یا Qt

یک مثال دیگر را با هم بررسی کنیم :

int *( *( *arr[5])())();

این کد را اینگونه تحلیل می کنیم :

  • از نام متغیر شروع می کنیم : arr
  • سمت راست اندیس آرایه را میبینم : 5
  • به چپ میرویم و به * برمیخوریم : اشاره گر
  • از پرانتز خارج می شویم، به سمت راست حرکت میکنیم و به () برمیخوریم : به ورودی یک تابع
  • به چپ میرویم و به * برمیخوریم : خروجی به اشاره گر
  • از پرانتز خارج می شویم، به سمت راست حرکت میکنیم و به () برمیخوریم : به ورودی یک تابع
  • به چپ میرویم و به * برمیخوریم : خروجی به اشاره گر
  • مجددا به سمت چپ حرکت میکنیم و به int برمیخوریم : خروجی int

مثال های بیشتر  :

اگر هنوز کامل بر روی این قانون تسلط پیدا نکردید پیشنهاد میکنم مثال های زیر را مطالعه کنید :

float ( * ( *b()) [] )(); // b is a function that returns a 
// pointer to an array of pointers
// to functions returning floats.

void * ( *c) ( char, int (*)()); // c is a pointer to a function that takes
// two parameters:
// a char and a pointer to a
// function that takes no
// parameters and returns
// an int
// and returns a pointer to void.

void ** (*d) (int &, 
char **(*)(char *, char **)); // d is a pointer to a function that takes
// two parameters:
// a reference to an int and a pointer
// to a function that takes two parameters:
// a pointer to a char and a pointer
// to a pointer to a char
// and returns a pointer to a pointer 
// to a char
// and returns a pointer to a pointer to void

float ( * ( * e[10]) 
(int &) ) [5]; // e is an array of 10 pointers to 
// functions that take a single
// reference to an int as an argument 
// and return pointers to
// an array of 5 floats.
More Tips : https://www.cprogramming.com/tips

خب امیدواریم مطالب آموزشی برنامه نویسی پیشرفته امروز برای شما مفید واقع شده باشد. اگر علاقمند به برنامه نویسی ++C هستید توصیه میکنم حتما و حتما برنامه نویسی با کیوت یا Qt را یادبگیرید. همچنین بهترین زبان های برنامه نویسی را هم برسی ، و بهترین زبان برنامه نویسی برای کابرد خودتان را انتخاب کنید.

به اشتراک گذاری این نوشته:

درباره ی محمد حسین کوهی قمصری

دانشجوی کارشناسی برق گرایش الکترونیک

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *