برنامه نویسی C در سیستم عامل GNU - قسمت دوم(7630 مجموع کلمات موجود در متن) (9131 بار مطالعه شده است)
نام مقاله : برنامه نویسی به زبان C در سیستم عامل GNU - قسمت دوم
نویسنده : مَهدی رسولی
ویرایشگر: نوید
نسخه : ۱.۰
آخرین تغییر : ۱۳ بهمن ۱۳۸۵
|
فصل سوم:دادهها و عبارتها
برنامههای مفید واقعی شامل دادهها، انجام عملیات بر روی آنها و نمایش آنها در خروجی هستند.در زبان C شما از قطعات نامگذاری شده حافظه که متغیر (variables) نامیده می شود برای نگهداری دادهها استفاده می کنید. برنامههای C می توانند با کمک نام متغیرها دادههای درون آنها را در هر زمان تغییر دهند. هر متغییر یک نام منحصر به فرد (identifier ) دارد که شما برای استفاده یا تغییر مقدارش به آن رجوع می کنید. یک عبارت (expression) هر چیزیست که بتواند مورد ارزیابی قرار گیرد. برای نمونه 1+1 عبارتی با مقدار 2 است. در این عبارت علامت بعلاوه یک عملگر دوتایی (binary operator) است زیرا بر روی دو مقدار(value) برای ساخت یک مقدار عمل میکند.
قواعد نامگذاری متغیرها مشابه نامگذاری توابع است.شما می توانید از اعداد،ارقام و کاراکتر زیر خط در نامگذاری آنها استفاده کنید به شرطی که کاراکتر اول عدد نباشد. همچنین به مانند توابع متغیرها نیز باید قبل از به کارگیری اعلان شوند. نامیکه برای متغیر در نظر می گیرید بهتر است گویای هدفی باشد که متغیر را برای آن به کار می گیریم. این عمل مطالعه کدهای شما را آسانتر می سازد. شما میتوانید متغیرهای خود را شخصا تعریف کرده و یا از انواعی (types) از قبل تعریف شده اند استفاده کنید.
قبل از اینکه در باتلاق اصطلاحات فنی فرو رویم بیایید با هم نگاهی به یک قطعه کد کوچک بیاندازیم تا نشان دهیم همهاینها چقدر ساده است. در این نمونه ما دو متغییر از نوع پیش تعریف شده int را به کار می گیریم:
نمونه 3.1 bicycles.c
include #
int
()main
}
; int number_of_bicycles
; int number_of_wheels
; number_of_bicycles = 6
; number_of_wheels = number_of_bicycles * 2
;( printf("I have %d bicycles ", number_of_bicycles
;( printf("So I have %d wheels ", number_of_wheels
; return 0
{
3.1 تشریح برنامه دوچرخه
مطالب جدید کمی برای بررسی وجود دارد که برای تشریح آنها برنامه را به چند بخش تقسیم می کنیم :
int number_of_bicycles;
int number_of_wheels;
هر یک از این دو سطر یک متغیر را تعریف میکند. Int یکی از انواع داده است که به صورت توکار در زبان C وجود دارد. متغییرهایی از نوع int توانایی نگهداری مقادیر مثبت و یا منفی صحیح را دارا هستند.
این سطر مقدار 6 را در متغییر number_of_bicycles ذخیره میکند. علامت مساوی به عنوان عملگر تخصیص (the assignment operator) شناخته می شود. این عملگر مقدار عبارت سمت راست خود را به به متغییر سمت چپش اختصاص می دهد.
number_of_wheels = number_of_bicycles * 2;
در این سطر علاوه بر عملگر تخصیص از عملگر ضرب (multiplication operator) نیز استفاده شده است. ستاره (asterisk) نیز یک عملگر دوتایی است زیرا دو مقدار را برای ایجاد یک مقدار در هم ضرب میکند. در اینجا این عملگر مقدار 12 را ایجاد می کند که درون متغیر number_of_wheels ذخیره می شود.
printf("I have %d bicycles ", number_of_bicycles);
printf("So I have %d wheels ", number_of_wheels);
در اینجا نیز باز با ()printf سر و کار داریم که البته در شکلی متفاوت از آنچه قبلا دیده بودیم به کار رفته است. در این نمونه ()printf دو نشانوند (arguments) پذیرفته است که توسط یک کاما یا ویرگول از یکدیگر جدا شده اند. نشانوند اول ()printf به عنوان رشته قالب (format string) شناخته می شود. وقتی یک d% در رشته قالب وجود داشته باشد ()printf متوجه می شود که باید منتظر یک نشانوند اضافی باشد تا مقدار آن جایگزین %d گردد. به ازای هر %d باید یک نشانوند اضافی وجود داشته باشد.
با دانستن این مطلب جدید از اینکه پس از کامپایل و اجرای این قطعه کد سطور زیر نمایش داده شود غافلگیر نخواهید شد :
خروجی برنامه
I have 6 bicycles
So I have 12 wheels
مثل همیشه در صورتیکه برخی قسمتها خوب متوجه نشدهاید نگران نباشید زیرا نمونههای بیشتری را انجام خواهیم داد.
3.2انواع دادهای
تمام انواع دادهای که در C تعریف شده اند از قسمتهایی از حافظه که بایت نامیده می شوند ساخته شده اند. در اکثر معماریهای کامپیوتر یک بایت از هشت بیت تشکیل شده است. هر بیت یک مقدار صفر یا یک را نگهداری میکند.
هشت بیت که هر کدام دارای دو حالت باشند در کنار هم ۲۵۶ ترکیب مختلف را ایجاد می کنند. (۲ به توان ۸ حالت) بنابر این یک مقدار صحیح که از دو بایت تشکیل شده است می توانند عددی بین ۰ تا ۶۵۵۳۵ را در خود نگه دارد. (از صفر تا ۲ به توان ۱۶) اما معمولا متغییرهای نوع صحیح از اولین بیت خود برای نگهداری علامت مثبت و یا منفی عدد استفاده می کنند بنابراین می توانند عددی بین مثبت و منفی ۳۲۷۶۸ را در خود نگه دارند. اگر چنانچهاین متغییرها از نوع بدون علامت باشند تنها قادر به نگهداری اعداد بزرگتر یا مساوی صفر خواهند بود. نوع دادهای متغییرهای بدون علامت در ابتدا دارای عبارت unsigned می باشد.
همانطور که قبلا اشاره کردیم ۸ نوع داده پایهای در زبان C تعریف شده اند. ۵ نوع برای نگهداری اعداد صحیح با مقادیر گوناگون و ۳ نوع برای نگهداری مقادیر گوناگون اعداد با ممیز شناور. (اعداد اعشاری) زبان C هیچگونه نوع داده پایهای را برای متن فراهم نکرده است. متنها از کاراکترهای تکی ساخته میشوند و کاراکترها توسط اعداد نمایندگی میشوند. در نمونه قبلی از یکی از انواع دادهای به نام int استفاده کردیم که دارای بیشترین کاربرد در زبان C است.
بیشتر اعدادی که در برنامههای کامپیوتری استفاده میشوند اعداد صحیح هستند. ما کمی بعد درباره انواع با ممیز شناور صحبت خواهیم کرد. به ترتیب اندازه مقادیر صحیح علامت دار و بدون علامت از کوچک به بزرگ عبارتند از:
محدوده
اعداد قابل نگهداری در سیستم
32 بیتی
|
اندازه در سیستم 32 بیتی
|
نوع داده ای
|
ترتیب
|
-127
تا
127
|
8
|
char
|
1
|
0
تا
255
|
8
|
unsigned
char
|
2
|
-32,767
تا
32,767
|
16
|
short
|
3
|
0
تا
65,535
|
16
|
unsigned
short
|
4
|
-2,147,483,647
تا
2,147,483,647
|
32
|
int
|
5
|
0
تا
4,294,967,295
|
32
|
unsigned
int
|
6
|
-2,147,483,647
تا
2,147,483,647
|
32
|
long
|
7
|
0
تا
4,294,967,295
|
32
|
unsigned
long
|
8
|
-9,223,372,036,854,775,807 تا
9,223,372,036,854,775,807
|
64
|
long
long
|
9
|
0
تا
18,446,744,073,709,551,615
|
64
|
unsigned
long long
|
10
|
انواع دادهای کوچکتر دارای این مزیت هستند که مقدار کمتری از حافظه را اشغال میکنند. انواع دادهای بزرگ باعث ایجاد خطای اجرایی (performance penalty) می شوند.متغییرهایی از نوع دادهای int بزرگترین عدد صحیح ممکن را که باعث ایجاد خطای اجرایی (performance penalty) نمیشود نگهداری میکنند به همین دلیل مقدار متغییرهای نوع Int بسته به نوع کامپیوتری که شما استفاده میکنید متفاوت است.
نوع دادهای char معمولا یک بایت است. وجه تسمیه آن بهاین دلیل است کهاین نوع دادهای معمولا برای نگهداری کاراکترهای تنها استفاده می شود. اندازه سایر انواع دادهای به نوع سخت افزار کامپیوتری که شما استفاده میکنید بستگی دارد. اغلب رایانهای رومیزی 32 بیتی هستند که اشاره به اندازه دادهای که آنها برای پردازش آن طراحی شده اند دارد.در رایانههای 32 بیتی نوع دادهای int از 4 بایت تشکیل شده است (2 به توان 32 حالت).نوع دادهای short معمولا کوچکتر از int و نوع دادهای long از نوع دادهای int بزرگتر و یا با آن مساوی است و در نهایت نوع دادهای long long برای نگهداری مقادیر عددی خیلی خیلی بزرگ است.
نوع دادهای متغییری که شما استفاده میکنید تاثیر زیادی بر روی کاهش سرعت اجرا یا اشغال حافظه برنامه شما ندارد.جز در موارد خاص شما می توانید تنها از متغییرهای نوع int استفاده کنید. در دهه گذشته اغلب رایانهها پردازندههای 16 بیتی داشتند که اندازه متغییرهای نوع Int را به 2 بایت محدود می کرد. در حال حاضر متغییرهای از نوع دادهای short از 2 بایت و متغییرهای از نوع دادهای long از 4 بایت تشکیل شده اند. در حال حاضر با پردازندههای 32 بیتی نوع دادهای پیش فرض یعنی int به اندازه کافی برای نگهداری متغییرهای که سابقا از نوع دادهای long برای نگهداری آنها استفاده می شد گنجایش دارد. برای اطلاع از اندازه هر یک از انواع دادهای بر روی رایانه خود قطعه کد زیر را کامپایل و اجرا کنید. در این برنامه از عملگر جدیدی به نام ()sizeof تشکیل شده که مقدار حافظهای را که توسط هر نوع دادهای اشغال می گردد بیان می کند.
نمونه 3.2 sizeof_types.c
int
main()
{
printf("sizeof(char) == %d\n", sizeof(char));
printf("sizeof(short) == %d\n", sizeof(short));
printf("sizeof(int) == %d\n", sizeof(int));
printf("sizeof(long) == %d\n", sizeof(long));
printf("sizeof(long long) == %d\n", sizeof(long long));
return 0;
}
3.3 نمونه دیگری از انتساب
وقت آن رسیده که نمونه دیگری را بررسی کنیم. در این نمونه هم چند مطلب تازه وجود دارد که در یک دقیقه آنرا توضیح خواهیم داد.
نمونه 3.3 displaying_variables.c
#include
int
main()
{
short first_number = -5;
long second_number, third_number;
second_number = 20000 + 10000;
printf("the value of first_number is %hd\n", first_number);
printf("the value of second_number is %ld\n", second_number);
printf("the value of third_number is %ld\n", third_number);
return 0;
}
در نمونه بالا از یک متغییر نوع short و دو متغییر نوع long استفاده کردهایم. می توانستیم هر سه متغییر را از نوع int در نظر بگیریم اما از انواع دادهای دیگر استفاده کردیم تا نشان دهیم این انواع دادهای چقدر به هم شبیه اند. در نخستین سطر تابع main ما متغییری را اعلان و همزمان آن را مقدار دهی کردهایم که این کار بسیار متداول است. در سطر دوم دو متغییر را با جدا کردن آنها از هم به وسیله کاما اعلان نمودهایم. این کار ممکن است نشانه مهارت ما باشد ولی بهتر آنست که هر متغییر را در سطری جدا گانه اعلان کنیم تا به خوانا بودن برنامه کمک شده باشد. سطر سوم بسیار شبیه به برخی کدهای نمونه اول است. عملگر جمع مقدار 30000 را تولید و سپس این مقدار در متغییر second_number قرار می گیرد. آخرین مطلب قابل اشارهاینست که در رشته قالب بندی تابع ()printf به جای %d از %hd برای متغییرهای از نوع دادهای short و از %ld برای متغییرهای از نوع دادهای long استفاده شده است. بهاین عبارتهای کوچک کاراکتری اصطلاحا شاخصهای تبدیل (conversion specifiers) گفته می شود. هر نوع از انواع دادهای نصریح کننده تبدیل مختص خود را دارد. در صورتیکه خواسته باشید کاراکتر درصد (%) را چاپ کنید باید از %% استفاده نمایید.
هنگامییکه برنامه بالا را کامپایل و اجرا کنید مقدار متغییرهای خود را مشاهده خواهید کرد. مقدار متغییر third_number عجیب خواهد بود زیرا در طول برنامه هیچ مقداری به آن اختصاص داده نشده است. وقتی که شما یک متغییر را اعلان می نمایید سیستم عامل مقداری از حافظه را به آن اختصاص می دهد. شما راهی برای فهمیدن اینکهاین قسمت از حافظه قبلا برای چه کاری استفاده شده نخواهید داشت .تا زمانیکه مقداری را به متغییر خود انتساب ندادهاید مقدار ی که از قبل در خانههای حافظه اختصاص یافته به متغییر وجود دارد به متغییر اختصاص خواهد داشت. بنابر این مقدار متغییرها تا قبل از انتساب مقدار مشخص به انها ضرورتا عددی تصادفی و غیر قابل پیش بینی خواهد بود. فراموش کردن انتساب مقدار به متغییرها یک اشتباه رایج در میان برنامه نویسان مبتدی است.
3.4 توضیح سریع تابع ()printf
شما احتمالا متوجه عبارت دو حرفی n\ در مواقعی که از تابع ()printf استفاده می کنیم شدهاید. این عبارت در خروجی چاپ نمیشود بلکه ()printf را متوجه می کند که باید به سطر بعد برود. به طور کلی اسلاش وارونه (\) کاراکتر گریز (escape character) زبان سی محسوب می شود و هرگاه در داخل 2 کوتیشن مارک قرار گیرد کاراکتر بعد از آن معنای مخصوصی را خواهد داد.نمونه دیگر t\ است که به اندازه یک tab فاصله خالی در خروجی ایجاد می کند.
کاراکتر مخصوص دیگری که ()printf آنرا مراعات می کند درصد (%) است که از ()printf میخواهد کاراکترهای بعد از خود را در هنگام چاپ با مقدار یک متغییر مشخص جایگزین نماید. d% رشته کاراکتری است که یک متغییر از نوع دادهای Int را برای چاپ با استفاده از سیستم شمارش بر مبنای ده یا دهدهی نمایندگی می کند. به ازای هر d% در رشته قالب بندی شما باید به تابع ()printf بفهمانید که چه متغییری را قصد جایگزین کردن با آن دارید. در نمونه زیر برخی استفادههای بیشتر تابع ()printf را مشاهده میکنید:
نمونه 3.4 more_printf.c
Int
main()
{
int one = 1;
int two = 2;
int three = 4; /* the values are unimportant here */
printf( "one ==\t%d\ntwo ==\t%d\nthree ==\t%d\n", one, two, three );
return 0;
}
3.5 عملیات ریاضی ساده
در ابتدای این فصل اشاره کردیم که یک برنامه معمولا با انجام عملیات بر روی دادهها سر و کار دارد. با به کار گیری علایم استاندارد ریاضی انجام عملیات ریاضی در زبان c به صورت بسیار آسان قابل فهم و خواندن است. به نمونه زیر توجه کنید:
نمونه 3.5 wages.c
int
main()
{
int hours_per_day;
int days_per_week;
hours_per_day = 8;
days_per_week = 5;
printf("I work %d hours a week.\n", (days_per_week * hours_per_day) );
printf("%d %d hour days\n", days_per_week, hours_per_day);
return 0;
}
3.6 متغییرهای عمومی و محلی
متغییرهایی که تا به حال از آنها استفاده کردیم متغییرهای محلی (local variables) بودند این متغییرها در همان تابعی که قصد استفاده از آنها را داریم تعریف شده پس از اجرای تابع نابود می شوند و همانطور که گفتیم تا قبل از انتساب مقدار آنها غیر قابل پیش بینی است.
دسته دیگری از متغییرها که متغییرهای عمومی (global variables) نام دارند در خارج از توابع و معمولا بالای تابع ()main تعریف می شوند. این متغییرها از داخل تمام توابعی بعد از اعلان آنها تعریف شوند قابل استفاده بوده در ابتدای اجرای برنامهایجاد و تا پایان اجرای برنامه در حافظه باقی می مانند. بر خلاف متغییرهای محلی به محض اعلان این متغییرها مقدار اولیه صفر به آنها تعلق می گیرد.
نمونه 3.6 global_variable.c
#include
int global_varialble;
int first_function(void);
int
main()
{
printf("The first value of global_varialbles is %d \n",global_varialble);
first_function();
return 0;
}
int
first_function()
{
global_varialble = 5;
printf("Now the value of global_varialble is %d \n",global_varialble);
return 0;
}
3.7 حوضه متغییر
منظور از حوضه (scope) یک متغییر محدودهایست که متغییر در آن قابل دسترسی و استفاده می باشد. به عنوان نمونه همانطور که در بالا دیدیم حوضه متغییرهای محلی همان تابعی بود که در آن تعریف شده بودند و حوضه متغییرهای عمومی تمام توابعی بود که بعد از اعلان آن متغییرها تعریف می شدند.
3.8 کلاسهای حافظه
کلاس حافظه خصوصیتی از متغییر است که طول عمر و حوضه متغییر را مشخص می کند.منظور از طول عمر زمان ایجاد و نابود شدن متغییر می باشد. کلاسهای حافظه انواع گوناگونی دارد که در این جا دو مورد از پرکاربرد ترین آنها یعنی کلاس حافظهایستا (static) و کلاس حافظه خارجی (external) را توضیح می دهیم:
3.8.1 کلاس حافظه خارجی
بعضی اوقات کدهای برنامه ما در بیش از یک فایل قرار دارد.شما میتوانید از متغییرهای عمومی که در یکی از فایلها اعلان شده است در فایل دیگری استفاده کنید به شرط اینکه آن متغییر را درفایل دوم نیز اعلان نمایید. دستور اعلان متغییر در فایل دوم باید با کلمه کلیدی extern آغاز گردد. در نمونه زیر کدهای برنامه از دو فایل تشکیل شده است:
نمونه 3.7 main.c
int main()
{
extern int my_var;
my_var = 500;
print_value();
return 0;
}
نمونه 3.8 secondary.c
#include
int my_var;
void print_value()
{
printf("my_var = %d\n", my_var);
}
3.8.2 کلاس حافظهایستا
در زبان C هرگاه یک تابع را بیش از یک بار فراخوانی کنیم با هر بار فراخوانی تابع متغییرهای محلی تعریف شده در تابع از ابتدا ایجاد شده سپس مقدار اولیه گرفته و در انتهای اجرای تابع نابود می شوند اما اگر این متغییرها از نوع کلاس حافظهایستا باشند تنها یک بار مقدار اولیه گرفته در هنگام خروج از تابع آخرین مقدار خود را حفظ کرده و تا انتهای اجرای برنامه در حافظه باقی می مانند. برای اینکه متغییرهای محلی یک تابع را از نوع ایستا تعریف کنیم باید در دستور اعلان آنها ابتدا کلمه کلیدی static را ذکر نماییم تا آن متغییر از نوع محلی ایستا تعریف شود. اما متغییرهای عمومی به طور پیش فرض از نوع ایستا بوده و ذکر یا عدم ذکر کلمه کلیدی static در ابتدای فرمان اعلان آنها بی تاثیر است. در صورتیکه متغییر محلی از نوع ایستا تعریف نشود کلاس حافظه آن از نوع اتوماتیک (automatic) خواهد بود.کلاس حافظه اتوماتیک با آوردن کلمه کلیدی auto در ابتدای دستور اعلان متغییر مشخص می شود. البته چون متغییرهای محلی در صورتیکه کلاس حافظه دیگری برای آنها بیان نشود به طور پیش فرض از نوع اتوماتیک هستند آوردن و یا نیاوردن کلمه کلیدی autoدر ابتدای دستور اعلان آنها یکسان است.
نمونه 3.9 static_variable.c
#include
int first_function(void);
int
main()
{
first_function();
first_function();
first_function();
return 0;
}
int
first_function()
{
int x=0;
static int y=0;
printf(“automatic x = %d static y = %d \n”,x,y);
x=x+1;
y=y+1;
return 0;
}
3.9 متغییرهای غیر قابل تغییر
یک عادت خوب برنامه نویس اینست که هیچ گاه عددی غیر از صفر یا یک را در خلال کدهایتان به کار نبرید!. اگر به ثابت عددی دیگری غیر از صفر و یک احتیاج داشتید آنرا تبدیل به یک متغییر از نوع غیرقابل تغییر (constant) نمایید. برای این کار در ابتدای دستور اعلان متغییر از کلمه کلیدی const استفاده نمایید. نمونه :
Const int my_age = 20;
مقدار این متغییر در تمام طول برنامه 20 باقی خواهد ماند و هر کجا که قصد تغییر آن را داشته باشید با خطای کامپایلر مواجه خواهید شد. عدد 20 معنای مختصری می دهد در حالیکه مشخصهای مانند my_age اطلاعات بیشتری در مورد عملی که تابع انجام می دهد در اختیار ما قرار می دهد. مزیت دیگر استفاده از متغییرهای غیر قابل تغییر در اینست که برای تغییر مقدار ثابت عددی باید در تمام برنامه به دنبال آن ثابت گشته و آن را تغییر دهیم ولی برای تغییر مقدار متغییرهای غیر قابل تغییر تنها فرمان اعلان آنها را تغییر می دهیم.
فصل چهار : کنترل جریان
زبان برنامه نویسی c دو سبک برای تصمیم گیری فراهم نموده است : حلقهها (looping) انشعابها(Branching). انشعاب یعنی تصمیم گیری در مورد اینکه چه عملیاتی انجام شود و حلقه یعنی اینکه یک عملیات خاص چند مرتبه تکرار شود.
4.1 انشعاب
وجه تسمیه انشعاب بهاین دلیل است که برنامه در مواجه با دو انشعاب در کد خود انتخاب می کند که کدام یک را ادامه دهد. دستور if یکی از ساده ترین ساختارهای انشعاب است. این دستور از یک شرط (expression)که داخل پرانتز قرار گرفته و نیز یک دستور(statement) و یا بلوکی از دستورات(block of statements) که توسط دو آکولاد باز و بسته یعنی { و } محاصره شده اند تشکیل شده است.در صورتیکه شرط صحیح باشد ( ارزشی غیر صفر داشته باشد) در آنصورت دستور و یا بلوک دستورات اجرا خواهند شد و در غیر این صورت دستورات نادیده گرفته خواهند شد. دستور if دارای یکی از دو شکل کلی زیر است:
فرم کلی اول با یک دستور
if (expression)
statement;
فرم کلی دوم با بلوک دستورات
if (expression)
{
statement1;
statement2;
statement3;
}
در زیر نمونهی ساده از کاربرد دستور if را مشاهده میکنید:
نمونه 4.1 using_if.c
#include
int
main()
{
int cows = 6;
if (cows > 1)
printf("We have cows\n");
if (cows > 10)
printf("loads of them!\n");
return 0;
}
کامپایل – اجرا و خروجی برنامه بالا به صورت زیر است:
ciaran@pooh:~/$ gcc -Wall -Werror -o cows using_if.c
ciaran@pooh:~/$ ./cows
We have cows
ciaran@pooh:~/$
دستور ()printf دوم در کد بالا به دلیل اشتباه بودن شرط آن ( با ارزش صفر) اجرا نمی گردد.
4.2if…else
شکل دومی از دستور if نیز وجود دارد که به شما امکان می دهد تا بلوکی از کدها را مشخص نموده تا در صورت اشتباه بودن شرط اجرا شوند. این ساختار با نام دستور if ... else شهرت دارد و به وسیله قرار دادن کلمه کلیدی reserved word)) else و یک بلوک کد دیگر در انتهای ساختار معمولی if شکل می گیرد.
پس ار تست شرط دستر if یکی از دو بلوک کد بسته به صحیح یا غیر صحیح بودن شرط اجرا خواهند شد. به نمونه زیر توجه کنید:
نمونه 4.2 cows2.c
int
main()
{
int cows = 0;
if (cows > 1)
{
printf("We have cows\n");
printf("%d cows to be precise\n", cows);
}
else
{
if (cows == 0)
printf("We have no cows at all\n");
else
printf("We have only one cow\n");
}
if (cows > 10)
printf("Maybe too many cows.\n");
return 0;
}
شما اکنون باید قادر به حدس زدن خروجی برنامه باشید:
خروجی برنامه
ciaran@pooh:~/$ ./cows2
We have no cows at all
ciaran@pooh:~/$
در نمونه بالا یک دستور if … else در درون یک دستور if … else دیگر وجو داشت. این ساختار در زبان c کاملا منطقی و پرکاربرد است.
4.3 دستور switch
این ساختار انشعاب نسبت ساختارهای قبلی قدری پیچیده تر است. اگر چه دستور switch بسیار انعطافپذیر است اما تنها برای تست کردن دادههای صحیح و کاراکتری کاربرد دارد. فرم کلی آن به صورت زیر است:
فرم کلی ساختار switch
switch (integer or character expression)
{
case constant1 : statement1;
break;
case constant2 : statement2;
break;
case constant3 : statement3;
break;
}
با اجرای دستور switch ابتدا عبارت داخل پرانتز با تمام مقادیری که جلوی آنها عبارتهای case نوشته شده مقایسه شده و با هرکدام از آنها که برابر بود دستورات بعد از آن تا رسیدن به عبارت break اجرا می شود.سپس با اجرای دستور break کامپایلر از حلقه خارج می شود.
نمونه 4.3 morse.c
#include
int main ();
void morse (int);
int main ()
{
int digit;
printf ("Enter any digit in the range 0 to 9: ");
scanf ("%d", &digit);
if ((digit < 0) || (digit > 9))
{
printf ("Your number was not in the range 0 to 9.\n");
}
else
{
printf ("The Morse code of that digit is ");
morse (digit);
}
return 0;
}
void morse (int digit) /* print out Morse code */
{
switch (digit)
{
case 0 : printf ("-----");
break;
case 1 : printf (".----");
break;
case 2 : printf ("..---");
break;
case 3 : printf ("...--");
break;
case 4 : printf ("....-");
break;
case 5 : printf (".....");
break;
case 6 : printf ("-....");
break;
case 7 : printf ("--...");
break;
case 8 : printf ("---..");
break;
case 9 : printf ("----.");
}
printf ("\n\n");
}
نمونه 4.4 fs.c
#include
int main ()
{
printf ("Will you join the Free Software movement? ");
if (yes())
{
printf("Great! The price of freedom is eternal vigilance!\n\n");
}
else
{
printf("Too bad. Maybe next life...\n\n");
}
return 0;
}
int yes()
{
switch (getchar())
{
case 'y' :
case 'Y' : return 1;
default : return 0;
}
}
4.3 حلقهها
حلقهها راهی را برای تکرار فرمانها و نیز کنترل اینکه فرمانها چند مرتبه انجام شود فراهم میکنند.فرضا شما قصد دارید حروف الفبا را بر روی صفحه نمایش چاپ کنید. می توانید این کار را با یک بار فراخوانی تابع ()printf انجام دهید. این یک راه حل برای انجام این کار است اما به نظر نمی رسد که بهترین راه باشد.حال بر فرض اگر خواسته باشید اعداد یک تا هزار را در یک ستون چاپ کنید چه میکنید؟ آیا باز هم از یک تابع ()printf بسیار طولانی استفاده میکنید و یا هزار بار تابع ()printf را در کدهایتان تایپ میکنید؟!. البته می توانید راه حلهای ذکر شده را به کار برید ولی بهتر است انجام کارهای تکراری را به کامپیوتر سپرده و وقت خود را صرف پرداختن به قسمتهای جذاب تر برنامه خود کنید.
4.4 حلقه while
اساسی ترین حلقه در زبان c حلقه while است.دستور while کاری شبیه تکرار دستور if را انجام می دهد. اگر شرط دستور صحیح باشد دستورات انجام می شوند با این تفاوت که پس از اجرای دستورات شرط مجددا بررسی می شود تا در صورتیکه هنوز صحیح باشد دستورات بار دیگر انجام شوند. و این حلقه تا زمانی تکرار می شود که شرط مقدار غلط پیدا کند. در صورتیکه در همان مرتبه اول بررسی شرط مقدار ناصحیح داشته باشد دستورات به هیچ وجه اجرا نمی گردند. به عبارت دیگر درصورتیکه شرط هرگز مقدار غلط پیدا نکند حلقه به طور بی نهایت تکرار می شود.
برای کنترل تعداد دفعاتی که یک حلقه کدهایش را اجرا می کند شما حداقل یک متغییر در جمله شرط دارید که در بلوک کد متعاقب تغییر می یابد. این موضوع به جمله شرط امکان می دهد در برخی مراحل نادرست گردد.
نمونهی را که پیش رو دارید یک بازی ساده حدس زدن عدد است:
نمونه 4.5 guess_my_number.c
#include
int
main()
{
const int MAGIC_NUMBER = 6;
int guessed_number;
printf("Try to guess what number I'm thinking of\n");
printf("HINT: It's a number between 1 and 10\n");
printf("enter your guess: ");
scanf("%d", &guessed_number);
while (guessed_number != MAGIC_NUMBER);
{
printf("enter your guess: ");
scanf("%d", &guessed_number);
}
printf("you win.\n")
return 0;
}
بلوک کد حلقه while در نمونه بالا تا زمانی که بازیکن عدد 6 را حدس بزند مرتب تکرار می شود.
4.5 حلقه for
معمولا در مواردیکه تعداد دفعات تکرار حلقه از قبل معیین است از حلقه for استفاده می کنیم. در ساختار حلقه for متغییری وجود دارد که تعداد دفعات تکرار حلقه را تعیین می کند و اصطلاحا شمارنده نامیده می شود. در پرانتز بعد از کلمه for سه عبارت وجود دارند که توسط سمی کالون (;) از یکدیگر جدا شده اند. اولین عبارت مقدار اولیه متغییر شمارنده را مشخص می کند. آخرین عبارت معیین می کند که با هر بار اجرای دستورات حلقه مقدار متغییر شمارنده به چه میزان اضافه یا کم می شود و بالاخره عبارت وسط که شرط حلقه است مشخص می کند حلقه تا چه زمانی باید اجرا شود.
فرم کلی حلقه if به صورت زیر است:
for (initialization; expression; action )
{
statement1;
statement2;
statement3;
}
در نمونه زیر متغییر i متغییر شمارنده حلقه for می باشد:
نمونه 4.6 for_ten.c
#include
int
main()
{
int i;
/* display the numbers from 0 to 9 */
for (i = 0; i < 10; i++)
printf("%d\n", i);
return 0;
}
4.6 حلقه do… while
حلقه do … while شبیه حلقه while عمل می کند با این تفاوت که شرط حلقه به جای ابتدا در انتهای حلقه بررسی می شود. پس حتی اگر شرط حلقه مقدار نادرست داشته باشد دستورات حلقه حداقل یک بار اجرا می شوند. در ذیل نمونهی از کاربرد این حلقه آمده است:
نمونه 4.7 guess_my_number.c
#include
int
main()
{
const int MAGIC_NUMBER = 6;
int guessed_number;
printf("Try to guess what number I'm thinking of\n");
printf("HINT: It's a number between 1 and 10\n");
do
{
printf("enter your guess: ");
scanf("%d", &guessed_number);
}
while (guessed_number != MAGIC_NUMBER);
printf("you win.\n")
return 0;
}
4.7 عملگر شرطی
عملگر ?: شبیه دستور if … else عمل می کند با این تفاوت که چون یک عملگر است ( و نه یک دستور )در میان عبارتها نیز کار برد دارد.
نمونه 4.8 apples.c
#include
int
main()
{
apples = 6;
printf("I have %d apple%s ", apples, (apples == 1) ? "" : "s");
return 0;
}
?: تنها عملگر سه گانه در زبان c است.در نمونه بالا عبارت (apples == 1) شرط ماست که درستی آن بررسی می شود. در صورتی که شرط صحیح باشد ( که در نمونه بالا نیست ) در خروجی کلمه apple بدون s و در صورت اشتباه بودن شرط کلمه apple همراه با s چاپ خواهد شد.
4.9 break و continue
زبان c راه بسیار سادهای برای خارج شدن از حلقههای تکرار در مواقع لزوم فراهم میکند. برای این کار باید از دستور break که پیش از این به منظور بیرون جهیدن از ساختار switch از آن بهره برده بودیم استفاده کرد.
نمونه زیر اعداد یک تا 12 را چاپ می کند:
نمونه 4.9 break.c
#include
int
main()
{
for (i = 1; i <= 20; i++)
{
if (i == 12)
{
break;
}
printf ( “%d “ ,i);
}
return 0;
}
به کار بردن دستور continue در یک حلقه تکرار باعث می شود تا دستورات بعدی نادیده گرفته شده و مفسر به ابتدای حلقه باز گردد. نمونه زیر حاصل تقسیم عدد بیست را بر اعداد محدوده منفی ده تا مثبت ده چاپ می کند اما برای جلوگیری از تقسیم شدن عدد بیست بر عدد صفر از یک شرط و دستور continue استفاده شده است.
نمونه 4.10 continue.c
#include
int
main()
{
for (i = -10; i <= 10; i++)
{
if (i == 0)
{
continue;
}
printf ("%d", 20/i);
}
return 0;
}
فصل پنجم : اشاره گرها
5.1 اصول اولیه
محدودیتی که ممکن است شما نیز متوجه آن شده باشید این است که توابع تنها از طریق مقادیر بازگشتی خود در برنامه شما تاثیر گذراند. با این وصف اگر بخواهید تا یک تابع با بیش از یک متغییر سر و کار داشته باشد چه خواهید کرد؟ پاسخ استفاده از اشاره گرهاست.
یک اشاره گر نوع خاصی از متغییرهاست که برای نگهداری آدرسهای حافظه کاربرد دارد یعنی یک اشاره گر آدرس متغییر دیگری را به عنوان مقدار خود نگهداری میکند. اعلان اشاره گرها به مانند اعلان متغییرهای عادی است با این تفاوت که شما باید یک کاراکتر ستاره “*” را به ابتدای نام اشاره گر اضافه کنید. دو عملگر جدید برای کار با اشاره گرها وجود دارد که شما باید آنها را بشناسید:عملگر & و عملگر * که هر دو جزء عملگرهای یگانی پیشوندی محسوب می شوند .
زمانی که شما یک علامت آمپرسند "&" را در آغاز نام یک متغییر اضافه می کنید شما آدرس آن متغییر را دریافت خواهید کرد که می تواند در یک اشاره گر نگهداری شود. اما زمانیکه علامت ستاره را در آغاز نام یک اشاره گر به کار می برید شما مقدار نگهداری شده در آدرس حافظهای را که اشاره گر به آن اشاره می کند دریافت خواهید کرد. مثل همیشه بحث را با یک نمونه دنبال می کنیم :
نمونه 5.1 pointers_are_simple.c
#include
int
main()
{
int my_variable = 6, other_variable = 10;
int *my_pointer;
printf("the address of my_variable is : %p\n", &my_variable);
printf("the address of other_variable is : %p\n", &other_variable);
my_pointer = &my_variable;
printf("\nafter \"my_pointer = &my_variable\":\n");
printf("\tthe value of my_pointer is %p\n", my_pointer);
printf("\tthe value at that address is %d\n", *my_pointer);
my_pointer = &other_variable;
printf("\nafter \"my_pointer = &other_variable\":\n");
printf("\tthe value of my_pointer is %p\n", my_pointer);
printf("\tthe value at that address is %d\n", *my_pointer);
return 0;
}
خروجی برنامه آدرس حافظه دو متغییر را به شما نشان می دهد. مقادیر در رایانه من با رایانه شما متفاوت خواهند بود. در تابع ()printf همانطور که شما هم متوجه شدهاید از %p برای نمایش آدرسها بهره بردیم که شاخص تبدیل برای تمام اشاره گرهاست. به هر حال خروجی برنامه در رایانه من به صورت زیر بود :
خروجی برنامه
the address of my_variable is : 0xbffffa18
the address of other_variable is : 0xbffffa14
after "my_pointer = &my_variable":
the value of my_pointer is 0xbffffa18
the value at that address is 6
after "my_pointer = &other_variable":
the value of my_pointer is 0xbffffa14
the value at that address is 10
5.2 آدرس یک متغییر
برنامهها در زمان اجرا ، وقتی به اعلان یک متغییر می رسند از سیستم عامل درخواست تخصیص مقداری حافظه برای آن متغییر را می نمایند. سیتم عامل قطعهای از حافظه را که اندازه آن برای نگهداری مقادیر متغییر مناسب است انتخاب و آدرس آن را به برنامه اعلام میکند. هر زمان که برنامه قصد خواندن دادههایی را که درون آن متغییر نگهداری می شوند داشته باشد ، به آدرس حافظه آن متغییر مراجعه کرده و به برابر با اندازه آن متغییر بایتهای حافظه را می خواند.
در صورتی که شما یک بار دیگر برنامهای را که در اغاز این فصل ذکر کردیم اجرا کنید ممکن است نتایج مشابه یا متفاوتی را نسبت به بار اول در مورد آدرسهای حافظه بدست آورید کهاین امر بستگی به شرایط سیستم شما دارد اما حتی اگر در بار دوم نتایج یکسانی را نسبت به یار اول بدست آورید هیچ تضمینی برای اینکه اجرای برنامه در فردا نیز همین نتایج را داشته باشد وجود نخواهد داشت. در واقع این احتمال که فردا نیز برنامه همین نتایج را داشته باشد تقریبا محال است.
5.3 اشاره گرها در جایگاه آرگومان (نشانوند )های تابع
یکی از بهترین مزیتهای اشاره گرها این است که آنها به توابع این امکان را می دهند تا متغییرهایی در خارج از حوزه خود را تغییر دهند. با ارسال یک اشاره گر برای یک تابع ، شما به آن تابع امکان خواندن و نوشتن بر روی دادههای نگهداری شده در متغییر مربوط به آن اشاره گر را می دهید.
مثلا شما قصد نوشتن تابعی را دارید که مقادیر دو متغییر را جا به جا میکند. بدون بهره گیری از اشاره گرها نوشتن این برنامه غیر ممکن است. در زیر کدهای برنامه مورد نظر را می بینید:
Example 5-2. swap_ints.c
#include
int swap_ints(int *first_number, int *second_number);
int
main()
{
int a = 4, b = 7;
printf("pre-swap values are: a == %d, b == %d\n", a, b)
swap_ints(&a, &b);
printf("post-swap values are: a == %d, b == %d\n", a, b)
return 0;
}
int
swap_ints(int *first_number, int *second_number)
{
int temp;
/* temp = "what is pointed to by" first_number; etc... */
temp = *first_number;
*first_number = *second_number;
*second_number = temp;
return 0;
}
همانطور که مشاهده می کنید اعلان تابع () swap_ints به کامپایلر gcc می گوید که انتظار دو اشاره گر ( آدرس متغییر )را داشته باشد. همچنین عملگر & به منظور ارسال آدرس دو متغییر به جای مقدار آنها به کار رفته است.
موفق باشید
[قسمت اول]
|