ارتباط بین آرایه ها و اشاره گرها در برنامه نویسی C

در این آموزش با ارتباط بین آرایه ها و اشاره گرها در برنامه نویسی C آشنا خواهید شد. همچنین می توانید با استفاده از اشاره گرها به عناصر آرایه دسترسی پیدا کنید.
در زبان C، آرایهها و اشارهگرها پیوندی بسیار عمیق، ذاتی و تقریباً جدانشدنی دارند؛ تا جایی که هسته زبان و کامپایلر در بسیاری از مواقع با هر دوی آنها دقیقاً به یک شکل رفتار میکند. در حقیقت، آرایه یک بلوک پیوسته از خانههای حافظه است و اشارهگر، ابزاری است که با استفاده از آن میتوان در این بلوک قدم زد و خانهها را پیمایش کرد.
ویژگیهای بنیادین ارتباط این دو
-
نام آرایه، یک آدرس پنهان: مهمترین و کلیدیترین پیوند این دو مفهوم در این است که «نام یک آرایه» (بدون هیچ کروشهای)، در زبان C دقیقاً معادل با «آدرسِ فیزیکیِ اولین خانه» آن آرایه در حافظه است. به عبارتی، نام آرایه در ذات خود یک اشارهگر است که به نقطه شروع بلوک دادهها اشاره میکند.
-
محاسبات ریاضی پنهان (Pointer Arithmetic): زمانی که شما از کروشهها برای دسترسی به خانههای مختلف آرایه استفاده میکنید، زبان C در پشت صحنه هیچ درکی از مفهوم کروشه ندارد! کامپایلر این درخواست را مستقیماً به «ریاضیات اشارهگرها» ترجمه میکند. او آدرسِ نقطه شروع را میگیرد و بر اساس جنس دادهها، روی حافظه سختافزاری گام برمیدارد تا به خانه مورد نظر برسد. کروشهها صرفاً یک روکش ظاهری برای راحتی برنامهنویس هستند.
-
پدیده تنزل (Decay) در توابع: یکی از پدیدههای عجیب زبان C این است که وقتی یک آرایه کامل را به عنوان ورودی به یک تابع میفرستید، آرایه تمام هویت، اندازه و ابعاد خود را از دست میدهد و اصطلاحاً «تنزل» پیدا میکند. چیزی که تابع دریافت میکند دیگر یک آرایه نیست، بلکه صرفاً یک اشارهگر ساده به نقطه شروع آن است. به همین دلیل توابع در C هرگز به طور خودکار نمیدانند طول آرایهای که به آنها پاس داده شده چقدر است.
-
تفاوت در لنگرگاه حافظه: با وجود تمام این شباهتها، یک تفاوت حیاتی بین آنها وجود دارد؛ یک متغیر اشارهگر کاملاً آزاد است و میتواند در طول برنامه تغییر مسیر دهد و به خانههای مختلفی اشاره کند. اما «نام آرایه»، یک اشارهگر ثابت (لنگرانداخته) است که آدرسِ نقطه شروعِ آن برای همیشه در حافظه قفل شده و تحت هیچ شرایطی قابل تغییر مسیر نیست.
هدف و فلسفه این ارتباط تنگاتنگ
فلسفه این طراحی هوشمندانه، دستیابی به بالاترین حد سرعت پردازش و جلوگیری مطلق از کپی شدن دادههای حجیم در سیستم است.
طراحان زبان C میدانستند که آرایهها ممکن است حاوی هزاران یا میلیونها تکه داده باشند. اگر قرار بود با هر بار ارسال یک آرایه به یک تابع یا بخش دیگری از برنامه، تمام آن بلوک عظیم اطلاعاتی در حافظه کپی شود، سیستمعامل به سرعت فلج میشد. با گره زدن ماهیت آرایه به اشارهگرها، زبان C تضمین میکند که حتی برای پردازش بزرگترین بلوکهای داده، تنها چیزی که بین بخشهای مختلف سیستم جابهجا میشود، صرفاً یک «آدرس فیزیکی بسیار کوچک» از نقطه شروع آن است. این معماری، یکی از اصلیترین دلایل سرعت بیرقیب زبان C در تعامل با سختافزار است.
اما قبل از این حتماً دو آموزش زیر را مرور کنید:
- آرایه ها در برنامه نویسی C
- اشاره گرها در برنامه نویسی C
ارتباط بین آرایه ها و اشاره گرها
یک آرایه بلوکی از داده های کنار هم و متوالی است. برنامه زیر برای چاپ آدرس عناصر آرایه است،
#include
int main() {
int x[4];
int i;
for(i = 0; i < 4; ++i) {
printf(“&x[%d] = %pn”, i, &x[i]);
}
printf(“Address of array x: %p”, x);
return 0;
}
خروجی
&x[0] = 1450734448
&x[1] = 1450734452
&x[2] = 1450734456
&x[3] = 1450734460
Address of array x: 1450734448
بین دو عنصر متوالی درآرایه x به اندازه ۴ بایت تفاوت وجود دارد. چون اندازه داده int برابر با ۴ بایت است (در این کامپایلر). توجه داشته باشید که آدرس برای
&x[0] و x
یکسان است. زیرا نام متغیر x به اولین عنصر آرایه اشاره می کند. در مثال بالا این موضوع کاملا مشخص است و [x[0 نیز معادل *x است.

به طور مشابه،
- [x[1& معادل x + 1 و [x[1 معادل (x+1)* است.
- [x[2& معادل x + 2 و [x[2 معادل (x+2)* است.
- …
- به طور کلی [x[i& معادل x + i و [x[i معادل *(x+i) است.
مثال ۱: اشاره گرها و آرایه ها
#include
int main() {
int i, x[6], sum = 0;
printf(“Enter 6 numbers: “);
for(i = 0; i < 6; ++i) {
// است. scanf(“%d”, &x[i]); با
scanf(“%d”, x+i);
// است sum += x[i] با
sum += *(x+i);
}
printf(“Sum = %d”, sum);
return 0;
}
خروجی
Enter 6 numbers: 2
۳
۴
۴
۱۲
۴
Sum = 29
در این برنامه، آرایه x دارای ۶ عنصر است و برای دسترسی به عناصر آن از اشاره گرها استفاده شده است.
در بیشتر موارد نام آرایه ها به اشاره گرها تبدیل می شوند. به همین دلیل می توانید از اشاره گر برای دسترسی به عناصر آرایه استفاده کنید. با این حال باید توجه داشته باشید که اشاره گرها و آرایه ها یکسان نیستند.
چندین مورد وجود دارد که نام آرایه نمی تواند به اشاره گر تبدیل شود.
مثال ۲: آرایه ها و اشاره گرها
#include
int main() {
int x[5] = {1, 2, 3, 4, 5};
int* ptr;
// ذخیره می شود ptr آدرس سومین عنصر آرایه در
ptr = &x[2];
printf(“*ptr = %d n”, *ptr); // 3
printf(“*(ptr+1) = %d n”, *(ptr+1)); // 4
printf(“*(ptr-1) = %d”, *(ptr-1)); // 2
return 0;
}
خروجی
*ptr = 3
*(ptr+1) = 4
*(ptr-1) = 2
در این مثال، [x[2& یعنی آدرس عنصر سوم به اشاره گر ptr اختصاص داده شده است. بنابراین، ptr* عدد ۳ را نمایش می دهد. به همین ترتیب (ptr+1)* مقدار عنصر چهارم و (ptr-1)* مقدار عنصر دوم آرایه را برمی گردانند.
