فصل 4
عبارت، دستور، عملگر
هدف کلی
آشنایی با عبارتها، دستورهای ساده و ساختیافته، و عملگرهای مختلف
هدفهای رفتاری
انتظار میرود پس از خواندن این فصل دانشجو بتواند،
1. مفهوم عبارت را تعریف کند.
2. عبارتهای محاسباتی، قیاسی، و منطقی را بشناسد.
3. مفهوم دستور را تعریف کند.
4. انواع دستورهای ساده را بشناسد.
5. انواع دستورهای ساختیافته را بشناسد.
6. مفهوم عملگر را تعریف کند.
7. عملگرهای محاسباتی را بشناسد.
8. عملگرهای انتساب را بشناسد.
9. عملگرهای یکانی را بشناسد.
10. چند عملگر تکعملوندی را نام ببرد.
11. عملگرهای رابطهای (مقایسهای) را بشناسد.
12. عملگر منطقی را بشناسد.
13. عملگر شرطی را بشناسد.
14. عملگر کاما را بشناسد.
15. عملگرهای حافظه را بشناسد.
عبارت
در زبان برنامهنويسي، عبارت مجموعهاي معنيدار از دادهها (مقادير عددي و متغيرها) است كه با استفاده از نشانهها يا عملگرهاي محاسباتي، قياسي و منطقي با يكديگر تركيب شدهاند. در زبان C عبارات را به شکل زير دستهبندي میکنند.
عبارت محاسباتي
تركيبي از مقادير ثابت، متغيرهاي صحيح و اعشاري با استفاده از مجموعه عملگرهاي محاسباتي است كه با قاعدة خاصي تشكيل ميشود، مانند مثالهاي زير.
1. a * x + b
2. (a + b) / c
در مثال اول مقدار a در مقدار x ضرب و نتيجه با مقدار b جمع ميشود.
در مثال دوم ابتدا مقدار b با مقدار a جمع و سپس بر مقدار c تقسيم ميشود.
مثال 4ـ1 جدول 4ـ1 نمونههايي از عبارات محاسباتي را نشان میدهد.
جدول 4ـ1 چند عبارت محاسباتی

عبارات قياسي
عبارات قياسي تركيبي است از عبارات محاسباتي با استفاده از عملگرهاي قياسي و با رعايت قوانين مربوط به نحوة به كار بردن عملگرها. نتيجة حاصل از اجراي عبارت قياسي هميشه درست يا نادرست است؛ يعني، اگر شرط يا شرطهاي به كار رفته در عبارت قياسي برقرار باشد، نتيجة عبارت مزبور درست است وگرنه نادرست میشود، كه در اغلب زبانها (مانند پاسكال) آنها را به ترتيب true يا false نامند. ولي، در زبان C مقدار true برابر يك و مقدار false برابر صفر است. در عبارات قياسي حق تقدم اجرا، اول با عبارات محاسباتي است و سپس عمل مقايسة مورد نظر انجام ميگيرد.
مثال 4ـ2 نمونههايي از عبارات قياسي در جدول 4ـ2 نشان داده شده است.
جدول 4ـ2 چند عبارت قیاسی
|
معادل در زبان C |
عبارت رياضي |
| |
|
a >15 |
a > 15 | ||
|
a+ b - c <= 3.14 |
a + b - c ≤ 3.14 |
| |
|
3*a + b != 2 |
3a + b ≠ 2 |
| |
عبارت منطقي
عبارت منطقي مجموعهاي از عبارات محاسباتي و قياسي است كه در آن حداقل يك عملگر منطقي نيز به كار رفته است. معمولاً اين گونه عبارات از دو گروه قبلي پيچيدهترند. در اغلب زبانها، عبارات قياسي كه در طرفين عملگر منطقي قرار ميگيرند بايد در داخل پرانتز محصور شوند.
مثال 4ـ3 نمونههايي از عبارات منطقي در جدول 4ـ3 نشان داده شده است.
جدول 4ـ3 چند عبارت منطقی
|
معادل در زبان C |
عبارت رياضي |
|
(a<5) && (a>3) يا (a>3) && (a<5) |
5 > a > 3 |
|
(a <= 3.14) | | (a>=15) |
(a ≤ 3.14) a ≥ 15 |
دستور
دستور حكمي است كه سبب ميشود كامپيوتر عملي انجام دهد. دو گروه دستور داریم: دستورهاي ساده و دستورهاي ساختيافته.
دستورهاي ساده
دستورهاي ساده دستورهاي غيرشرطياند كه متداولترين آنها عبارتاند از:
- جايگزين كردن مقداري معين به يك متغير كه به آن دستور انتساب ميگويند.
- خواندن و نوشتن
- فراخواني تابع
- انتقال كنترل به نقطهاي از برنامه
مثال 4ـ4 نمونههايي از دستورهای ساده در زير نشان داده شده است.
A = B * C ;
scanf("%d%d ", &a , &b) ;
printf("%d%f",a , b) ;
fact(b) ;
goto a ;
دستورهاي ساختيافته
دستورهاي ساختيافته دستورهايياند كه از انواع ساختارهاي الگوريتمي ساخته شدهاند و متداولترين آنها عبارتاند از:
- دستور مركب كه شامل دو يا چند دستور متوالي است و در داخل يك زوج آكولاد محصور است. در زبان C هر دستور ساده به يك سميكولون ختم ميشود.
{
scanf("%d %d" , &a , &b) ;
s = a * b ;
p = 2 * (a+b) ;
printf("%d %d", s , p) ;
}
- دستور حلقة تكرار
for (i =0 ; i<=10 ; i++)
sum = sum + i ;
- دستور شرطي.
if (a>b)
c = a + b ;
else
c = a – b ;
عملگر
عملگر يا اپراتور نشانههايياند كه در عبارات به كار ميروند و به كمك آنها ميتوان اعمالي را روي انواع داده انجام داد. انواع عملگرها عبارتاند از: محاسباتی، انتساب، یکانی، رابطهای (مقایسهای)، منطقی، شرطی، کاما، و حافظه.
عملگرهای محاسباتی
فهرست عملگرهاي محاسباتي در جدول 4ـ4 نشان داده شده است.
جدول 4ـ4 عملگرهاي محاسباتي
|
نام عملگر |
نشانه |
شکل |
نوع عمل |
|
جمع |
+ |
a + b |
a جمع با b |
|
تفريق |
ـ |
a - b |
a منهاي b |
|
منهاي يكاني |
ـ |
-a |
منهاي a |
|
جمع يكاني |
+ |
+a |
مقدار عملوند a |
|
ضرب |
* |
a * b |
a ضرب در b |
|
تقسيم |
/ |
a / b |
a تقسيم بر b |
|
باقيماندة تقسيم |
% |
a % b |
باقيماندة تقسيم a بر b |
|
يك واحد افزايش |
++ |
a++ , ++a |
افزايش يك واحد به مقدار a |
|
يك واحد كاهش |
-- |
a-- , --a |
كاهش يك واحد از مقدار a |
چهار عملگر / , * , - , + تقريباً روي همه نوع دادههاي استاندارد موجود در زبان C به كار میرود. در صورتي که عملگر "/" روي مقادير صحيح يا كاراكتر به كار رود، جزء اعشار حذف ميشود. مثلاً مقدار 10/3برابر 3 خواهد بود يعني فقط جزء صحيح آن درنظر گرفته خواهد شد و قسمت اعشار بريده ميشود.
عملگر % باقيماندة تقسيم را به دست ميآورد و هر دو عملوند آن بايد مقدار صحيح باشد. مثلاً مقدار 10%3 برابر يك خواهد بود. يعني باقيماندة تقسيم 10 بر 3 مساوي يك است.
دو عملگر ++ و -- در ساير زبانهاي برنامهنويسي وجود ندارند. عملگر ++ يك واحد به عملوند خود اضافه ميكند و عملگر -- يك واحد از عملوند خود كم ميكند. هر دو عملگر تكعملوندياند. درواقع دو دستور+ +a ; و a+ + ;معادل اين دستورند: a = a + 1 ;.همچنين دو دستور – –a ; و a– – ;معادل اين دستورند a = a – 1 ; .
توجه داشته باشيد سرعت عمل دو عملگريکاني ++ و -- از سرعت عمل عملگر انتساب (يعني عملگر =) بالاتر است.
مثال 4ـ5 به خروجي قطعه برنامة زير توجه کنيد.
a = 5 ;
printf ("%d %d\n", a , a+ +) ;
printf ("%d", a) ;
|
خروجی برنامه | |
|
5 |
5 |
|
|
6 |
مثال 4ـ6 به خروجي قطعه برنامة زير توجه کنيد.
a = 5 ;
printf ("%d %d\n", a , + +a) ;
printf ("%d", a) ;
|
خروجي برنامه | |
|
6 |
5 |
|
|
6 |
با توجه به دو مثال بالا ملاحظه ميکنيد که از نظر کاربرد دو دستور + +a ; و a+ + ; با يکديگر تفاوت دارند؛ يعني، در مثال اول با اجراي دستور printf اولي مقدار a (كه مساوي 5 است) چاپ ميشود و سپس دوباره همان مقدار a چاپ ميگردد و پس از انجام عمل چاپ مقدار آن يك واحد افزايش مييابد. با اجراي دستور printf دوم كنترل به آغاز خط جديد انتقال مييابد، سپس مقدار a كه اكنون برابر 6 است چاپ ميشود. در مثال دوم با اجراي دستور printf اولي مقدار a (كه مساوي 5 است) چاپ و سپس دستور + +a اجرا ميشود؛ يعني، به a يك واحد افزوده ميشود بعد مقدار آن كه برابر 6 شده است چاپ ميشود. دستور printf دوم در اينجا نيز مشابه مثال اول عمل ميكند. عملكرد دو دستور --a و a-- نيز به همين روش است.
ترتيب تقدم اين گروه از عملگرها در جدول 4ـ5 نشان داده شده است.
جدول 4ـ5 ترتیب تقدم عملگرها
|
-- ++ |
بالاترين تقدم |
|
ـ |
|
|
* / % |
|
|
- + |
پايين ترين تقدم |
در مورد عملگرهاي همتقدم ترتيب تقدم از چپ به راست است. درصورت وجود پرانتز، تقدم پرانتز از تقدم همة عملگرها بالاتر است.
مثال 4ـ7 با توجه به مقادير داده شده، چند عبارت محاسباتي همراه با مقادير آنها در جدول 4ـ6 نشان داده شده است.
int a = 10 , b = 3 ;
float C = 12.5 , d = 2.0 ;
جدول 4ـ6
|
مقدار |
عبارت محاسباتي |
مقدار |
عبارت محاسباتي |
|
14.5 |
C + d |
13 |
a + b |
|
10.5 |
C - d |
7 |
a - b |
|
25.0 |
C * d |
30 |
a * b |
|
6.25 |
C / d |
3 |
a / b |
|
error |
C % d |
1 |
a % b |
مثال 4ـ8 با توجه به اعلان داده شده، چند عبارت محاسباتي همراه با مقادير آنها در جدول 4ـ7 نشان داده شده است.
char c1 = 'A' , c2 = 'E' ;
جدول 4ـ7
|
مقدار |
عبارت محاسباتي |
|
65 |
c1 |
|
134 |
c1 + c2 |
|
139 |
c1 + c2 + 5 |
|
187 |
’5’ c1 + c2 + |
در عبارات مورد نظر هر كجا c1 و c2 به كار رفته، به جاي آنها کد اسكي معرف كاراكتر مربوط به آنها به كار برده شده است؛ يعني، در مورد متغير c1 كه معرف كاراكتر A است عدد 65 (کد اسكي حرف A) و در مورد متغير c2 نيز كه معرف كاراكتر E است عدد 69 (کد اسكي حرف E) به كار برده شده است. همچنين ملاحظه ميکنيد كه عدد 5، با کاراکتر 5 که کد اسكي آن 53 است تفاوت دارد.
مثال 4ـ9 با توجه به اعلان داده شده چند عبارت محاسباتي همراه با مقادير آنها در جدول 4ـ8 نشان داده شده است.
int a = 11 , b = -3 ;
جدول 4ـ8
|
مقدار |
عبارت محاسباتي |
|
8 |
a + b |
|
14 |
a - b |
|
-33 |
a * b |
|
-3 |
a / b |
|
2 |
a % b |
در مثال بالا اگر مقدار a برابر -11 و مقدار b برابر 3 باشد، مقدار a / b باز هم برابر -3 ميشود، اما مقدار a % b برابر -2 خواهد شد. به طريق مشابه اگر a و b هر دو مقدار منفي (متناظراً -11 و -3) داشتند، مقدار a / b برابر 3 ميشد، ولي مقدار a % b باز هم برابر -2 باقي ميماند.
عملگرهاي انتساب
در زبان C علامت ’=’ به مفهوم مساوي نيست، بلكه عملگر جايگذاري يا عملگر انتساب است. اين عملگر موجب ميگردد كه مقدار عملوند سمت راست آن در محل حافظهاي كه با عملوند سمت چپ مشخص شده است قرار گيرد. براي مثال دستور K = 123 ; مقدار 123 را به متغير k اختصاص ميدهد؛ يعني، آنچه در سمت چپ علامت قرار دارد، نام يك شناسه يا متغير و آنچه در سمت راست آن قرار دارد، مقدار يا value متغير مزبور است. پس دستور بالا به مفهوم k " مساوي 123 " نیست بلکه یعنی "مقدار 123 به k اختصاص داده شود". پس بايد به تمايز بين نام متغير و مقدار متغير توجه كرد.
حال دستور متعارف K = K+1 ; را درنظر بگيريد. از نظر رياضي اين دستور مفهوم نيست. اما از ديدگاه برنامهسازي، دستور مزبور دستور جايگذاري و بدين مفهوم است كه متغيري را كه نام آن K است پيدا كنيد. سپس به مقدار آن يك واحد اضافه کنید و مقدار جديد را به متغيري كه نام آن K است (درواقع به همان متغير) اختصاص دهيد.
به طور كلي در زبان C چندين عملگر مختلف انتساب يا جايگذاري وجود دارد كه همة آنها براي تشكيل يك عبارت انتساب يا عبارت جايگذاري به كار میروند و مقدار يك عبارت را به يك شناسه يا متغير اختصاص يا نسبت ميدهند. متداولترين عملگر انتساب عملگر ’=’ است. فرم كلي دستور انتساب به صورت زير است.
identifier = expression ;
variable = expression ;
توجه داشته باشيد كه عملگر انتساب، يعني ’=’، كاملاً با عملگر مساوي كه علامت ’= =’ است فرق دارد. عملگر انتساب براي اختصاص و نسبت دادن يك مقدار به يك شناسه يا متغير به كار میرود، درحالي كه عملگر تساوي يا برابري، براي تعيين اينكه آيا دو عبارت مقدار يكسان دارند يا نه به كار ميرود. پس اين دو عملگر نميتوانند به جاي يكديگر به كار روند.
اگر دو عملوند عملگر انتساب از نظر نوع يكسان نباشند، مقدار عبارت يا عملوند طرف راست به طور خودكار به نوع شناسه يا متغير طرف چپ عملگر تغيير مييابد. بنابراين اگر نتيجة عبارت سمت راست عملگر مزبور از نوع float و عملوند سمت چپ آن از نوع int باشد، جزء اعشاري آن حذف خواهد شد.
مثال 4ـ10 با توجه به اعلان داده شده، نمونههايي از دستور انتساب در جدول 4ـ9 نشان داده شده است.
int a , b = 5 ;
جدول 4ـ9
|
مقدار |
عبارت |
مقدار |
عبارت |
|
-25 |
a = -25.9 ; |
5 |
a = 5.56 ; |
|
5 |
a = 2*b /2 ; |
2 |
a = b/2 ; |
|
4 |
a = 2*(b/2) ; |
120 |
;’x’a = |
|
-3 |
-52’0’ -’a’ a = |
48 |
;’0’ a = |
|
47 |
3/’B’-’E’ a = |
32 |
’ ’ a = |
ملاحظه ميکنید كه در عبارات بالا در مورد كاراكترها، کد اسكي آنها جايگزين ميشود. مثلاً در آخرين عبارت، به جاي E و B به ترتيب مقادير 69 و 66 قرار ميگيرد و در نتيجه
69 - 66/3 = 69-22 = 47.
همچنين در عبارت a = 2 * (b/2) ; از آنجايي که پرانتز تقدم بالاتري دارد، اول مقدار b/2 محاسبه ميشود كه نتيجة آن برابر 2 خواهد شد و سپس نتيجة حاصل در 2 ضرب ميشود و بنابراين نتيجة نهايي برابر 4 خواهد شد.
در زبان C ميتوان دستورهاي انتساب چندگانه به كار برد مانند a = b = 5 ;. شکل كلي اين گونه دستورهاي انتساب به صورت زير است.
V1 = V2 =... = Vn = expression ;
در چنين حالتي، تقدم عمل انتساب از راست به چپ است. بنابراين دستور a = b = c = 5 ; معادل
a = (b = (c = 5)) ; است.
در زبان C، ميتوان عملگر انتساب را با عملگرهاي محاسباتي تركيب كرد و عملگرهاي+ = ، -، = ، *= ، /= ، %= را به دست آورد که آنها را عملگرهاي محاسباتي انتساب نامند و در جدول 4ـ10 نشان داده شدهاند.
جدول 4ـ10 عملگرهاي محاسباتي انتساب
|
عملگر |
نام عملگر |
دستور انتساب |
معادل |
|
+ = |
انتساب جمع |
a += b ; |
a = a+b ; |
|
-= |
انتساب تفريق |
a -= b ; |
a = a-b ; |
|
*= |
انتساب ضرب |
a *= b ; |
a = a*b ; |
|
/= |
انتساب تقسيم |
a /= b ; |
a = a/b ; |
|
%= |
انتساب باقيماندة تقسيم |
a %b ; |
a = a%b ; |
مثال 4ـ11 اگر متغيرهاي a و b از نوع int باشند و مقدار آنها به ترتيب برابر 10 و 5 باشد، دستور a += b ; معادل دستور a = a + b ; است؛ يعني، a = 15.
عملگرهاي يكاني
زبان C مجموعهای عملگر دارد كه فقط روي يك عملوند عمل ميكنند و آنها را عملگرهاي يكاني يا تكعملوند نامند. اين عملگرها اغلب در جلوي عملوند خود قرار ميگيرند. متداولترين عملگر تكعملوندي علامت منفي است كه در جلوي يك مقدار ثابت عددي، يا يك متغير و يا يك عبارت قرار ميگيرد.
مثال 4ـ12 در زير نمونههايي از عملگر منهاي يکاني نشان داده شده است.
–915 – 3.1415 – 10.25 –2e –4
–sum – (x+y) –3 * (x + y)
اگر جلوي مقادير علامتي قرار نگيرد، علامت آنها مثبت در نظر گرفته ميشود، ولي به هرحال ميتوان جلوي آنها يك علامت "+" قرار داد كه در اين صورت "+" را كه فقط داراي يك عملوند است جمع يكاني نامند.
مثال 4ـ13 در زير نمونههايي از عملگر جمع يکاني نشان داده شده است.
+915 +3.1415 +10.25 +2e – 4
+sum +(x+y) +3 * (x + y)
اگر عملگر مزبور به كار نمیرفت، مقدار اقلام بالا باز هم تغيير پيدا نميكرد و به همين دليل كاربرد اين عملگر متداول نيست.
از عملگرهاي تكعملوندي ديگر عملگر "cast" است كه آن را عملگر تبديل نوع نيز گويند و در خودآزمایی 2 فصل 3 بررسي کردیم.
يكي ديگر از عملگرهاي تكعلموندي عملگر sizeof است كه بزرگي عملوند خود را برحسب بايت برميگرداند. عملوند اين عملگر معمولاً با نوع داده همراه است، مانند
int , unsignded int , long , float , unsigned
و مشابه آنها، كه در اين صورت عملگر مزبور بزرگي اين نوع ساختمان دادههاي استاندارد را برميگرداند، يا اينكه عملوند آن عبارت است كه در اين صورت بزرگي آن عبارت را برحسب بايت برميگرداند. جدول 4ـ11 نحوة كاربرد اين عملگر را نشان ميدهد.
جدول 4ـ11 کاربرد عملگر sizeof
|
نام عملگر |
نشانه |
شکل |
نوع عمل |
|
بزرگي (sizeof) |
sizeof |
sizeof (t) sizeof x |
بزرگي نوع داده t يا عبارت x را برحسب بايت برميگرداند. |
مثال 4ـ14 به دستورهای زير توجه کنید.
k1 = sizeof (char) ;
k2 = sizeof (short) ;
k3 = sizeof (float) ;
k4 = sizeof (int);
k5 = sizeof (int *) ;
k6 = sizeof (double) ;
با اجراي دستورهاي بالا مقادير k3 , k2 , k1 بهترتيب برابر 4 , 2 , 1 خواهد بود كه بهترتيب معرف طول نوع داده char و short و float است. در مورد k4 نيز اگر نوع int گونهاي از زبان C به طول 2 بايت باشد، مقدار k4 برابر 2 خواهد بود و اگر به طول 4 بايت باشد، مقدار آن 4 خواهد بود. k5 نيز بزرگي يك اشارهگر به يك داده از نوع integer است كه برحسب ماشين مورد نظر ممكن است 4 بايت و يا عدد ديگري باشد.
مثال 4ـ15 درنتيجة اجراي دو دستور زير
float a ;
printf ("%d%d", sizeof a , sizeof (float)) ;
مقادير 44 نمايش داده خواهد شد، كه 4 اول معرف طول متغير a است (كه از نوع float است) و 4 دوم معرف طول نوع داده float است. درضمن ملاحظه ميگردد كه اگر عملوند اين عملگر معرف نوع داده مانند int، float باشد، عملوند در داخل پرانتز محصور ميگردد و اگر معرف متغير (مانند a در مثال بالا) باشد، نياز نيست كه در درون زوج پرانتز قرار داده شود.
يكي ديگر از عملگرهاي متداول تكعملوندي عملگر منطقي "!" به مفهوم نقيض است كه در عملگرهاي منطقي توضيح میدهیم.
فصل چهارم
نخها، چندپردازشی متقارن و ریز هسته ها
فرآیندها و نخ ها:
فرآیند دو خصوصیت زیر را دارد:
- تملک منبع: ( فرآیند یا وظیفه )
به هر فرآیند یک آدرس مجازی برای نگهداری تصویر فرآیند می دهد.
- توزیع وقت فرآیند: ( نخ یا فرآیند سبک وزن )
فرآیند مسیر اجرای یک یا چند برنامه است و توسط سیستم عامل مورد زمان بندی و توزیع قرار می گیرد.
چند نخی:
به قابلیتی از سیستم که امکان اجرای چند نخ را حمایت می کند.
تک نخی: به رویکرد سنتی یک نخ اجرا در هر فرآیند .
مانند MS-DOS از فرآیند تک کاربره و از یک نخ حمایت می کند.
UNIX از چند کاربر ولی تنها از یک نخ حمایت می کند.
موتور زمان اجرا یjava نمونه سیستمی از یک فرآیند چند نخی است.
در سيستمهاي عامل هاي زیرازسيستم جند نخي استفاده شده است
Windows NT ; solaris ; Mach ; os/2
موارد همراه فرآیند:
فضای آدرس مجازی که تصویر فرآیند را در بر دارد.
دسترسی حفاظت شده به فرآیند.
حالت اجرای نخ.
متن ذخیره شده.
پشته اجرا.
مقداری حافظه ایستا برای متغییرهای محلی هر نخ.
دسترسی به منابع و حافظه فرآیند.
مقاسیه فرآیند تک نخی و چند نخی:
فرآیند چند نخی:
یک بلوک کنترل فرآیند،فضای آدرس فرآیند،برای هر نخ پسته جدا گانه و بلوک کنترل جدا گانه و ... وجود دارد.
تمام نخ های فرآیند ،در حالت و منابع آن فرآیند شریک هستند.
در یک فضای آدرس هستندوبه داده های یکسانی دسترسی دارند.
فرآیند تک نخی:
شامل بلوک کنترل ،فضای آدرس فرآیند،فضای آدرس فرآیند،پشته کاربر،هسته یرای مدیریت و ... است.
حالات نخ :
ایجاد (زایش):
با ایجاد یک فرآیند ،نخ هم ایجاد می شود.
مسدود شدن:
تا بروز حادثه نخ مسدود می شود.
رفع مسدود بودن:
اتفاق حادثه لازم.
پایان:
با تکمیل یک نخ ،متن ثبات و پشته هایش آزاد می شود.
همگام سازی نخ ها
تمام نخ ها یک فرآیند در فضای آدرس و در منابع دیگر شریک هستند به همین تغییر در هر یک از منابع توسط یک نخ در محیط سایرین تغییر ایجاد می کند لذا همگام سازی فرآیند ها ضروری است.
به دو دلیل از نخ جداگانه برای رسم مجدد صفحه نمایش استفاده می کنند:
1-page maker تعدادشیء هایی که در روی یک صفحه است را محدود نکند.
2-استفاده از نخ جداگانه،امکان قطع کردن رسم را در اختیار می گذارد.
نخهای سطح کاربر
کار مدیریت توسط کاربر انجام می شود.
هسته از وجود این نخ ها بی اطلاع است.
از کتابخانه ها برای مدیریت نخها استفا ده می کنند.
هر کاربردی با یک نخ شروع می شود و شروع اجرایش از همان نخ است.
هرگاه این نخ در حال اجرا باشد می توان نخ جدیدی را ایجاد کرد که به احضار کتابخانه منجر می شود.
الگوریتم زمانبندی آن ایجاد می شود تا آماده اجرا می شود.
نخهای سطح هسته
تمام کار مدیریت نخ توسط هسته انجام می شود.
کد مدیریت در ناحیه کاربرد وجود ندارد.
یک نخ واسط برنامه سازی کاربردی به نخ هسته وجود دارد.
امتیاز نخ سطح کاربر به سطح هسته:
تعویض نخ به حالت ممتاز نیاز ندارد.
کاربرد می تواند زمانبندی خاص داشته باشد
نخ های سطح کاربر می توانند روی هر سیستم عامل اجرا گردند.
اشکال نخ های سطح کاربر به سطح هسته:
اکثر فراخوانی های سیستم مسدود کننده هستند.
کاربرد چند نخی نمی تواند از امتیازات چند پردازشی استفاده کند.
رفع اشکال نخ های سطح کاربر به سطح هسته
نوشتن کاربردها به صورت فرآیندهای متعدد به جای نخ های متعدد که امتیاز اصلی نخ ها را از بین می برد.
راه حل غلبه بر نخ های مسدود کننده : استفاده از روشی به نام جلد کردن که هدف آن تبدیل فراخوانی سیستم از مسدود کننده به غیر مسدود کننده.
امتیاز نخ های سطح هسته:
(تمام کارمدیریت نخ توسط هسته انجام می شود.)
هسته می تواند به صورت هم زمان،نخ های چند گانه یک فرآند واحد را روی پردازنده های متعدد زمانبندی کند.
اگر نخی در یک فرآیند مسدود شد ،هسته می تواند نخ دیگری از آن فرآیند را بار گئاری کند.
<خود روالهای هسته نیز می تواند چند نخی باشد.
عیب اصلی نخ های سطح هسته:
انتقال کنترل از یک نخ به نخ دیگر در داخل یک فرآیند واحد نیز نیازمند تغییر به حالت هسته است.
رویکردهای ترکیبی:
ترکیبی از امکانات نخ سطح کاربر و نخ سطح هسته را فراهم می کند. مانند solaris .
ایجاد نخ به طور کامل در فضای کاربرد در داخل یک کاربرد صورت می گیرد.
به تعداد (کمتر یا مساوی) از نخ های سطح هسته نگاشته می شود.
نخ ها به موازات هم روی پردازنده های متعدد اجرا می شود.
یک فراخوان مسدود کننده کل سیستم را مسدود نمی کند.
رابط بین نخ ها و فرآیندها:
|
نخها:فرآیندها شرح مثال سیستم های عامل |
|
یک به یک هر نخ اجرا یک فرآیند unix چند به یک یک فرآیند یک فضای آدرس Windows NT
یک به چند یک نخ از یک محیط به دیگر رود ) Ra(clouds چند به چند ترکیب چند به یک ویک به چند TRIX |
روش های پیاده سازی رابط چند به چند:
تمام برنامه به صورت یک فرآیند واحد پیاده سازی شود.
از نظر مدیریت حافظه اشکالاتی دارد.
برنامه اصلی و زیر برنامه ورودی /خروجی به صورت دو فرآیند جداگانه پیاده سازی شوند. مستلزم سر بار اضافی است.
برنامه اصلی و زیر برنامه ورودی / خروجی به عنوان یک فعالیت و به صورت یک نخ پیاده سازی شود. ولی یک فضای آدرس ویکی برای زیربرنامه ورودی/خروجی ایجاد گردد. (بهترین روش است.)
معماری چند پردازشی متقارن:
FLYNN گروههای زیر را پیشنهاد کرده است:
جریان دستورالعمل واحد و داده های واحد: یک پردازنده دستورات واحدی را برای داده ها دریافت می کند.
جریان دستورالعمل چند گانه و داده های واحد: اجرای هم زمان تعدادی از دستورات پردازشی.
جریان دستورالعمل چند گانه و داده های چند گانه: اجرای هم زمان چنددستور بر روی چند داده.
جریان دستورالعمل واحد و داده های چند گانه: اجرای هم زمان چنددستور بر روی داده.
سازمان چند پردازنده متقارن:
هر یک واحد کنترل ،واحد حساب و منطق و ثباتهای خاص خود را دارند.
به یک حافظه اصلی مشترک دسترسی دارد.
پردازنده ها از طریق حافظه با یکدیگر در ارتباط هستند.
امکان تبادل مستقیم وجود دارد.
هر پردازنده از طریق یک گذرگاه مشترک به یک حافظه اصلی متصل است.
امکان دسترسی به بلوک ها مهیا است.
تغییر در یک حافظه پنهان سبب تغییر در حافظه پنهان دیگر می شود.(وابستگی حافظه پنهان)
با آن در سخت افزار بر خورد می شود.
ملاحظات طراحی سیستم عامل چند پردازنده ای:
هم زمانی فرآیند ها یا نخ ها: توسط هر یک از پردازنده ها انجام شود .
زمانبندی: توسط هر یک از پردازنده ها انجام شود .
همگام سازی:: ابزاری است برای تنظیم انحصار متقابل و تنظیم حادثه ها.
مدیریت حافظه: باید سخت افزار مناسب را بهره جوید.
قابلیت اطمینان و تحمل خرابی: در برخورد با خرابی با ید اقدام کند.
ریز هسته ها:
ریز هسته سیستم عامل کوچکی است که بنیان لازم برای گسترشهای مولفه ای را به وجود می آورد.
رویکرد ریز هسته با به کارگیری آن در سیستم عامل Mach مورد توجه عمومی قرار گرفت.
سیستم عامل windows NT نیز داردای ریز هسته است که مدعی قابلیت حمل و مولفه ای بودن است.
معماری ریز هسته:
در این سیستم های عامل یکپارچه، مجازا هر رویه ای می توانست هر رویه دیگری را فراخوانی کنند.
در سیستم عامل های لایه ای،عمل ها به صورت سلسله مراتبی طراحی شده است و تنها سطوح مجاوربا یکدیگر محاوره داشته باشند.
فلسفه ریز هسته این است که تنها اعمال کاملا اصلی سیستم عامل باید در هسته باشد.
کاربردها روی ریز هسته ساخته شده.
بخشهایی از سیستم عامل که درخارج ریز هسته قرار دارند به صورت فرآیند خدمتگذار پیاده سازی می شوند.
بر مبنای برابری و از طریق ریز هسته با هم ارتباط برقرار می کنند.
ریز هسته به عنوان یک مبادله کننده پیام عمل می کند.
مزایای ریز هسته:
واسط یکنواخت. (برای درخواستهای فرآیند)
قابلیت گسترش. (مجاز دانستن افزایش خدمات)
قابلیت انعطاف. (به قابلیت گسترش مربوط است)
قابلیت حمل. (تمام کد مربوط به ویژگی های خاص پردازنده در هسته وجود دارد)
قابلیت اطمینان.
حمایت سیستم توزیعی: (توسط سیستم عامل توزیعی)
سیستم عامل شیء گرا.
کارایی ریز هسته:
ریز هسته خیلی کوچک سبب شده که کاهش کارایی را حذف و انعطاف و فابلیت اطمینان را بهتر کند.
به دلیل اینکه ریز هسته های مختلف طیفی از اعمال و اندازه را نشان می دهند، نمی توان هیچ قاعده محکم و سریعی در مورد اعمالی که ریز هسته باید ارائه کند و ساختار پیاده سازی آن اظهار داشت.
طرح ریز هسته:
ریز هسته مستقیما به سخت افزار بستگی دارد و اعمال زیر را حمایت می کند:
1- مدیریت حافظه اولیه.
2- ارتباط بین فرآیندها.
3- ورودی / خروجی.
4- مدیریت وقفه.
سه روش زیر برای حمایت مدیریت حافظه مجازی و صفحه بندی خارجی:
اهدا: هر فرآیند می تواند تعدادی از صفحات خود را به دیگری دهد.
نگاشت: یک فرآیند می تواند هر یک از صفحات خود را به فضای دیگری بنگارد.
مطالبه: هر صفحه که قبلا استفاده شده را مطالبه کند.
ارتباط داخلی فرآیند:
پیام شکل اصلی ارتباط بین فرایندها یا نخهاست.
1-سرآمد => فرآیندهای گیرنده –فرستنده
را مشخص می کند.
پیام=>
2-بدنه => اشاره گری به بلوک داده ها
یا بعضی اطلاعات کنترلی
مدیریت ورودی و خروجی و وقفه ها:
اداره وقفه های سخت افزاری به صورت پیام و درگاه های ورودی و خروجی در فضاهای آدرس میسر می شود.
ریز هسته وقفه را تشخیص می دهد ولی آنها را اداره نمی کند.
پیامی را برای فرآیند سطح کاربر مربوط به آن وقفه تولید می کند.
مدیریت نخ و چند پر دازشی متقارن در Windows NT
ویژگیهای فرآیندهای NT عبارتند از:
فرآیند NT به صورت شیءپیاده سازی شده اند.
هر فرآیند قابل اجرا،می تواند حاوی یک نخ یا بیشتر یاشد.
هم شیء های فرآیند و هم شیء های نخ ،دارای راهکارهای پیش ساخته همگام سازی هستند.
به هر فرآیند مجموعه بلوک های حافظه مربوط است.
به هر فرآیند یک نشانه امنیتی دست یابی نسبت داده می شود.
فرآیند شامل یک جدول شیء هم هست که دستگیره های به دیگرشیءهای شناخته شده این فرایند دارد. برای هر یک از نخهای که در این شیء هست یک دستگیره وجود دارد.
اگر فرآیندی برای باز کردن چنین دستگیرهای تلاش کند، سیستم امنیتی مجاز بودن آن ودر نتیجه امکان تغییر خصوصیا تش را تعیین می کند.
فرآیند نمی تواند مستقیما ساختار ها را تغییر دهد.
فرایند شامل یک جدول شیء است .
خصوصیات شیء فرایند
|
مقدار یکتای فرآیند توصیف چه کسی به این فرآیند.... برای نخ های این فرآیند مجموعه پردازنده های پیش فرض حداکثر منابع مجموع زمان اجرا نخ متغییر و... ثبت شده توسط فرآیند تعداد و نوع عملیات انجام شده کانالهای ارتباطی بین فرایندها دلیل پایان یافتن یک فرآیند |
شناسه فرآیند توصیف امنیت اولویت پایه وابستگی فرآیند به پردازنده حداکثر سهم زمان اجرا شمارنده های I/O شمارنده عملیات حافظه مجازی درگاههای استثنا/اشکالزدایی وضعیت خروج |
خصوصیت شیء نخ :
شناسه نخ: مقدار یکتای نخ.
متن نخ: مجموع مقادیر نخ و....
اولویت پویا: اولویت اجرای نخ در هر لحظه.
اولویت پایه: کمترین اولویت نخ.
وابستگی نخ به پردازنده: پردازتده هایی که می توان در روی نخ انجام شود.
زمان اجرای نخ: زمان اجرای آن در هسته و در حالت کاربرد.
وضعیت هشدار: فراخوانی رویه نا همگام .
شماره تعلیق : تعداد دفعات تعلیق اجرای نخ.
نشانه جعل هویت: دستیابی موقت .
درگاه پایان: یک کانال ارتباطی بین فرآیندها.
وضعیت خروجی نخ: دلیل پایان یافتن نخ.
حالت های نخ:
آماده: برای اجرا زمانبندی گردیده.
جانشین: برای هر پردازنده خاص ،یک نخ جانشین
اجرا: با تعویض به مرحله اجرا می رود.
انتظار: یک نخ موقعی به حالت اجرا می رود که:
1- روی حادثه مسدود کردد.
2- به منظور همگام سازی ،داوطلبانه منتظر شود.
3- توسط یک زیر سیستم محیط وادار به تعلیق گردد.
انتقال: منابع فراهم نیست.
پایان: توسط خودش یا پدر.
مدیریت نخ و جند پردازشی متقارن در Solaris
فرآیند: همان فرآیند معمولی
نخ های سطح کاربر: به وسیله کتابخانه ای ار نخها در فضای آدرس یک فرآیند پیاده سازی می شوند.
فرآیند سبک وزن: نگاشتی بین نخ های سطح کاربر و نخ های هسته در نظر گرفت.
نخ های هسته: موجودیتهای بنیادی.
اجرای نخ:
همگام سازی: جهت هما هنگ کردن فعالیت.
تعلیق : معلق شدن و منتظر درخواست.
قبضه : عملی که موجب به اجرا در آمدن فعالیت اولویت دار شود.
واگذاری.
راه حل solaris برای اداره وقفه
برای اداره وقفه ها solaris مجموعه ای نخ های هسته را به کارگرفته است.
با استفاده از اولیه های انحصار متقابل ،هسته دسترسی به ساختمان داده ها را کنترل می کند.
اولویت وقفه ها ،از اولویت دیگر نخ های هسته بیشتر است.
فصل دوم
معماری مجموعه دستورالعملها
مراحل اجرای یک دستورالعمل
واکشی دستورالعمل: برداشت دستورالعمل بعدی از حافظه
کدبرداری از دستورالعمل: بررسی دستورالعمل برای مشخص شدن اینکه:
چه عملی باید توسط دستورالعمل انجام گیرد (به عنوان مثال جمع)
چه عملوندهایی مورد نیازند، و نتایج باید کجا قرار گیرند.
واکشی عملوندها: عملوندها برداشت می شوند.
اجرا: اجرای عملیات بر روی عملوندها
بازنویسی نتیجه: نوشتن نتیجه در محل مخصوص
دستورالعمل بعدی: تعیین اینکه دستورالعمل بعدی از کجا گرفته شود.
چه چیزی در یک ISA(معماری مجموعه دستورالعمل) مشخص می شود؟
کدبرداری از دستورالعمل: اعمال و عملوندها چگونه تعیین می گردند؟
واکشی عملوندها: عملوندها ممکن است کجا باشند؟ چه تعداد؟
اجرا: چه اعمالی می تواند انجام گیرد؟ چه نوع داده و چه اندازه هایی؟
بازنویسی نتایج: نتایج کجا نوشته می شوند؟ چه تعداد؟
دستورالعمل بعدی: دستورالعمل بعدی را چگونه می توان انتخاب نمود؟
یک ISA ساده:حافظه به حافظه
چه عملیاتی می تواند اجرا شود؟ عملیات پایه ریاضی (برای این لحظه)
چه نوع داده و چه اندازه ای؟ نوع داده صحیح 32 بیتی (integer 32)
عملوندها و نتایج کجا می توانند قرار گیرند؟ حافظه
چه تعداد عملوند و نتیجه؟ 2 عملوند، 1 نتیجه
اعمال و عملوندها چگونه مشخص می شوند؟
OP DEST,SRC1,SRC2 عمل مقصد,منبع1,منبع2
چگونه می توانیم دستورالعمل بعدی را انتخاب کنیم؟ بعدی به ترتیب
مدل حافظه
حافظه را به عنوان یک آرایه بزرگ از n عدد صحیح در نظر بگیرید، که بوسیله اندیس قابل دستیابی است.(حافظه با دستیابی تصادفی موسوم به ram)
به عنوان نمونه ، M[1] شامل مقدار 3 است. ما می توانیم در این مکانها نوشتن و خواندن را انجام دهیم. این مکانها صرفا در دسترس ماست. تمام مکانهای “مجرد” (از قبیل متغیرها در C) باید مکانهایی را در M تعیین کنند.
Address Contents
|
14 |
|
13 |
|
99 |
|
. . . |
|
0 |
0
1
2
.
.
.
N - 1
ترجمه کد ساده
کد C مفروض
A = B + C;
ما می توانیم تصمیم بگیریم که متغیر A مکان 100 ، B مکان 48 و C مکان 76 را
اشغال می کند. کد بالا را به معادل کد اسمبلی آن تبدیل می کنیم:
ADD M[100], M[48], M[76]
چگونه می توانیم عبارت زیر را تبدیل کنیم
A = (B + C) * (D + E);
استفاده از یک مکان موقتی
فرض کنید ما A را در 100، B را در 48 ، C را در 76، D را در 20 و E را
در32 قرار می دهیم.
اکنون یک مکان بدون استفاده را انتخاب می کنیم (مثلا 84)
ADD M[100], M[48], M[76] # A = B + C
ADD M[84], M[20], M[32] # temp = D + E
MUL M[100], M[100], M[84] # A = A * temp
مشکلات در معماری حافظه به حافظه
حافظه اصلی خیلی کندتر از مدارات محاسباتی است
- این مطلب از سال 1950 تا 2003 درست است!
خانه های زیادی برای مشخص نمودن آدرسهای حافظه گرفته می شوند.
معمولا نتایج یک یا دو دستورالعمل بعد مورد استفاده قرار می گیرند.
به خاطر داشته باشید: بخشهای اشتراکی را سریعتر نمائید!
راه حل: نتایج میانی یا موقتی را در حافظه های سریع و نزدیک به واحد محاسبه ذخیره نمائید.
ماشینهای مبتنی بر انباشتگر(Accumulator)
یک ماشین انباشتگر، یک بافر پرسرعت واحد (مانند یک مجموعه از D latchها یا فلیپ فلاپها ، هر کدام برای یک بیت داده) را نزدیک واحد محاسبه منطق نگهداری می کند.
در ساده ترین حالت، فقط یک عملوند می تواند مشخص گردد; انباشتگر به صورت مجازی به مفهوم “OP Operand” می باشد یعنی:
acc. = acc. OP operand
Example:
LOAD M[48] # Load B into acc.
ADD M[76] # Add C to acc. (now has B+C)
STORE M[100] # Write acc. To A
LOAD M[20] # Load D into acc.
بارگذاری D در انباشتگر
ADD M[32] # Add E to acc. (now has D+E)
جمع E با انباشتگر.(اکنون داریم D+E)
STORE M[100] # Write acc. To A
نوشتن محتوای انباشتگر در A.
LOAD M[48] # Load B into acc.
بارگذاری Bدر انباشتگر.
ADD M[76] # Add C to acc. (now has B+C)
جمع نمودن Cبا انباشتگر.(اکنون داریم B+C)
MUL M[100] # Multiply A to acc.
ضرب کردن A در انباشتگر.
STORE M[100] # Write (B+C) * (D+E) to A
نوشتن نتیجه در A
ضعف ماشینهای مبتنی بر انباشتگر
هنوز نیازمند ذخیره سازی بسیاری مقادیر موقتی و میانی در حافظه می باشیم
در واقع انباشتگر فقط برای یک ترتیب از محاسبات که در آن نتیجه یکی، ورودی برای بعدی است، مفید می باشد.
ماشینهای مبتنی بر انباشتگر هنوز در کامپیوترهای اولیه معمول بودند
یک طراحی ساده، و بنابراین محبوب ، مخصوص برای
- کامپیوترهای اولیه
- میکروپروسسرهای اولیه (4004 ، 8008)
- مدلهای Low-end (ارزان)
پیشنهادات برای ماشینهای مبتنی بر انباشتگر
اگر منابع سخت افزاری بیشتری در دسترس است، مکانهای ذخیره سازی سریع را در کنار انباشتگر قرار دهید:
ماشینهای مبتنی بر پشته
ماشینهای مبتنی بر ثبات
- خاص منظوره
- عام منظوره
ماشینهای مبتنی بر پشته
ایده: یک ستون از مکانهای ذخیره سازی سریع دارای یک بالا(top) و یک پایین (bottom)
یک دستورالعمل فقط از مقدار بالای پشته(top) قابلیت برداشت دارد، یا شاید دو یا سه مقدار به عنوان بالای پشته در نظر گرفته شوند.
ما می توانیم مقادیر جدید را در بالای پشته قرار دهیم (“push”) یا از بالای پشته برداریم (“pop”) اما فقط همین. ما نمی توانیم به مکانهایی در زیر پشته دسترسی داشته باشیم مگر اینکه هر چیز بالای آن را خارج کنیم.
Address Contents
|
14 |
|
3 |
|
99 |
|
. . . |
|
0 |
Top
2nd from top
3rd from top
.
.
.
معماری مجموعه دستورالعمل ماشینهای مبتنی بر پشته
اعمال اصلی شامل:
بارگذاری: برداشت مقدار از حافظه و قرار دادن آن بر روی پشته
ذخیره سازی: برداشت مقدار از پشته و ذخیره آن در حافظه
حسابی: خارج کردن یک یا دو مقدار از پشته؛ قرار دادن نتیجه روی پشته
دونسخه ای (Dup): گرفتن مقدار از بالای پشته بدون حذف آن؛ قرار دادن یک کپی جدید از آن در روی پشته (چرا این عمل کاربردی است؟)
ماشین مبتنی بر پشته که A=(B+C)*(D+E) را انجام می دهد
ماشین مبتنی بر پشته
کاربرد ماشینهای مبتنی برپشته
بسیاری از کامپیوترهای اولیه
واحد ممیز شناور در 8086 (تقریبا)
ماشین مجازی جاوا
ماشینهای مبتنی بر ثبات
ایده:به کار بردن مکانهای ذخیره سازی زیاد(”ثباتها“)نزدیک انباشتگر
ثبات ها نام / شماره های مشخصی دارند که می توانند به جای حافظه استفاده می شوند
دستیابی نسبت به حافظه اصلی
خیلی سریعتر است
(1-2 CPU cycles vs. ~ 100 CPU cycles on PC)_
ثبات ها نسبت به مکان های حافظه خیلی نزدیک ترند
MIPS_ 32 تا ثبات 32 بیتی دارد
_ثبات های کمتر آدرس های کوچکتر وتعداد بیتهای کمتر برای نام گذاری آنها
_منابع استفاده کمتر است و باید با دقت ار آنها استفاده شود!
ثباتهای عام منظوره_خاص منظوره
یک ثبات خاص_ منظوره برای اهداف مشخص استفاده می شود و ممکن است عملیاتی را که استفاده می شود را محدودکند
طراحی آسانتر سخت افزار: ثبات را در جایی که دقیقا نیازاست قرار بده
به منظور استفاده موثر برای کامپایلر خیلی سخت تر است.
یک ثبات عام منظوره می تواند دربیشتر مسیرهای عملیاتی استفاده شود بنابراین مسیریابی خیلی مشکل است
ثبات های خاص منظوره The z_80 cpu
هفت تا ثبات 8 بیتی: (می توانند جفت شوند A,B,C,D,E,H,LBC,DE,HL).
سه تا ثبات 16 بیتی:62 PC (شمارنده برنامه).
جمع ,تفریق, شیفت تنها با A می تواند انجام شود (شمارنده 8 بیتی ).
افزایش و کاهش می تواند با تمام ثبات ها وجفتهای ثبات انجام شود .
می توانند از حافظه آدرس (HL)را واکشی کنند و در هر8بیت ثبات قرار دهند.
یک واکشی از آدرس(BC)یا(DE)تنها می تواند به Aبرود.
واکشی ها از (BC),(HL)و (IX) تعداد چرخه های متفاوتی می گیرد
چه کسی می خواهد برای این یک کامپایلر بنویسد؟
ثبات عام منظوره ماشین های (GPR)
MIPS(وپردازشگرهای مشابه)32تا ثبات عمومی دارند (GPRs)هر32بیتlongهستند.همه می توانند نوشته یا خوانده شوند به جز ثبات صفر که همیشه صفراست ونمی تواند تغییر کند.زمان دستیابی به ثبات ثابت است.
Address Contents
|
0 |
|
3 |
|
99 |
|
. . . |
|
14 |
$0
$1
$2
ماشین GPR A=(B+C)*(D+E) راانجام می دهد
ADD $1 M[48], M[76] #R1 = B + C
ADD $2 M[20], M[32] #R2 = D + E
MUL M[100], $1, $2 #A = R1 * R2
اندازه های داده های متفاوت
چطورباید با اندازه های داده های متفاوت رفتارکنیم؟
انتخاب یک اندازه برای یک واحد ذخیره شده در یک آدرس تنها
ذخیره کردن داده بزرگ در یک مجموعه از مکانهای همجوارحافظه
ذخیره کردن داده کوچک در یک مکان:
use shift & mask ops
امروزه تقریبا همه ماشینها(شاملMIPS)دارای آدرس دهی بایتی
“Byte_Adressable”هستند هر مکان آدرس دهی ، در حافظه 8بیتی نگهداری می شود.
حافظهMIPS
روی یک ماشین قابل آدرس دهی بایتی ازقبیلMIPS اگرما بگوییم یک کلمه (32بیت) درآدرس80 ذخیره شده به این معنااست که مکانهای 80تا83را اشغال کرده.(کلمه بعدی از84 شروع می شود).
به طور نرمال بارگذاری وذخیره چندین بیت باید ”تنظیم“ شود.آدرس nبایتی بارگذاری یا ذخیره باید مضربی ازnباشد.برای نمونه نیم کلمه تنها در آدرسهای زوج ذخیره میشود.
MIPS اجازه بارگذاری شدن و ذخیره شدن برای استفاده مخصوص دستورالعمل ها را نمی دهند اما آنها ممکن است کندترشوند(بیشتر پردازشگرها این را برای همیشه اجازه نمی دهند!).
Byte-Order (“Endianness”)
برای یک داده چند بایتی کدام قسمت به کدام بایت می رود؟
اگر$1محتوی1000000(F4240H )و ما آن را در آدرس 80 ذخیره کنیم :
در یک ماشین “Big Endian” “Big End” به آدرس 80 می رود
در یک ماشین “Little Endian” از سوی دیگر است.
Big-Endian vs. Little-Endian
ماشینهای Big Endian:MIPS,SPARC,68000
ماشینهای :Little Endianبیشتر پردازشگر های intel,Alpha,Vax
سازگاری مشکلات انتقال چندین بایت داده بین ماشینهای Big Endian,Little Endian
فصل چهارم
دستورالعمل هاي اساسي
انتقال داده ها بين مكانهاي مختلف حافظه
كارايي زمان و فضا (حافظه ) دو مفهوم اساسي اين فصل مي با شند .
دستورالعملmov در زبان اسمبلي براي كپي كردن داده ها از محلي به محل ديگر مورد استفاده قرار مي گيرد.
دستورالعمل xchg ، دستور مفيد ديگري است كه درزبان اسمبلي براي تعويض داده هاي منبع ومقصد كاربرد دارد.
نكته : مي توان اندازه ي داده ي مورد نظر را از يك كلمه به يك بايت و يا بالعكس تغيير داد.
....
• ادامه مبحث سازماندهي فايلها براي
کارايي
• شاخص گذاري
براي
بازيابي سريعتر فضا به وارد زير نيازمنديم :
۱)
راهي که بلافاصله بدانيم که حفره هاي خالي در فايل وجود دارد يا نه
۲)
راهي که اگر چنين حفره اي وجود دارد مستقيماً به آن پرش کنيم.
استفاده
از ليست هاي پيوندي براي پيوند دادن تمام رکوردها هر دو نياز فوق را
برآورده مي کند.
آسان
ترين راه براي کار کردن با ليست استفاده از آن به صورت پشته است.
پشته ليستي است که در آن اضافه و حذف گره
ها از يک انتهاي ليست انجام مي شود.
براي
بازيابي رکوردها از طريق ليست پيوندي به موارد زير نياز داريم :
۱) راهي براي پيوند دادن رکوردهاي حذف شده و
تبديل آنها به يک ليست
۲) الگوريتمي براي اضافه کردن رکوردهاي حذف شده
به ليست
۳) الگوريتمي براي پيدا کردن و خارج کردن يک
رکورد از ليست هنگامي که مي خواهيم از آن رکورد استفاده کنيم.
براي مقابله با پراکندگي خارجي يک روش متراکم
کردن فايل است و دو راه ديگر به قرار زير است:
۱) اگر دو حفره رکورد در ليست به صورت فيزيکي کنار هم قرار
گيرند آنها را با هم يکي مي کنيم تا يک حفره رکورد بزرگتر ايجاد شود. به اين کار ادغام
حفره ها در فايل ميگوييم.
۲) سعي مي کنيم پراکندگي را به حداقل برسانيم.
به اين ترتيب که يک راهبرد انتخاب جا را در نظر مي گيريم که برنامه با استفاده از
آن يک حفره رکورد را از ليست انتخاب کند.
هنگامي که نياز داريم يک حفره رکورد را
از ليست خارج کنيم با شروع از ابتداي فايل عمل جستجو را انجام مي دهيم تا رکوردي
به اندازه کافي بزرگ پيدا کنيم يا به انتهاي ليست برسيم. اين راهبرد انتخاب جا به عنوان
راهبرد اولين جاي مناسب( first fit)
ناميده مي شود.
سه مشکل اساسي مربوط به مرتب سازي و جستجوي
دودويي عبارتند از :
۱) جستجوي دودويي نياز به بيش از يک يا دو
دسترسي به ديسک دارد.
۲) نگهداري يک فايل به صورت مرتب شده خيلي گران
تمام مي شود.
۳) مرتب سازي داخلي تنها در مورد فايل هاي کوچک
عملي است.
مرتب
سازي کليدي که گاهي به آن مرتب سازي با برچسب مي گويند بر اين ايده
استوار است که وقتي فايلي را در حافظه مرتب مي کنيم تنها چيزيکه واقعاً به آن نياز
داريم کليد رکوردها است.
عيب مرتب
سازي کليدي اين است که مرتب کردن فايلي با n
رکورد نياز به n دستيابي تصادفي به فايل اصلي دارد
که مي تواند بسيار بيشتر از خواندن ترتيبي همان تعداد رکورد وقت بگيرد.
شاخص گذاري
همه شاخص ها بر اساس يک مفهوم اصلي واحد عمل مي
کنند: کليدها و آدرس فيلدها.
انواع شاخص هايي که در اين فصل بررسي مي کنيم شاخص
ساده ناميده مي شوند زيرا با استفاده از آرايه هاي ساده اي از ساختمان ها نشان
داده مي شوند ،که حاوي کليدها و آدرس فيلدها هستند.
چون شاخص ها به طور غير مستقيم عمل مي کنند ،
بدون دستکاري محتويات فايل ،به فايل نظم و ترتيب مي بخشند.
کاتالوگ کارتي در واقع مجموعه اي از سه
شاخص است که هر کدام از يک فيلد کليد متفاوت استفاده مي کنند و همه انها از
يک شماره کاتالوگ يکسان به عنوان فيلد آدرس بهره مي گيرند.
بنابراين کاربرد ديگر شاخص بندي اين است که مي
توان از طريق مسيرهاي گوناگوني به فايل دست يافت.
در جستجوي دودويي لازم است امکان پرش به وسط فايل را داشته
باشيم.
راه
ديگر براي مرتب سازي ، ايجاد شاخص براي فايل است.
ساختار
شيء شاخص بسيار ساده است.
اين
ساختار ليستي است که هر عنصر آن دو فيلد دارد:
يک فيلد کليد و يک فيلد براي آفست بايت.
عملياتي
که براي يافتن داده هاي مورد نظر ،از طريق شاخص لازمند عبارتند از :
۱) ايجاد فايل داده ها و شاخص خالي اوليه
۲) باز کزدن فايل شاخص در حافظه ،قبل از به
کارگيري آن
۳) نوشتن فايل شاخص بر روي ديسک ،پس از به
کارگيري آن
۴) افزودن رکوردهايي به فايل و داده ها
۵) حذف رکوردها از فايل داده ها
۶) بهنگام کردن رکوردها در فايل داده ها
۷) بهنگام کردن شاخص براي انعکاس تغييرات به
عمل آمده در فايل داده ها.
مزيت بزرگي که روش شيء گرا دارد آن است
که براي اجراي اين عمليات به هرچه نياز داشته باشيم مي توانيم در متدهاي کلاس خود
بيابيم.
در
ايجاد فايل ها بايد دو فايل ايجاد شوند :
۱) فايل داده ها براي نگهداري اشياي داده اي
۲) فايل شاخص براي نگهداري شاخص کليد اوليه
بهنگام
سازي رکوردها به دو صورت انجام مي شود :
۱) بهنگام سازي ،تعداد فيلد و کليد را تغيير مي
دهد.
۲) بهنگام سازي ،در فيلد و کليد تأثير نمي
گذارد.
آشکارترين
بهينه سازي ،استفاده از جستجوي دودويي در متد find است که توسط :
insert , search و remove به کار گرفته مي شود.
منبع ديگر بهينه سازي ،چنانچه رکورد شاخص
تغيير نکرده باشد ، نوشتن درباره رکورد شاخص در فايل شاخص است.
دستيابي
به شاخص روي ديسک داراي معايب زير است :
۱) جستجوي دودويي شاخص به جاي آنکه با سرعت
حافظه صورت پذيرد ،نياز به چندين پيگرد دارد.
۲) ترتيب مجدد شاخص که از حذف يا افزودن رکورد
ناشي مي شود نياز به جابه جا کردن يا مرتب سازي رکوردها در حافظه ثانويه
دارد که اين کار ميليونها بار گران تر از اجراي اين عمليات در حافظه است.
هرگاه
يک شاخص ساده در حافظه جا نشود بايد از موارد زير استفاده کرد :
۱) در صورتي که سرعت دستيابي در اولويت قرار
داشته باشد ،از سازماندهي درهمسازي استفاده شود.
۲) در صورتي که به هر دو نوع دستيابي کليدي و
ترتيبي نياز داشته باشيد ،از يک شاخص چند سطحي با ساختار درختي نظير
درخت B استفاده شود.
شاخص هاي ساده نسبت به استفاده از فايل داده
اي که بر حسب کليد مرتب شده اند مزاياي چشمگيري دارد :
۱) شاخص ساده استفاده از جستجوي دودويي را براي
دستيابي کليدي به يک رکورد در فايلي که طول رکوردهاي آن متغير است امکان پذير مي
سازد.
۲) اگر ورودي هاي شاخص بسيار کوچکتر از
رکوردهاي فايل داده ها باشد ،مرتب سازي و نگهداري شاخص نسبت به مرتب سازي و
نگهداري فايل داده ها زمان کمتري مي برد.
۳) اگر در فايل داده ها رکوردهايي وجود دارند
که در جاي خود مستقر هستند ،با استفاده از شاخص مي توان ترتيب کليدها را بدون
جابجايي رکوردهاي داده ها عوض کرد.
هنگاميکه شاخص ثانويه اي موجود باشد
،افزودن يک رکورد به فايل به معناي افزودن يک ورودي شاخص ثانويه است. زمان لازم
برا انجام اين کار بسيار مشابه زمان لازم براي افزودن ورود يي به شاخص اوليه است.
يک اختلاف مهم شاخص ثانويه و شاخص اوليه آن
است که شاخص ثانويه مي تواند حاوي کليدهاي دوگانه باشد.
حذف يک رکورد معمولاً به معناي حذف تمامي آدرس
هاي آن رکورد در سيستم فايل است.
بنابراين حذف رکوردي از فايل داده ها نه تنها
به معناي حذف ورودي مربوط در شاخص اوليه بلکه به معناي حذف همه ورودي هاي
موجود در همه شاخص هاي ثانويه اي است که به اين ورودي از شاخص
اوليه رجوع مي کنند.
مشکل اين است که شاخص هاي ثانويه همانند شاخص
اوليه به ترتيب کليدها نگهداري مي شوند. در نتيجه حذف يک ورودي شامل ترتيب مجدد
ورودي هاي موجود ،به منظور بستن فضاي باقيمانده از حذف است.
بهنگام سا زي فايل داده ها فقط هنگامي شاخص
ثانويه را تحت تأثير قرار مي دهد که کليد اوليه يا ثانويه تغيير يابند. که سه
وضعيت ممکن است پيش بيايد :
۱) بهنگام سازي باعث تغيير کليد ثانويه مي شود.
۲) بهنگام سازي باعث تغيير کليد اوليه مي شود.
۳) بهنگام سازي محدود به فيلدهاي ديگر
ساختارهاي
شاخص ثانويه اي که تا کنون ارائه کرديم دو مشکل دارند :
۱)
هربارکه رکورد جديدي به فايل افزوده مي شود ،بايد فايل شاخص را دوباره مرتب کنيم
،حتي اگر رکورد جديد به يک کليد ثانويه موجود مربوط باشد.
۲) اگر
کليدهاي ثانويه وجود داشته باشد ،فيلد کليد ثانويه براي هر ورودي تکرار مي شود.
اين کار باعث هدر رفتن فضا مي شود.
درسيستم فايلي که طي اين فصل طراحي کرديم ، انقياد
کليدهاي اوليه به آدرس در زمان ايجاد شدن فايل ها رخ مي دهد ولي کليدهاي
ثانويه در زمان استفاده ،به آدرس خود پيوند مي يابند.
پردازش کمک ترتيبي و مرتب سازي فايل هاي بزرگ
عمليات کمک ترتيبي شامل پردازش هماهنگ دو يا
چند ليست ترتيبي براي ايجاد يک ليست خروجي است.
گاهي پردازش منجر به ادغام يا اتحاد
اقلام موجود در ليست هاي خروجي مي شود ،گاهي هدف ،تطابق يا جايگذاري اقلام در ليست
ها است ،گاهي نيز عمليات شامل ترکيبي از تطابق و ادغام مي شود.
اين نوع عمليات روي ليست هاي ترتيبي ،مبناي
بسياري از پردازش هاي فايل ها را تشکيل مي دهند.
گرچه روال همخواني بسيار ساده به نظر مي رسد
،براي آن که اين روال بهتر عمل کند به چند نکته بايد توجه داشت :
۱) آماده سازي
۲) دستيابي به عضو بعدي ليست
۳) همزمان سازي
۴) کنترل شرايط پايان فايل
۵) تشخيص خطاها
اگر قرار باشد تعداد زيادي از ليست ها با هم
ادغام شوند ،مي توان به جاي حلقه مقايسه ها از درخت انتخاب استفاده
کرد.
مرتب
سازي در حافظه شامل سه مرحله است :
۱) خواندن کل فايل از روي ديسک به حافظه
۲) مرتب سازي رکوردها با استفاده از يک روال
مرتب سازي استاندارد ،مثل مرتب سازي shell
۳) نوشتن دوباره فايل روي ديسک
آيا يک الگوريتم مرتب سازي داخلي وجود دارد که
به قدر کافي سريع باشد و بتواند مرتب سازي اعداد را بلافاصله پس از خوانده شدن
آنها آغاز کند و منتظر قرار گرفتن کل فايل در حافظه نشود؟
بله ،نام آن مرتب سازي هرمي (heapsort) است و مبتني بر همان اصل درخت انتخاب است.
هرم
درختي دودويي با ويژگي هاي زير است :
۱) هر
گره داراي کليدي است که آن کليد بزگتر يا مساوي کليد واقع در گره پدرش است.
۲) يک
درخت دودويي کامل است.
۳) به
خاطر ويژگيهاي ۱ و ۲ ،در نگهداري درخت مي توان آرايه اي اختصاص داد که در آن ،گره ريشه ،انديس ۱ و انديسهاي فرزندان چپ و راست
گره i ،به ترتيب برابر با
i۲ و ۱ + i۲ باشند.

الگوريتم مرتب سازي هرمي دو بخش دارد:
ابتدا هرم را ايجاد مي کنيم سپس کليدها را به
صورت مرتب شده در خروجي قرار مي دهيم.
بازيابي
ترتيبي کليدها به صورت زير انجام مي شود :
۱) تعيين مقدار کليد موجود در اولين موقعيت هرم
. اين مقدار کوچکترين مقدار هرم است.
۲) انتقال بزرگترين مقدار هرم به اولين محل آن
و کم کردن يک واحد از تعداد عناصر.
۳) ترتيب دوباره هرم. با اينکار بزرگترين عنصر با فرزند
کوچکش جابجا مي شود.
هر بار
که اين سه مرحله اجرا مي شود ،کوچکتري مقدار بازيابي شده از هرم حذف مي گردد.
مرتب
سازي کليدي دو نارسايي دارد :
۱) هنگاميکه کليدها مرتب سازيمي شوند ،بايد
زمان زيادي صرف اين موارد شود. پيگرد هر رکورد در رکوردهاي مرتب شده ،خواندن هر
رکورد به حافظه و نوشتن آن روي فايل مرتب شده جديد شود.
۲) در مرتب سازي کليدي ،اندازه فايلي که
قابل مرتب سازي است به تعداد جفت کليد/اشاره گري که در حافظه جا شود ،محدود مي
شود. در نتيجه هنوز نمي توانيم فايل هاي واقعاً بزرگ را مرتب سازي کنيم.
رانش
داراي ويژگي هاي زير است :
۱) واقعاً قادر به مرتب سازي فايل هاي بزرگ هست
و به فايل هايي به هر اندازه قابل بسط است.
۲) خواندن فايل ورودي در مرحله ايجاد
رانش ،ترتيبي است و لذا بسيار سرعتر از ورودي است ،زيرا ورودي به ازاي هر رکورد
نياز به پيگرد دارد.
۳) خواندن هر رانش طي مرحله ادغام و نوشتن رکوردهاي مرتب شده نيز ترتيبي
است.
۴) اگر براي بخشي از ادغام که در حافظه انجام
مي شود از مرتب سازي هرمي استفاده شود مي توانيم اين عمليات را با I/O همپوشاني کنيم تا زمان ادغام افزايش پيدا نکند.
۵) چون I/O
تا حد زيادي ترتيبي است ،در صورت نياز مي توان براي هر دو عمليات ورودي و خروجي از
نوار نيز استفاده کرد.
I/O چهار بار اجرا مي
گردد.
در
مرحله مرتب سازي :
۱) خواندن همه رکوردها به حافظه براي مرتب سازي و تشکيل
رانش ها
۲) نوشتن رانش هاي مرتب شده روي ديسک.
در
مرحله ادغام :
۱) خواندن رانش هاي مرتب شده به حافظه براي
ادغام
۲) نوشتن فايل مرتب شده روي ديسک
جلسه چهارم
طرح اوليه متن اصلي
فراروند
ایجاد متن نوعی تولید و خلاقیت است . از این نقطه نظر نیاز به طراحی دارد یعنی
باید یک طرح اولیه برای متنی که می خواهیم تهیه نماییم . در مرحله تهیه طرح اولیه
کارهای زیر را انجام می دهیم :
فصل چهارم:
روش حریصانه در طراحی الگوریتم
الگوریتم حریصانه ، به ترتیب عناصر را گرفته ، هر بار آن عنصری را که طبق ملاکی معین ”بهترین“ به نظر می رسد، بدون توجه به انتخاب هایی که قبلا انجام داده یا در آینده انجام خواهد داد، بر می دارد.
الگوریتم حریصانه ، همانند برنامه نویسی پویا غالبا برای حل مسائل بهینه سازی به کار می روند، ولی روش حریصانه صراحت بیشتری دارد.
در روش حریصانه ، تقسیم به نمونه های کوچک تر صورت نمی پذیرد.
.....
جلسه چهارم
«تكرار»
مقدمه
تكرار، اجراي پي در پي يك دستور يا بلوكي از دستورالعملها در يك برنامه
است. با استفاده از تکرار ميتوانيم کنترل برنامه را مجبور کنيم تا به خطوط قبلي
برگردد و آنها را دوباره اجرا نمايد.
C++ داراي سه دستور تكرار است:
دستور while، دستور do_while و دستور for. دستورهاي تکرار به علت طبيعت چرخهمانندشان، حلقه نيز ناميده ميشوند.
1- دستور while
تابع scanf()
در زبان C دادههاي ورودي ميتوانند به كمك تابع كتابخانهاي scanf از طريق دستگاه ورودي استاندارد وارد كامپيوتر شوند. تابع scanf نيز تابع فرمتدار و مشابه تابع printf است ولي در جهت عكس عمل ميكند. به كمك اين تابع ميتوان دادههاي عددي، كاراكترها، رشتهها يا تركيبي از آنها را وارد كامپيوتر كرد. فرمت اين تابع مشابه فرمت تابع printf و فرم كلي آن به صورت زير است.
scanf ("control string", arguments list) ;
يا
scanf ("control string", argl , arg2 ,…, arg n) ;
در اينجا نقش رشتة كنترل مشابه تابع printf و شامل اطلاعات قالببندي خاص است. مشابه printf اين تابع نيز ميتواند هر تعداد آرگومان را دارا باشد، كه در آن اولين آرگومان رشتة فرمت يا رشتة كنترل است. همچنين اين تابع، اغلب همان كد فرمت تابع printf را به كار ميبرد؛ براي مثال كدهاي فرمت %s, %c , %f , %d که به ترتيب براي خواندن دادههايي از نوع مقادير صحيح، اعشاري، كاراكتر و رشته به كار میروند. تفاوت مهم بين اين دو تابع آن است كه در جلوي آرگومانها، اپراتور آدرس يعني "&" نيز قرار ميگيرد.
البته اگر بخواهيد مقداري را براي متغير رشتهاي بخوانيد، نيازي به اپراتور "&" نخواهد بود زيرا رشتهها در زبان ِC به صورت آرايهاي از نوع كاراكتر معرفي ميگردند و نام آرايه نيز معرف آدرس آرايه (يعني آدرس اولين عنصر آن) است.
مثال 3ـ8 برنامة زير نحوة کاربرد عملگر & را در تابع scanf نشان ميدهد.
#include
main ()
{
int x ;
char name[6] ;
scanf("%d" , &x) ;
scanf("%s", name) ;
printf("%d %s", x , name) ;
}
دستور scanf اول سيستم را هدايت ميكند كه داده ورودي را به صورت عدد صحيح از طريق ترمينال دريافت كند و اين مقدار را در متغير x ذخيره کند. دستور scanf دوم به دليل استفاده از آرايه، بدون عملگر & به کار میرود و اگر در اين برنامه براي متغير name رشتة "book" را وارد كرده باشيم، خروجي آن كلمة book خواهد بود.
جدول 3ـ3 فرامين يا کاراکترهاي فرمت براي دادههاي ورودي را كه کاراکترهاي تبديل نيز ناميده ميشوند نشان میدهد.
جدول 3ـ3 كاراكترهاي فرمت در تابع scanf ()
|
شـــرح |
كد فرمت |
|
داده ورودي به صورت تككاراكتر تعبير ميشود. |
%c |
|
داده ورودي به صورت عدد صحيح علامتدار (در مبناي 10) تعبير ميشود. |
%d |
|
داده ورودي به صورت عدد صحيح علامتدار تعبير ميشود. |
%i |
|
داده ورودي به صورت عدد صحيح بدون علامت دهدهي تعبير ميشود. |
%u |
|
داده ورودي به صورت عدد صحيح اعشاري با مميز شناور (floating_point) تعبير ميشود. |
%f , %e, %g |
|
داده ورودي به صورت عدد صحيح كوتاه (short integer) تعبير ميشود. |
%h |
|
داده به صورت رشته تعبير ميشود. ورودي با يك كاراكتر non_white_space آغاز ميگردد و با اولين كاراكتر white_space خاتمه ميپذيرد (به پايان رشته به طور خودكار كاراكتر "\0" افزوده خواهد شد). |
%s |
|
داده ورودي به صورت عدد صحيح در مبناي 8 تعبير ميشود. |
%0 |
|
داده ورودي به صورت عدد صحيح در مبناي 16 تعبير ميشود. |
%x , %X |
|
داده ورودي اشارهگر تعبير ميشود. |
%p |
مثال 3ـ9 برنامة زير يك خط متن حداكثر به طول 79 كاراكتر را ميخواند و آن را به همان صورت چاپ ميكند.
#include
main () /* read a line of text */
{
char line[80] ;
int count , k ;
/* read in the line */
for (k=0 ; line[k]=getchar ()!=’\n’ ; + +k)
count = k ;
for (k=0 ; k
putchar(line[k]) ;
}
در حلقة for، شمارندة k از صفر شروع ميشود و مقدار آن در هر تكرار يك واحد افزايش مييابد و در هر تكرار يك كاراكتر با تابع getchar از طريق ورودي استاندارد دريافت ميشود و به line[k] نسبت داده ميشود و وقتي كه كاراكتر خط جديد (يعني \n) وارد شد، عمل ورود كاراكترهاي رشته خاتمه مييابد كه در اين لحظه مقدار k برابر تعداد كاراكترهاي واقعي رشته خواهد بود. سپس در حلقة بعدي محتواي آراية line[ ] كه دربردارندة رشتة دريافت شده است چاپ ميگردد (دو تابع getchar و putchar دوباره بررسي خواهد شد). راه ديگر براي ورود رشتهها به حافظة كامپيوتر استفاده از تابع gets است كه در مبحث رشتهها بحث میکنیم.
براي خواندن رشتههايي كه در آنها فضاي خالي (space يا blank) وجود داشته باشد، ميتوان به طريقي از تابع scanf نيز استفاده کرد. براي اين كار ميتوان به جاي كاراكتر تبديل نوع s در رشتة كنترلي، دنبالهاي از كاراكترها را در داخل كروشه به صورت [...] قرار داد كه در اين صورت رشتة مورد نظر هريك از كاراكترهاي موجود در داخل كروشه ازجمله blank را شامل میشود.
با چنين روشي وقتي كه برنامه اجرا ميگردد، تا زماني كه كاراكترهاي متوالي خوانده شده از طريق دستگاه ورودي با يكي از كاراكترهاي موجود در درون كروشهها يكسان باشد، عمل خواندن رشتهها ادامه مييابد. فضاي خالي نيز در داخل رشتهها منظور میشود. به محض اينكه كاراكتري خوانده شود كه در داخل كروشهها وجود نداشته باشد، عمل خواندن خاتمه ميپذيرد. درضمن يك كاراكتر null به طور خودکار به پايان رشته افزوده میشود.
مثال 3ـ10 برنامة زير كاربرد تابع scanf را براي خواندن رشتههايي كه شامل حروف بزرگ و فضاي خالي است نشان ميدهد. طول اين رشته با درنظر گرفتن كاراكتر پايان رشته 80 كاراكتر خواهد بود.
#include
main ()
{
char line[80] ;
..........
scanf("%[ ABCDEFGHIJKLMNOPQRSTUVWXYZ ]", line) ;
..........
}
حال اگر از طريق ورودي، رشتة COMPUTER SCIENCE وارد شود، وقتي كه برنامه اجرا ميگردد، تمامي رشتة مزبور به آراية line نسبت داده ميشود. به هرحال اگر يكي از حروف رشتة مزبور به حرف كوچك تايپ شود، ورود رشته در همان كاراكتر خاتمه ميپذيرد. مثلاً اگر در مثال بالا p بهصورت كوچك تايپ شود، فقط سه حرف com به آراية line نسبت داده ميشود و عمل خواندن در حرف چهارم (حرف p) خاتمه خواهد يافت.
راه ديگر آن است كه به جاي اينكه كاراكترهاي مجاز در رشتة مورد نظر را در داخل كروشه ذكر كنيم، فقط كاراكترهايي را كه مجاز نيستيم در رشتهها به كار ببريم مشخص میکنيم. براي اين كار كافي است كاراكترهاي مورد نظر را به دنبال نماد "^" كه circumflex ناميده ميشود، در داخل كروشه قرار دهيم. يعني در اينجا نقش كاراكترهاي كروشهاي عكس حالت قبلي است و وجود هركدام از آنها در داخل يك رشته موجب قطع ورود بقية كاراكترهاي رشته ميگردد و عمل خواندن رشته خاتمه ميپذيرد.
اگر كاراكتر داخل كروشهها كه بعد از "^" ميآيد، فقط كاراكتر خط جديد "\n" باشد، رشتهاي كه از طريق دستگاه ورودي استاندارد وارد ميشود هر كاراكتر اسكي به جز كاراكتر خط جديد را شامل میشود. بنابراين، كاربر ميتواند هرچه خواست بهعنوان كاراكترهاي رشته وارد كند و در پايان كليد Enter را فشار دهد. اين كليد كاراكتر خط جديد را صادر ميكند و درنتيجه پايان رشته را اعلام خواهد كرد.
مثال 3ـ11 فرض كنيد كه يك برنامة C شامل دستورهاي زير است.
#include
main ()
{
char line[80] ;
..........
..........
scanf("%[^\n]", line) ;
..........
..........
}
وقتي كه تابع scanf در برنامة بالا اجرا ميگردد، رشتهاي به طول نامشخص (ولي حداكثر 79 كاراكتر) از طريق دستگاه ورودي استاندارد وارد ميگردد و به line نسبت داده ميشود. هيچ گونه محدوديتي در مورد كاراكترهاي تشكيلدهندة رشته وجود نخواهد داشت، فقط بايد در يك خط بگنجد. براي مثال رشتة زير از طريق صفحهكليد وارد و به line نسبت داده میشود.
WE LEARN MATHEMATICS.
تابع getchar()
براي خواندن يك كاراكتر از دستگاه ورودي، ميتوان علاوه بر تابع scanf از تابع getchar نیز استفاده کرد. تابع مزبور كه جزء كتابخانة I/O زبان استاندارد C است، كاراكتری از دستگاه ورودي استاندارد که معمولاً صفحهكليد است ميخواند. اين تابع آرگومان ندارد و به طور متعارف در يك دستور انتساب يا جايگذاري به كار میرود و كاراكتر دريافتي از ورودي را به متغيري كه در سمت چپ دستور جايگذاري مورد نظر است اختصاص ميدهد. شکل كلي آن به صورت زير است.
character variable = getchar() ;
= getchar() ; متغير كاراكتري
كه در آن متغير كاراكتري نام متغيري از نوع كاراكتر است كه بايد از قبل توصيف شده باشد.
مثال 3ـ12 به دستورهاي زير توجه کنيد.
char ch ;
ch = getchar() ;
در عبارت اول، متغير ch از نوع كاراكتر توصيف شده است. وقتي كه اجراي برنامه به دستور دوم برسد، برنامه منتظر فشار دادن كليدي از صفحهكليد ميشود. حال كاراكتر كليد فشار داده شده، به متغير ch اختصاص مییابد. چنانچه متغير ch از نوع int معرفي گردد، کد اسكي كاراكتر مربوط به كليد فشار داده شده، به آن متغير اختصاص مييابد.
اگر هنگام خواندن كاراكتر با تابع getchar، شرايط پايان فايل پيش آيد مقدار سمبوليكي EOF به طور خودكار برگشت داده میشود (اين مقدار در داخل فايل stdio.h اختصاص مییابد. به طور متعارف مقدار 1- به EOF اختصاص داده ميشود، اگرچه ممكن است اين مقدار از کامپایلری به کامپایلر ديگر فرق كند). ظاهر شدن EOF بهاين طريق، راه سادهاي براي تشخيص پايان فايل در هنگام اجراي آن است ( در اين مورد، در مبحث فايلها بيشتر بحث خواهیم کرد. لذا به هيچ وجه نگران آن نباشيد). ميتوان تابع getchar را نيز براي خواندن رشتة چند كاراكتري به صورت حلقة تكرار به كار برد كه در هر تكرار یک كاراكتر را بخواند.
تابع putchar()
اين تابع براي شمارش يك كاراكتر روي خروجي استاندارد كه معمولاً صفحه نمايش است به كار میرود و نقش آن مشابه تابع getchar اما در جهت عكس است. طبيعي است كاراكتري كه انتقال مييابد به صورت ثابت كاراكتر يا متغيری از نوع كاراكتر که آرگومان تابع مزبور است به كار میرود. شکل كلي اين تابع به صورت زير است.
putchar (character variable) ;
; (متغير كاراكتري) putchar
البته ميتوان ثابت كاراكتري را نيز به عنوان آرگومان تابع مزبور به كار برد. اين تابع با استفاده از آرايه يک رشته را در خروجي چاپ میکند.
مثال 3ـ13 برنامة زير به تدريج در هر بار يك كاراكتر ميخواند و سپس آن را در خروجي چاپ ميكند.
#include
main ()
{
char ch ;
while (1)
{
ch = getchar() ;
putchar(ch) ;
}
}
در اين برنامه ch كاراكتر اعلان شده است. هر بار كه يك كاراكتر از طريق دستگاه ورودي استاندارد خوانده ميشود، به همان طريق به خروجي انتقال مييابد. اما به لحاظ اينكه عبارت مربوط به while، يعني مقدار داخل پرانتز بعد از while، برابر "1" و هميشه غيرصفر است، براساس قوانين زبان C، اين عبارت هميشه درست يا true است. بنابراين ساختار while(1) حلقهای بينهايت است و تنها راه متوقف ساختن برنامه وقفهای است كه با کليدهاي control-c عملي خواهد شد.
راه ديگر براي نوشتن برنامة بالا به صورت زير است.
#include
main ()
{
int ch ;
while ((ch=getchar()) != EOF)
putchar(ch) ;
}
این برنامه تركيب دو عمل در يك دستور را در حلقه نشان ميدهد. گفتیم EOF در برنامة بالا علامت سمبوليك پايان فايل است. آنچه در واقعيت نشانة پايان فايل را نشان میدهد تابع سيستم است. براي اين كار اغلب عدد 1- به كار میرود، ولي سيستمهاي مختلف ممکن است مقادير متفاوتي داشته باشند. با گنجانيدن فايل stdio.h و به كار بردن ثابت سمبوليكي EOF، برنامه را قابل حمل يا قابل اجرا ساختهايم. يعني، فايل مبنا روي سيستمهاي مختلف بدون تغيير اجرا میشود.
ملاحظه ميكنيد كه در روش اخير، متغير ch به جاي char به صورت int معرفي شده است. هرچه براي نشان دادن پايان فايل به كار میرود، نميتواند مقداري باشد كه يك كاراكتر را معرفي نمايد. حال چون C بهصورت int معرفي شده است، ميتواند مقادير تمام كاراكترهاي ممكن و همين طور مقدار ويژة EOF را نگهداري کند.
همان طور كه گفتیم، هر دو تابع getchar و putchar در فايل stdio.h تعريف شدهاند و ممكن است در بعضي سيستمها در فايلهاي ديگري نيز مانند فايل conio.h تعريف شده باشند.
مثال 3ـ14 برنامة زير يك خط متن را از ورودي با حروف كوچك دريافت و آن را به حروف بزرگ تبديل ميكند.
#include
main ()
{
char line[80] ;
int count , k ;
/* read in the line */
for (k=0 ; (line[k]=getchar())!=’\n’ ; + + k) ;
count = k ;
/* write out the line in upper-case */
for(k=0 ; k
putchar(toupper(line[k])) ;
}
در برنامة بالا از حلقة for و تابع کتابخانهاي toupper استفاده شده است. نقش اين تابع آن است كه حروف كوچك را به بزرگ تبديل ميكند. بنابراين اگر حروف ورودي هنگام تايپ حروف بزرگ يا ارقام و مشابه آن باشند، به شکل اولية خود نمايش داده خواهند شد. براي مثال اگر ورودي به صورت Advanced programming باشد، خروجي به صورت ADVANCED PROGRAMMING خواهد بود.
مثال 3ـ15 برنامة زير يك خط از متن را ميخواند و در آن هر كاراكتر را (به غير از كاراكتر فضاي خالي يا space) به كاراكتر بعدي تبديل ميكند و نمايش ميدهد (درواقع متن را به شکلی به صورت رمز در ميآورد و نمايش ميدهد).
#include
#define space ’ ’
main ()
{
char ch ;
ch = getchar () ; /* read a character from i/o */
while(ch!=’\n’) /*while not end of line */
{
if (ch= =space) /* leave the space */
putchar(ch) ; /* character unchanged */
else
putchar(ch+1) ; /* change other characters */
ch = getchar() ; /* get next character */
}
}
برای مثال اگر computer science2 ورودي باشد، خروجي dpnqvufs tdjfodf3