مبانی کامپیوتر و برنامه سازی - نصب و راه اندازی شبکه

Download Report

Transcript مبانی کامپیوتر و برنامه سازی - نصب و راه اندازی شبکه

‫مراجع‬
‫‪« ‬برنامه‌نویسی‌به‌زبان‌‪‌،»C‬سعید‌ابریشمی‪‌،‬انتشارات‌جهاد‌‬
‫دانشگاهی‬
‫‪« ‬مبانی‌کامپیوتر‌و‌برنامه‌نویسی به‌زبان‌‪‌، »C‬محمد‌داورپناه‬
‫جزی‪‌،‬انتشارات‌دانشگاه‌صنعتی‌اصفهان‬
‫‪C How to program, Deitel and Deitel, 6th ed‬‬
‫‪ ‬مبانی‌رایانه‪‌،‬فنی‌و‌حرفه‌ای‪‌،‬محمدرضا‌موحدی‪‌،‬محمدرضا‌‬
‫یمقانی‬
‫‪ ‬ابزار‌)‪Visual Studio – C++ (6, 2003, 2005, 2008, 2010, 2012‬‬
‫‪‬‬
‫ارزیابی‌درس‬
‫‪10‬‬
‫‪‬‬
‫امتحان پایان ترم‪:‬‬
‫‪‬‬
‫امتحان میان ترم‪:‬‬
‫‪‬‬
‫پروژه کالسی و نهایی‪ 5 :‬نمره‬
‫‪‬‬
‫حضور و غیاب‪ ،‬فعالیت کالسی‬
‫‪5‬‬
‫نمره‬
‫نمره‬
‫مبانی کامپیوتر و برنامه سازی‬
‫فصل‌اول‌‪ :‬مبانی‌کامپیوترها‬
‫مدرس‌‪ :‬رضا‌رمضانی‬
‫‪ 1-1‬تعاریف اولیه‬
‫‪‬‬
‫کامپیوتر‬
‫‪‬‬
‫‪‬‬
‫الگوریتم‬
‫‪‬‬
‫‪‬‬
‫وسیلهای است که دادهها و دستورالعملها را از انسان دریافت‬
‫کرده و پس از اجرای دستورالعملها برروی داده ها‪ ،‬دادههای‬
‫حاصل را به انسانها باز میگرداند‪ .‬به اجرای دستورالعملها برروی‬
‫دادهها پردازش گفته میشود‪.‬‬
‫دستورالعملهایی که برای کامپیوتر نوشته میشود را الگوریتم‬
‫گوییم ‪.‬‬
‫برنامه کامپیوتری‬
‫‪‬‬
‫به تشریح الگوریتمها برای کامپیوتر با استفاده از یک زبان برنامه‬
‫سازی گفته میشود‪.‬‬
‫‪ 1-1‬تعاریف اولیه‬
‫‪‬‬
‫زبان برنامه سازی‬
‫‪‬‬
‫زبانی است که برای کامپیوتر قابل فهم بوده و الگوریتمها با استفاده از آن به کامپیوتر داده‬
‫میشوند‪ .‬این زبانها به سه دسته تقسیم میگردند ‪:‬‬
‫‪‬‬
‫‪‬‬
‫زبانهای سطح پایین ‪ :‬که به آن زبان ماشین نیز گفته میشود‪ ،‬مستقیما به زبان خود کامپیوتر‬
‫(یعنی زبان صفر و یک) نوشته میشود و توسط کامپیوتر قابل اجرا میباشد‪.‬‬
‫زبانهای سطح باال ‪ :‬این زبانها بسیار نزدیک به زبان انسان هستند‪ .‬مثال ‪:‬‬
‫;‪If (a > b) then c = c + 1‬‬
‫برای تبدیل این زبان به زبان ماشین نیاز به مترجم داریم ‪:‬‬
‫‪‬‬
‫‪‬‬
‫کامپایلر )‪ : (Compiler‬ابتدا کل برنامه زبان سطح باال را بررسی کرده و درصورت نبود خطا کل آن را به زبان‬
‫ماشین تبدیل میکند‪ .‬اکنون برنامه آماده اجرا است‪.‬‬
‫‪‬‬
‫مفسر )‪ : (Interpreter‬برنامه زبان سطح باال را دستور به دستور به زبان ماشین تبدیل و همزمان آن را اجرا‬
‫میکند‪.‬‬
‫زبانهای بسیار سطح باال ‪ :‬زبانهای خاص منظورهای که برای عملیات خاص طراحی شدهاند همانند‬
‫زبان ‪ PROLOG‬برای هوش مصنوعی و یا ‪ SQL‬برای پایگاه داده ها‪.‬‬
‫‪ 1-2‬تاریخچه کامپیوتر‬
‫‪‬‬
‫کامپیوترهای نسل اول‬
‫‪‬‬
‫‪‬‬
‫کامپیوترهای نسل دوم‬
‫‪‬‬
‫‪‬‬
‫در سال ‪ 1964‬با ابداع مدارات مجتمع ‪ IC‬که صدها ترانزیستور را در یک فضای کوچک‬
‫جای میداد‪ ،‬ایجاد شدند‪.‬‬
‫نسل چهارم کامپیوترها‬
‫‪‬‬
‫‪‬‬
‫ابداع در اوایل دهه ‪1960‬و ویژگی مهم آنها استفاده از ترانزیستور بود‪.‬‬
‫کامپیوترهای نسل سوم‬
‫‪‬‬
‫‪‬‬
‫ابداع در اوایل دهه ‪ 1950‬و از المپ خالء بعنوان جزو اصلی خود استفاده میکردند‪.‬‬
‫در اواسط دهه ‪ 1970‬با ابداع مدارات مجتمع با فشردگی باال ایجاد شدند‪.‬‬
‫نسل پنجم کامپیوترها‬
‫‪‬‬
‫یا نسل کامپیوترهای هوشمند که قادر به انجام اعمالی همانند استنتاج و استدالل‬
‫مانند انسانها باشند‪.‬‬
‫‪ 1-3‬انواع کامپیوتر‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫کامپیوترهای بزرگ )‪(mainframe‬‬
‫‪ ‬این کامپیوترها از سرعت و قدرت باالیی برخوردارند و معموال در‬
‫سازمانهای بزرگ و برای محاسبات سنگین استفاده میشوند‪ .‬دستهای‬
‫از این کامپیوترها که دارای توان بسیار باالی محاسباتی هستند به‬
‫ابرکامپیوتر )‪ (supercomputer‬موسومند‪.‬‬
‫کامپیوترهای کوچک )‪(minicomputer‬‬
‫‪ ‬در اواخر دهه ‪ 1950‬کامپیوترهای کوچک وارد بازاز شدند که توان‬
‫محاسباتی کمتری داشتند و توسط سازمانهای کوچکتر مورد استفاده‬
‫قرار میگرفتند‪.‬‬
‫ریزکامپیوتر )‪(microcomputer‬‬
‫‪ ‬در آغاز دهه ‪ 1980‬ریزکامپیوترها یا کامپیوترهای شخصی با قیمت پایین‬
‫و حجم بسیار کوچک وارد بازار شدند و مورد استقبال مردم و افراد عادی‬
‫قرار گرفتند‪.‬‬
‫‪ 1-4‬اجزای کامپیوتر‬
‫‪‬‬
‫کامپیوتر از دو قسمت اصلی تشکیل شده است‬
‫‪‬‬
‫‪‬‬
‫سخت افزار )‪ : (Hardware‬کلیه دستگاههای الکتریکی‪،‬‬
‫الکترونیکی و مکانیکی تشکیل دهنده یک کامپیوتر را‬
‫سخت افزار آن میگوییم‪.‬‬
‫نرم افزار )‪ : (Software‬مجموعه برنامههایی هستند که‬
‫برای یک کاربرد خاص نوشته شدهاند و بدون آنها سخت‬
‫افزار قادر به کاری نیست‪.‬‬
‫‪ 1-4-1‬سخت افزار کامپیوتر‬
‫کنترل‬
‫واحد خروجی‬
‫حافظه اصلی‬
‫محاسبه و منطق‬
‫حافظه جانبی‬
‫واحد ورودی‬
‫‪ 1-4-1‬سخت افزار کامپیوتر‬
‫‪‬‬
‫واحد ورودی‬
‫‪‬‬
‫‪‬‬
‫واحد خروجی‬
‫‪‬‬
‫‪‬‬
‫این بخش وظیفه انتقال اطالعات از کامپیوتر به محیط خارج را بعهده دارد و مهمترین‬
‫دستگاههای خروجی عبارتند از ‪:‬‬
‫صفحه نمایش)‪ ،(Monitor‬چاپگر‪ ،‬رسام‪ ،‬بلندگو و ‪...‬‬
‫واحد محاسبه و منطق‬
‫‪‬‬
‫‪‬‬
‫وظیفه این بخش دریافت دادهها از محیط خارج و انتقال آنها به کامپیوتر میباشد‪.‬‬
‫دستگاههای ورودی مهم عبارتند از ‪:‬‬
‫صفحه کلید‪ ،‬ماوس‪ ،‬صفحه لمسی )‪ ،(touch screen‬قلم نوری‪ ،‬اسکنر‪ ،‬دیجیتایزر و ‪...‬‬
‫مغز اصلی کامپیوتر است که اعمال اصلی همچون جمع‪ ،‬ضرب‪ ،‬تفریق‪ ،‬تقسیم‪،‬‬
‫مقایسه دو مقدار و ‪ ...‬در آن انجام میپذیرد‪.‬‬
‫واحد کنترل‬
‫‪‬‬
‫این بخش و ظیفه کنترل سایر بخشها را بعهده دارد و تصمیم میگیرد کدام عمل در‬
‫چه زمانی صورت پذیرد این بخش بهمراه واحد محاسبه و منطق تشکیل واحد‬
‫پردازش مرکزی )‪ CPU (Central Processing Unit‬را میدهند‪.‬‬
‫‪ 1-4-1‬سخت افزار کامپیوتر‬
‫‪‬‬
‫واحد حافظه اصلی‬
‫‪‬‬
‫این واحد وظیفه نگهداری اطالعات (شامل دادهها و برنامه ها) را بعهده دارد‪ .‬در واقع‬
‫هر برنامهای برای اجرا‪ ،‬ابتدا باید بهمراه دادههای مورد نیاز وارد حافظه اصلی‬
‫گردد‪.‬حافظه اصلی به دو دسته اصلی تقسیم میگردد ‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫ حافظه با دستیابی تصادفی )‪ (RAM Random Access Memory‬این حافظه قابل‬‫خواندن و نوشتن میباشد و برای ذخیره اطالعات کاربران بکار میرود‪.‬‬
‫ حافظه فقط خواندنی )‪ (ROM Read Only Memory‬این حافظه فقط قابل خواندن‬‫است و محتویات آن قابل تغییر نیست‪ .‬این حافظه معموال در کارخانه سازنده پر شده و‬
‫حاوی دستورالعملهای الزم برای راه اندازی اولیه کامپیوتر میباشد‪.‬‬
‫حافظه از واحدهای کوچکی بنام بیت )‪ (Bit‬تشکیل شده است که هر بیت قابلیت‬
‫نگاهداری یک ‪ 0‬یا ‪ 1‬را در خود دارد‪ .‬به هر ‪ 8‬بیت یک بایت )‪ (Byte‬گفته میشود که‬
‫واحد اندازه گیری حافظه است‪ .‬به هر ‪ 2‬یا ‪ 4‬بایت‪ ،‬یک کلمه )‪ (Word‬میگوییم‪.‬‬
‫عالوه براین داریم ‪:‬‬
‫‪= 1024 Byte‬‬
‫‪= 1024 KiloByte = 1048576 Byte‬‬
‫‪= 1024 MegaByte = 1073741824 Byte‬‬
‫‪= 1024 GigaByte‬‬
‫‪1 KiloByte or 1K‬‬
‫‪1 MegaByte or 1M‬‬
‫‪1 GigaByte or 1G‬‬
‫‪1 TeraByte or 1T‬‬
‫‪ 1-4-1‬سخت افزار کامپیوتر‬
‫‪‬‬
‫حافظه جانبی‬
‫‪‬‬
‫از آنجا که با خاموش شدن کامپیوتر اطالعات حافظه اصلی پاک‬
‫میگردد‪ ،‬نیاز به حافظهای داریم که بتواند دادهها را مدت‬
‫طوالنیتری در خود نگاه دارد‪ .‬حافظه جانبی برای نگاهداری‬
‫طوالنی مدت اطالعات و همچنین جابجایی آنها بکار میرود‪.‬‬
‫عالوه براین بدلیل سرعت پایینتر نسبت به حافظه اصلی‪ ،‬ارزانتر‬
‫بوده و درنتیجه معموال حجم آن باال تر میباشد‪ .‬اما نکته مهم آن‬
‫است که اطالعات برای پردازش ابتدا باید وارد حافظه اصلی‬
‫گردند‪.‬در حال حاضر حافظههای جانبی مهم عبارتند از ‪:‬‬
‫‪ ‬دیسکهای مغناطیسی )‪(Hard Disk and Floppy Disk‬‬
‫‪ ‬دیسکهای نوری )‪(CD and DVD‬‬
‫‪Flash Disk ‬‬
‫‪ 1-4-2‬نرم افزار کامپیوتر‬
‫‪‬‬
‫نرم افزار کامپیوتر به دو دسته اصلی تقسیم میگردد ‪:‬‬
‫‪ ‬نرم افزارهای کاربردی ‪ :‬نرم افزارهایی هستند که برای یک کاربرد خاص‬
‫و رفع یک نیاز مشخص کاربران نوشته شده اند‪ .‬مانند سیستمهای‬
‫حسابداری‪ ،‬دبیرخانه‪ ،‬سیستم انتخاب واحد دانشگاهی‪ ،‬انواع بازیها‬
‫‪ ‬نرم افزارهای سیستمی ‪ :‬نرم افزارهایی هستند که برای ایجاد و یا‬
‫اجرای برنامههای کاربردی نوشته میشوند‪ .‬مهمترین برنامه‬
‫سیستمی‪ ،‬سیستم عامل است‪ .‬سیستم عامل نرم افزاری است که‬
‫ارتباط بین سخت افزار و کاربران (یا برنامههای کاربردی کاربران) را‬
‫فراهم میسازد‪ .‬در حقیقت سیستم عامل مدیریت منابع سخت افزاری‬
‫یک کامپیوتر را بعهده دارد‪.‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪ : Windows‬بیشتر در منازل و محیطهای اداری مورد استفاده قرار میگیرد‬
‫‪ : Linux‬بیشتر در محیطهای دانشگاهی و بعنوان سرور استفاده میشود‪.‬‬
‫‪ : Unix‬نیز بیشتر در کامپیوترهای بزرگ نصب میشود‪.‬‬
‫مبانی کامپیوتر و برنامه سازی‬
‫فصل‌دوم‌‪ :‬نحوه‌نمایش‌اطالعات‌در‌کامپیوتر‬
‫مدرس‌‪ :‬رضا‌رمضانی‬
‫‪ 2‬نحوه نمایش اطالعات در کامپیوتر‬
‫‪‬‬
‫‪‬‬
‫اطالعات در کامپیوتر به دو دسته اصلی تقسیم میگردند‪:‬‬
‫‪ ‬اطالعات کاراکتری (حرفی) ‪ :‬مانند ‪A B …Z $ # @ ! :‬‬
‫‪ ‬اطالعات عددی که خود به دو دسته اعداد صحیح و اعداد اعشاری‬
‫تقسیم میگردند‪.‬‬
‫برای نمایش اطالعات در کامپیوتر از مبنای ‪ 2‬استفاده میگردد‬
‫‪ 2-1‬سیستم اعداد‬
‫‪‬‬
‫مبنای ‪ ، 10‬مبنای مورد استفاده انسانها در ریاضیات‬
‫‪‬‬
‫در ریاضیات متداول هر عدد ‪ N‬بصورت زیر تفسیر میگردد ‪:‬‬
‫‪N = (an-1 an-2 … a2 a1 a0 )10 = a0 × 100 + a1 × 101 + a2 × 102 + … an-1 × 10n-1‬‬
‫‪‬‬
‫بعنوان مثال عدد ‪ 3482‬بصورت زیر تفسیر میگردد ‪:‬‬
‫‪(3482 )10 = 2 × 100 + 8 × 101 + 4 × 102 + 3 × 103‬‬
‫‪‬‬
‫‪‬‬
‫در سیستم دهدهی نیاز به ‪ 10‬رقم (از ‪ 0‬تا ‪ )9‬داریم‪.‬‬
‫می توان اعداد را در هر مبنای دلخواه دیگری مانند ‪ b‬نیز نشان داد در اینصورت هر عدد‬
‫مانند ‪ N‬در مبنای ‪ b‬بصورت زیر تفسیر میگردد ‪:‬‬
‫‪N = (an-1 an-2 … a2 a1 a0 )b = a0 × b0 + a1 × b1 + a2 × b2 + … an-1 × bn-1‬‬
‫‪‬‬
‫‪‬‬
‫کامال واضح است که در مبنای ‪ b‬نیاز به ‪ b‬رقم (از ‪ 0‬تا‪ ) b-1‬خواهیم داشت‪.‬‬
‫بعنوان مثال یک عدد در مبنای ‪ 6‬از ارقام ‪ 0..5‬تشکیل میگردد‪ ،‬بنابراین ‪ (341)6‬یک عدد درست‬
‫است اما ‪ (592)6‬غیر قابل قبول میباشد‪.‬‬
‫‪ 2-2‬تبدیل مبناها‬
‫‪‬‬
‫برای تبدیل یک عدد از مبنای ‪ 10‬به هر مبنای دلخواه ‪ ،b‬از روش تقسیمات‬
‫متوالی استفاده میگردد‬
‫‪(941)10 = (?)6‬‬
‫‪6‬‬
‫‪0‬‬
‫‪6‬‬
‫‪4‬‬
‫‪0‬‬
‫‪4‬‬
‫‪941 6‬‬
‫‪936 156 6‬‬
‫‪26‬‬
‫‪156‬‬
‫‪5‬‬
‫‪0 24‬‬
‫‪2‬‬
‫‪(941)10 = (4205)6‬‬
‫‪ 2-2‬تبدیل مبناها‬
‫‪‬‬
‫برای تبدیل از مبنای ‪ b‬به مبنای ‪ 10‬کافی است ارقام عدد مورد نظر را در‬
‫ارزش مکانی آنها ضرب و سپس با یکدیگر جمع کنیم ‪.‬‬
‫‪(4205)6 = (?)10‬‬
‫‪(4205)6 = 5 × 60 + 0 × 61 + 2 × 62 + 4 × 63 = 5 + 0 + 72 + 864 = (941)10‬‬
‫‪ 2-3‬مبنای ‪ 2‬و اهمیت آن‬
‫‪‬‬
‫مبنای ‪ 2‬اهمیت بسیار زیادی در کامپیوترهای دیجیتال دارد‪ .‬چراکه ‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫در مبنای ‪ 2‬تنها به ‪ 2‬رقم نیاز داریم‪،‬یعنی ‪ 0‬و ‪1‬‬
‫آین دو رقم را میتوان توسط هر ابزاری که دارای دو حالت باشد نشان داد‪ .‬مثال‬
‫یک المپ که خاموش بودن المپ به معنای ‪ 0‬و روشن بودن آن به معنای ‪1‬‬
‫میباشد‪.‬‬
‫این همان ایدهای است که کامپیوترهای دیجیتال از آن استفاده میکنند‪.‬‬
‫همانطور که قبال نیز گفته شد واحد نگهداری اطالعات در کامپیوتر بیت میباشد‬
‫که هر بیت قادر به نگهداری ‪ 0‬و یا ‪ 1‬است‪ .‬با کنار هم قرار دادن بیتها‪ ،‬بایتها‬
‫تشکیل میگردند و بدینوسیله اطالعات مورد نظر در قالب بایتها تشکیل‬
‫میگردند‪.‬‬
‫‪ 2-3‬مبنای ‪ 2‬و اهمیت آن‬
‫‪‬‬
‫‪‬‬
‫تبدیل اعداد از مبنای ‪ 10‬به ‪ 2‬و بالعکس بسیار ساده و همانند سایر مبناها‬
‫است‪.‬‬
‫تبدیل از مبنای ‪ 2‬به ‪10‬‬
‫‪(11001001)2 = (?)10‬‬
‫‪(11001001)2 = 1 × 20 + 0 × 21 + 0 × 22 + 1 × 23 + 0 × 24 + 0 × 25 +‬‬
‫‪1 × 26 + 1 × 27‬‬
‫‪= 1 + 0 + 0 + 8 + 0 + 0 + 64 + 128 = (201)10‬‬
‫‪‬‬
‫تبدیل از مبنای ‪ 10‬به مبنای ‪2‬‬
‫‪(486)10 = (?)2‬‬
‫‪ 2-3‬مبنای ‪ 2‬و اهمیت آن‬
‫‪2‬‬
‫‪0‬‬
‫‪2‬‬
‫‪1‬‬
‫‪0‬‬
‫‪1‬‬
‫‪2‬‬
‫‪3‬‬
‫‪2‬‬
‫‪1‬‬
‫‪486 2‬‬
‫‪486 243 2‬‬
‫‪121 2‬‬
‫‪0 242‬‬
‫‪1 120 60 2‬‬
‫‪2‬‬
‫‪1 60 30‬‬
‫‪30 15 2‬‬
‫‪0‬‬
‫‪0 14 7‬‬
‫‪1 6‬‬
‫‪1‬‬
‫‪(486)10 = (111100110)2‬‬
‫‪ 2-4‬مبناهای ‪ 8‬و ‪ 16‬و کاربرد آنها‬
‫‪‬‬
‫مشکل اصلی در مبنای ‪ 2‬اندازه بزرگ اعداد است‪ .‬بعنوان مثال عدد ‪ 486‬که در‬
‫مبنای ‪ 10‬تنها ‪ 3‬رقم دارد‪ ،‬تبدیل به یک عدد ‪ 9‬رقمی در مبنای ‪ 2‬شده است‪.‬‬
‫‪‬‬
‫این مسئله باعث میشود که محاسبه در مبنای ‪ 2‬برای انسانها بسیار مشکل‬
‫شود و معموال برنامهنویسان عالقه چندانی به مبنای ‪ 2‬ندارند‪.‬‬
‫‪‬‬
‫‪‬‬
‫مبنای ‪ 8‬نیز همانند سایر مبناها میتواند مورد استفاده قرار گیرد و در ظاهر‬
‫تفاوتی با سایر مبناها ندارد‪ .‬اما ویژگی جالب این مبنا در تبدیل ساده آن به‬
‫مبنای ‪ 2‬و بالعکس است‪.‬‬
‫همانطور که میدانیم در مبنای ‪ 8‬تنها ارقام ‪ 0‬تا ‪ 7‬استفاده میشوند‪ .‬از طرف‬
‫دیگر اگر یک عدد در مبنای ‪ 2‬با حداکثر ‪ 3‬رقم را درنظر بگیریم‪ ،‬در مییابیم که‬
‫میتوان‬
‫‪000 = 0, 001=1, 010=2, … , 111=7‬‬
‫را با آن نشان داد‪ .‬بنابراین میتوان نتیجه گرفت که هر ‪ 3‬رقم در مبنای ‪ ،2‬برابر‬
‫است با ‪ 1‬رقم در مبنای ‪ 8‬و بالعکس‪ .‬این نتیجه گیری تبدیل این دو مبنا را به‬
‫یکدیگر ساده میکند‪.‬‬
‫‪ 2-4‬مبناهای ‪ 8‬و ‪ 16‬و کاربرد آنها‬
‫‪‬‬
‫تبدیل از مبنای ‪ 2‬به ‪8‬‬
‫‪‬‬
‫تبدیل از مبنای ‪ 8‬به ‪2‬‬
‫‪1 0 1 0 1 1 1 0 = (256)8‬‬
‫‪6‬‬
‫‪= (10111001)2‬‬
‫‪5‬‬
‫‪2‬‬
‫‪( 2 7 1 )8‬‬
‫‪010 111 001‬‬
‫‪ 2-4‬مبناهای ‪ 8‬و ‪ 16‬و کاربرد آنها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫اکثر برنامهنویسان کامپیوتر ترجیح میدهند از مبنای دیگری بنام مبنای ‪16‬‬
‫استفاده نمایند‪.‬‬
‫این مبنا نیز همانند مبنای ‪ 8‬بسادگی قابل تبدیل به مبنای ‪ 2‬است‪ ،‬اما‬
‫اعداد آن به ارقام کمتری نیاز دارند‪.‬‬
‫در این مبنا نیاز به ‪ 16‬رقم داریم درحالیکه ارقام موجود فقط ‪ 10‬تا است‪.‬‬
‫بهمین دلیل از حروف ‪ A‬تا ‪ F‬برای ارقام ‪ 10‬تا ‪ 15‬استفاده میگردد‪.‬یعنی‬
‫ارقام عبارتند از ‪:‬‬
‫‪0 1 2 3 4 5 6 7 8 9 A B C D E F‬‬
‫تبدیل اعداد از مبنای ‪ 2‬به ‪ 16‬و بالعکس ازهمان روش گفته شده برای‬
‫مبنای ‪ 8‬استفاده مینماییم با این تفاوت که هر رقم در مبنای ‪ 16‬معادل ‪4‬‬
‫رقم در مبنای ‪ 2‬است‪.‬‬
‫‪ 2-4‬مبناهای ‪ 8‬و ‪ 16‬و کاربرد آنها‬
‫‪‬‬
‫تبدیل از مبنای ‪ 2‬به ‪16‬‬
‫‪‬‬
‫تبدیل از مبنای ‪ 16‬به ‪2‬‬
‫‪1 1 0 1 0 1 1 1 0 0 1 = (6B9)16‬‬
‫‪9‬‬
‫‪B‬‬
‫‪6‬‬
‫‪( A 3 E )16 = (101000111110)2‬‬
‫‪101000111110‬‬
‫‪ 2-5‬نمایش اعداد صحیح‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫اعداد صحیح در کامپیوتر با استفاده از مبنای ‪ 2‬نمایش داده میشوند‪.‬‬
‫برای نمایش اعداد صحیح از ‪ 1‬یا ‪ 2‬بایت و یا بیشتر (بسته به اندازه‬
‫عدد) استفاده میگردد‪.‬‬
‫چنانچه قصد ذخیره اعداد صحیح مثبت را داشته باشیم‪ ،‬با استفاده از ‪1‬‬
‫بایت میتوان اعداد ‪ 0‬تا ‪ 255‬را ذخیره کرد‪ .‬بنابراین برای ‪ 1‬بایت‪،‬‬
‫بزرگترین عدد قابل ذخیره برابر است با ‪.28 – 1 = 255‬‬
‫با استدالل مشابهی چنانچه از ‪ 2‬بایت یا ‪ 16‬بیت استفاده گردد‪،‬‬
‫بزرگترین عدد قابل ذخیره برابر ‪ 216 – 1 = 65535‬خواهد بود‪.‬‬
‫اما مشکل آنستکه اعداد منفی را چگونه ذخیره نماییم؟‬
‫برای این کار چندین روش وجود دارد که هریک را جداگانه بررسی‬
‫مینماییم‪.‬‬
‫‪ 2-5-1‬استفاده از بیت عالمت‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫در این روش سمت چپ ترین بیت برای عالمت عدد درنظر گرفته‬
‫میشود و سایر بیتها مقدار عدد رانشان میدهند‪.‬‬
‫‪ 0‬بودن بیت عالمت بمعنای مثبت بودن و ‪ 1‬بودن آن به معنای منفی‬
‫بودن عدد میباشد‪.‬‬
‫با داشتن ‪ 8‬بیت میتوان اعداد بین ‪ -127 … +127‬را نمایش داد‪.‬‬
‫‪1 1010011‬‬
‫‪-83‬‬
‫‪‬‬
‫‪0 1010011‬‬
‫‪+83‬‬
‫این روش دو مشکل اصلی دارد‪:‬‬
‫‪‬‬
‫‪‬‬
‫دو مقدار متفاوت ‪ +0‬و ‪ -0‬وجود دارد‪.‬‬
‫برای عمل جمع و تفریق نیاز به دو مدار جداگانه داریم‪.‬‬
‫‪ 2-5-2‬استفاده از متمم ‪1‬‬
‫‪‬‬
‫در این روش اعداد مثبت بصورت معمولی نمایش داده میشوند‪ .‬اما برای‬
‫نمایش اعداد منفی‪ ،‬ابتدا قدر مطلق آن را (بصورت عدد مثبت) نمایش داده‬
‫و سپس کلیه ‪0‬ها را به ‪ 1‬و بالعکس تبدیل مینماییم‪.‬‬
‫‪10101100‬‬
‫‪01010011‬‬
‫‪+ 83‬‬
‫‪- 83‬‬
‫‪‬‬
‫‪‬‬
‫با توجه به محدودیت تعداد بیتها و جلوگیری از تداخل اعداد مثبت و منفی‪ ،‬برای اعداد مثبت‬
‫باید بیت سمت چپ ‪ 0‬باشد و بهمین دلیل بازه اعداد مجاز از ‪ -127‬تا ‪ +127‬است‪.‬‬
‫خوشبختانه اکنون تنها نیاز به یک مدار برای عمل جمع و تفریق است اما هنوز هم دو نمایش‬
‫مختلف برای ‪ +0‬و ‪ -0‬وجود دارد‪.‬‬
‫‪11111111‬‬
‫‪-0‬‬
‫‪00000000‬‬
‫‪+0‬‬
‫‪ 2-5-3‬استفاده از متمم ‪2‬‬
‫‪‬‬
‫در این روش برای نمایش اعداد منفی ابتدا متمم ‪ 1‬آنها را محاسبه و سپس‬
‫آن را با ‪ 1‬جمع میکنیم‪.‬‬
‫‪10100110‬‬
‫‪-90‬‬
‫‪‬‬
‫‪10100101‬‬
‫‪01011010‬‬
‫‪+90‬‬
‫بازه اعداد مجاز در این روش از ‪ -128‬تا ‪ +127‬است‪.‬‬
‫‪ 2-5-3‬استفاده از متمم ‪2‬‬
‫‪‬‬
‫این روش هردو مشکل روشهای قبل را حل میکند‪ .‬چرا که ‪:‬‬
‫‪‬‬
‫تنها یک نمایش برای ‪ +0‬و ‪ -0‬وجود دارد‪.‬‬
‫‪1 00000000‬‬
‫‪-0‬‬
‫‪‬‬
‫‪11111111‬‬
‫‪00000000‬‬
‫‪+0‬‬
‫برای عمل تفریق میتوان از همان مدار جمع استفاده‬
‫کرد‪ .‬بدین ترتیب که ابتدا عدد دوم را به روش متمم‬
‫‪ 2‬منفی کرده و با عدد اول جمع میکنیم‪.‬‬
‫‪53 – 22 = 53 + (-22) = 31‬‬
‫)‪(00110101) – (00010110) = (00110101) + (11101010‬‬
‫)‪= 1(00011111‬‬
‫‪38 – 60 = 38 + (-60) = -22‬‬
‫)‪(00100110) – (00111100) = (00100110) + (11000100‬‬
‫‪ 2-6‬نمایش مقادیر اعشاری‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫نمایش اعداد اعشاری در کامپیوتر مشکلتر است‪.‬‬
‫ما معموال اعداد اعشاری را به شکل ممیز ثابت نشان میدهیم ‪53.648 :‬‬
‫اما شکل دیگری نیز وجود دارد که به آن نماد علمی یا ممیز شناور گفته‬
‫میشود و به شکل زیر است ‪:‬‬
‫‪53.648 × 100‬‬
‫‪5.3648 × 101‬‬
‫‪0.53648 × 102‬‬
‫‪5364.8 × 10-2‬‬
‫همانطور که دیده میشود‪ ،‬مکان ممیز در این نمایش شناور است و میتواند در‬
‫هر نقطهای قرار گیرد و البته توان نیز باید متناسب با آن تنظیم گردد‪ .‬نماد‬
‫علمی نرمال به حالتی گفته میشود که قسمت صحیح فقط دارای یک رقم‬
‫غیر صفر باشد‪.‬‬
‫برای نمایش اعداد اعشاری در کامپیوتر استانداردهای مختلفی وجود دارد که‬
‫همگی در اصول مشترکند و تنها تفاوتهایی در جزئیات دارند‪.‬‬
‫‪ 2-6‬نمایش مقادیر اعشاری‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫ما در اینجا از یک روش استاندارد متداول که توسط انجمن معتبر ‪ IEEE‬ارائه‬
‫شده است‪ ،‬استفاده میکنیم‪ .‬این استاندارد بنام‬
‫‪IEEE Standard 754 Floating Point Numbers‬‬
‫شناخته شده است‪.‬‬
‫برای اطالعات بیشتر به سایت ‪ http://standards.ieee.org‬مراجعه نمایید‪.‬‬
‫در استاندارد مورد نظر هر عدد اعشاری در یک کلمه ‪ 32‬بیتی ذخیره‬
‫میگردد‪ .‬ساختار هر عدد بصورت زیر است‪:‬‬
‫‪8‬‬
‫‪23‬‬
‫‪m‬‬
‫مانتیس‬
‫‪e‬‬
‫توان‬
‫‪1‬‬
‫‪s‬‬
‫عالمت‬
‫‪ 2-6‬نمایش مقادیر اعشاری‬
‫‪‬‬
‫برای ذخیره یک عدد اعشاری باید ابتدا آن را به نماد علمی نرمال در‬
‫مبنای ‪ 2‬تبدیل کنیم‪ .‬سپس آن را به شکل زیر ذخیره میکنیم ‪:‬‬
‫‪ ‬عالمت عدد را در ‪ s‬قرار میدهیم (مثبت = ‪ 0‬و منفی = ‪)1‬‬
‫‪ ‬توان را در قسمت ‪ e‬قرار میدهیم که یک عدد مثبت بدون عالمت‬
‫است(بین ‪ 0‬تا ‪.)255‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫نکته مهم آنکه از آنجا که ممکن است توان مثبت یا منفی باشد‪ ،‬ابتدا ‪127‬‬
‫واحد به توان اضافه میکنیم و سپس آن را ذخیره میکنیم‪.‬‬
‫بنابراین ‪ e = 131‬باشد بدین معناست که توان برابر ‪ 4‬بوده است و ‪ e=120‬به‬
‫معنای توان برابر ‪ -7‬است‪.‬‬
‫البته توانهای ‪ e=255‬و ‪ e=0‬برای منظورهای خاصی در نظر گرفته شدهاند که‬
‫بعدا توضیح داده خواهند شد‪.‬‬
‫قسمت پایه عدد را در مانتیس قرار میدهیم‪.‬‬
‫‪‬‬
‫نکته جالب آن است که چون در نمایش نرمال قسمت صحیح تنها یک رقم‬
‫غیرصفر دارد و در نمایش دودویی نیز تنها رقم غیر صفر‪ ،‬رقم ‪ 1‬میباشد؛‬
‫بنابراین تنها قسمت اعشاری در مانتیس ذخیره میگردد و قسمت صحیح‬
‫بطور پیش فرض ‪ 1‬درنظر گرفته میشود‪ .‬این باعث میشود که یک بیت در‬
‫ذخیره سازی صرفه جویی گردد‪.‬‬
‫‪ 2-6‬نمایش مقادیر اعشاری‬
‫‪‬‬
‫مثال‪ :‬عدد ‪ 486‬را بصورت دودویی اعشاری ذخیره نمایید‪.‬‬
‫‪‬‬
‫ابتدا آن را به مبنای ‪ 2‬تبدیل میکنیم‪.‬‬
‫‪(486)10 = (111100110)2‬‬
‫‪‬‬
‫اکنون داریم ‪:‬‬
‫‪‬‬
‫بنابراین جواب نهایی بصورت زیر خواهد شد ‪:‬‬
‫‪111100110 = 1.11100110 × 28‬‬
‫‪m : 11100110‬‬
‫‪e : 8+127 = 135 = 10000111‬‬
‫‪11100110000000000000000‬‬
‫‪10000111‬‬
‫‪0‬‬
‫‪s:0‬‬
‫‪ 2-6‬نمایش مقادیر اعشاری‬
‫‪‬‬
‫مثال ‪ :2‬عدد ‪ -121.640625‬را بصورت دودویی اعشاری ذخیره نمایید‪.‬‬
‫‪‬‬
‫ابتدا عدد ‪ 121‬را به مبنای ‪ 2‬میبریم‪:‬‬
‫‪(121)10 = (1111001)2‬‬
‫‪‬‬
‫واما برای تبدیل قسمت اعشاری باید از روش ضربهای متوالی استفاده نماییم‪ .‬بدین‬
‫صورت که ابتدا آن را در ‪ 2‬ضرب کرده و قسمت صحیح حاصلضرب را ذخیره میکنیم‪.‬‬
‫همین عمل را مجددا برروی قسمت اعشاری حاصلضرب انجام میدهیم و اینکار را تا‬
‫صفر شدن قسمت اعشاری و یا پر شدن تعداد بیتهای کلمه موردنظر (‪ 23‬بیت) تکرار‬
‫میکنیم‪ .‬در پایان ارقام ذخیره شده را از اول به آخر به ترتیب پس از ممیز قرار‬
‫میدهیم‪ .‬داریم ‪:‬‬
‫‪0.640625 × 2 = 1.28125‬‬
‫‪ = 1‬قسمت صحیح‬
‫‪0.28125 × 2 = 0.5625‬‬
‫‪ = 0‬قسمت صحیح‬
‫‪0.5625 × 2 = 1.125‬‬
‫‪ = 1‬قسمت صحیح‬
‫‪0.125 × 2 = 0.25‬‬
‫‪ = 0‬قسمت صحیح‬
‫‪0.25 × 2 = 0.5‬‬
‫‪ = 0‬قسمت صحیح‬
‫‪0.5 × 2 = 1.00‬‬
‫‪ = 1‬قسمت صحیح‬
‫‪(0.640625)10 = (0.101001)2‬‬
‫‪ 2-6‬نمایش مقادیر اعشاری‬
‫‪‬‬
‫بنابراین عدد نهایی بصورت زیر درخواهد آمد ‪:‬‬
‫‪(121.640625)10 = (1111001.101001)2 = (1.111001101001 × 26)2‬‬
‫‪m : 111001101001‬‬
‫‪e : 6 + 127 = 133 = 10000011‬‬
‫‪11100110100100000000000‬‬
‫‪‬‬
‫‪10000011‬‬
‫‪s:1‬‬
‫‪1‬‬
‫نکته مهم ‪ :‬توجه داشته باشید که به دلیل محدود بودن اندازه مانتیس (‪ 23‬بیت)‪ ،‬در هنگام‬
‫تبدیل اعداد از مبنای ‪ 10‬به مبنای ‪ 2‬مجبور هستیم عمل ضرب را تا زمانیکه بیتها پر شوند‬
‫ادامه دهیم‪ .‬این مسئله باعث میشود که مقدار تقریبی اعداد در کلمه ‪ 32‬بیتی ذخیره گردد‪.‬‬
‫بنابراین در هنگام کار با اعداد اعشاری به این مسئله توجه کنید‬
‫‪ 2-6‬نمایش مقادیر اعشاری‬
‫‪‬‬
‫یک عدد اعشاری ذخیره شده به فرم استاندارد موردنظر به شکل زیر تفسیر‬
‫میگردد ‪:‬‬
‫‪‬‬
‫اگر ‪ 0<e<255‬آنگاه‬
‫‪‬‬
‫اگر ‪ e=0‬و ‪ m‬غیر صفر باشد آنگاه‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪ = (-1)s × (0.m) × 2e-126‬عدد‬
‫که به آن عدد غیرنرمال )‪ (unnormalized‬گفته میشود‪ .‬دلیل این مسئله آنست که با‬
‫کوچکترین توان ممکن یعنی ‪ -126‬بتوان کوچکترین مانتیس ممکن (بدون اضافه شدن ‪)1‬‬
‫را برای نمایش اعداد کوچک داشت‪.‬‬
‫اگر ‪ e=0‬و ‪ m=0‬آنگاه ‪ = 0‬عدد‬
‫‪‬‬
‫‪‬‬
‫‪ = (-1)s × (1.m) × 2e-127‬عدد‬
‫که البته بسته به میزان ‪ s‬مقدار آن ‪ +0‬یا ‪ -0‬خواهد بود‪.‬‬
‫اگر ‪ e=255‬و ‪ m‬غیرصفر باشد آنگاه حاصل یک عدد نیست (‪ NaN‬یا ‪)Not a Number‬‬
‫اگر ‪ e=255‬و ‪ m=0‬و ‪ s=0‬آنگاه عدد برابر مثبت بینهایت است‪.‬‬
‫اگر ‪ e=255‬و ‪ m=0‬و ‪ s=1‬آنگاه عدد برابر منفی بینهایت است‪.‬‬
‫‪ 2-7‬نمایش کاراکترها در کامپیوتر‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫در کامپیوترها عالوه بر اطالعات عددی‪ ،‬گاهی الزم است که حروف و عالئم نیز ذخیره‬
‫گردد که به آنها کاراکتر میگوییم‪.‬‬
‫برای ذخیره سازی کاراکترها به هریک از آنها یک کد عددی نسبت داده شده است و در‬
‫حقیقت کد عددی هر کاراکتر در کامپیوتر ذخیره میگردد‪.‬‬
‫در گذشته پر کاربردترین کد مورد استفاده‪ ،‬کد ‪ ASCII‬بود که برای نمایش هر کاراکتر از‬
‫یک بایت استفاده میکرد‪ .‬از آنجا که هر بایت میتواند بین ‪ 0‬تا ‪ 255‬تغییر کند‪ ،‬بنابراین تا‬
‫‪ 256‬کاراکتر قابل تعریف است‪ .‬از این بین کدهای بین ‪ 0‬تا ‪ 127‬بصورت استاندارد برای‬
‫عالئم و حروف انگلیسی تعریف شده است و کدهای باالتر از ‪ 127‬برای هر کشور خالی‬
‫گذاشته شده است تا بتوانند حروف خاص زبان خود را تعریف کنند‪ .‬بعنوان مثال به‬
‫کدهای ‪ ASCII‬زیر دقت کنید‪:‬‬
‫‪A=65‬‬
‫‪B=66‬‬
‫‪C=67 … 0=48‬‬
‫… ‪1=49‬‬
‫اما امروزه و بدلیل ارتباطات گسترده جهانی از طریق اینترنت‪ ،‬نیاز به تعریف یک کد بین‬
‫المللی میباشد که کلیه زبانهای جهانی را دربرگیرد‪ .‬چراکه متنی که در کشور دیگری‬
‫به زبان فارسی نوشته میشود باید در ایران هم قابل خواندن باشد و الزمه این مسئله‬
‫یکسان بودن کدها است‪.‬‬
‫بهمین دلیل اخیرا کد بین المللی بنام ‪ Unicode‬ابداع شده است که تقریبا تمام زبانهای‬
‫زنده دنیا (از جمله زبان فارسی) را دربر میگیرد‪ .‬البته این کد از ‪ 2‬بایت برای نمایش هر‬
‫کاراکتر استفاده میکند و سیستم عاملهای جدید همگی از آن حمایت میکنند‪.‬‬
‫مبانی کامپیوتر و برنامه سازی‬
‫فصل‌سوم‌‪:‬الگوریتم‬
‫مدرس‌‪ :‬رضا‌رمضانی‬
‫‪ 3‬الگوریتم‬
‫‪‬‬
‫الگوریتم‬
‫‪‬‬
‫‪‬‬
‫مجموعه محدودی از دستورالعملها است که اگر به ترتیب دنبال‬
‫شوند موجب انجام کار خاصی میگردند‪.‬‬
‫هر الگوریتم باید دارای شرایط زیر باشد‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫ورودی ‪ :‬یک الگوریتم میتواند صفر یا چند ورودی داشته باشد که‬
‫از محیط خارج تامین میگردد‪.‬‬
‫خروجی ‪ :‬الگوریتم باید یک یا چند کمیت خروجی داشته باشد‪.‬‬
‫قطعیت ‪ :‬هر دستورالعمل باید واضح و بدون ابهام باشد‪.‬‬
‫کارایی ‪ :‬هر دستورالعمل باید قابل اجرا باشد‪.‬‬
‫محدودیت ‪ :‬در تمام حاالت‪ ،‬الگوریتم باید پس از طی مراحل‬
‫محدودی خاتمه یابد‪.‬‬
‫‪ 3‬الگوریتم‬
‫‪‬‬
‫‪‬‬
‫در علم کامپیوتر‪ ،‬ما معموال با یک مسئله مواجهیم که باید آن را حل‬
‫کنیم‪ .‬این مسئله میتواند در زمینههای مختلفی همچون علمی‪،‬‬
‫اقتصادی‪ ،‬ریاضی‪ ،‬فنی و ‪ ...‬باشد‪.‬‬
‫معموال برای حل یک مسئله‪ ،‬مراحل زیر طی میگردد‬
‫‪ ‬تعریف مسئله بصورت جامع و دقیق (شامل تعریف ورودیها و خروجیها)‬
‫‪ ‬بررسی راه حلهای مختلف برای حل مسئله‬
‫‪ ‬انتخاب مناسبترین راه حل و تهیه یک الگوریتم برای آن‬
‫‪ ‬آزمایش الگوریتم با دادههای ورودی و اشکالزدایی آن‬
‫‪ ‬تبدیل الگوریتم به یک زبان برنامهنویسی کامپیوتری (مانند ‪ C‬یا ‪)Pascal‬‬
‫‪ ‬وارد کردن برنامه به کامپیوتر و تست و اشکالزدایی آن‬
‫‪ ‬استفاده از برنامه‬
‫‪ 3-1‬نحوه بیان الگوریتمها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫چگونه میتوانیم الگوریتمها را بیان کنیم؟‬
‫الگوریتمها باید برای انسانها قابل فهم و درک باشند و همه بتوانند به‬
‫راحتی منظور نویسنده الگوریتم را درک کنند‬
‫معموال الگوریتمها به یک زبان طبیعی مانند فارسی یا انگلیسی نوشته‬
‫میشود‪.‬‬
‫این مسئله باعث میشود که بعضی ابهامات در درک الگوریتمها پیش‬
‫آید‪.‬‬
‫معموال یکسری از توافقات و تعریفها از قبل بین طراح و خواننده الگوریتم‬
‫برقرار میگردد‪.‬‬
‫از آنجا که زبانهای برنامهنویسی مانند ‪ C‬به زبان انگلیسی خیلی‬
‫نزدیک هستند‪ ،‬بعض از طراحان از ترکیب زبان ‪ C‬و انگلیسی (که به آن‬
‫کد شبه ‪ C‬میگویند) برای بیان الگوریتم استفاده میکنند‬
‫الزم بذکر است که در گذشته از نمودار گردشی )‪ (Flowchart‬نیز برای‬
‫بیان الگوریتمها با استفاده از شکلهای استاندارد استفاده میشد‪ ،‬که‬
‫تنها برای الگوریتمهای کوچک مناسب بود‪.‬‬
‫‪ 3-2‬شروع به کار با الگوریتمها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫الگوریتمی بنویسید که ضرایب یک معادله درجه دوم بصورت زیر را دریافت و‬
‫ریشههای آن را محاسبه و چاپ کند‪.‬‬
‫‪ax2 + bx + c = 0‬‬
‫برای حل این مسئله ابتدا باید ضرائب ‪ b ، a‬و ‪ c‬از کاربر دریافت و در خانههای حافظه‬
‫ذخیره گردند‪.‬‬
‫برای اینکه بتوانیم بعدا به این خانههای حافظه مراجعه کنیم‪ ،‬به هریک از آنها یک‬
‫نام نسبت میدهیم‪ .‬به هریک از این نامها یک متغیر گفته میشود‪.‬‬
‫دلیل این نامگذاری آنستکه مقادیر ذخیره شده در هریک از این خانههای حافظه‬
‫میتواند تغییر کند‪.‬‬
‫گرچه انتخاب نام بعهده خودشماست و میتواند هر چیزی باشد‪ ،‬ولی توصیه‬
‫میگردد از اسامی بامعنی و متناسب استفاده گردد‪ .‬این کار سبب میشود که‬
‫خواندن و درک الگوریتم شما برای سایر افراد نیز ساده گردد‪.‬‬
‫اکنون به قراردادهای زیر توجه کنید ‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫برای دریافت اطالعات از کاربر از دستور بخوان استفاده میگردد‪.‬‬
‫برای نوشتن اطالعات در خروجی از دستور چاپ کن استفاده میگردد‪.‬‬
‫برای انتساب یک مقدار به یک متغیر از عالمت ← استفاده میشود‪.‬‬
‫‪ 3-2‬شروع به کار با الگوریتمها‬
‫‪(1‬‬
‫‪(2‬‬
‫‪(3‬‬
‫‪ a‬و ‪ b‬و ‪ c‬را بخوان‬
‫‪delta ← b2 – 4ac‬‬
‫‪ x1   b  delta‬و‬
‫‪(4‬‬
‫‪(5‬‬
‫‪ x1‬و ‪ x2‬را چاپ کن‬
‫توقف کن‬
‫‪2a‬‬
‫‪ b  delta‬‬
‫‪2a‬‬
‫‪x2 ‬‬
‫‪ 3-3‬مکانیزم شرط‬
‫‪‬‬
‫‪‬‬
‫مکانیزم شرط هنگامی استفاده میشود که قصد داریم درستی یا‬
‫نادرستی یک عبارت رابررسی کرده و متناسب با نتیجه بررسی شرط‪،‬‬
‫عملیات خاصی را انجام دهیم و یا از انجام بعضی عملیات صرفنظر کنیم‪.‬‬
‫شکل کلی این دستور به شکل زیر است‬
‫اگر (عبارت شرطی) آنگاه دستورات‬
‫‪‬‬
‫این دستور به شکل زیر نیز استفاده میشود‪:‬‬
‫اگر (عبارت شرطی) آنگاه دستورات‪1‬‬
‫درغیر اینصورت دستورات ‪2‬‬
‫‪ 3-3‬مکانیزم شرط‬
‫‪(1‬‬
‫‪(2‬‬
‫‪(3‬‬
‫‪(4‬‬
‫‪ a‬و ‪ b‬و ‪ c‬را بخوان‬
‫اگر ) ‪ ( a = 0‬آنگاه چاپ کن ”معادله درجه ‪ 2‬نیست“ و توقف‬
‫کن‬
‫‪delta ← b2 – 4ac‬‬
‫اگر ) ‪ ( delta < 0‬آنگاه چاپ کن ”معادله جواب ندارد“‬
‫درغیراینصورت ‪ b  delta‬‬
‫و ‪ b  delta‬‬
‫‪x ‬‬
‫‪x ‬‬
‫‪2a‬‬
‫‪1‬‬
‫و ‪ x1‬و ‪ x2‬را چاپ کن‬
‫‪(5‬‬
‫توقف کن‬
‫‪2a‬‬
‫‪2‬‬
‫‪ 3-4‬مکانیزم حلقه تکرار‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫در بعضی الگوریتمها الزم است که عملیات مشخصی چندین بار تکرار‬
‫شوند‪ .‬بعنوان مثال فرض کنید قصد داریم میانگین معدلهای ‪ 100‬دانشجو را‬
‫محاسبه کنیم‪ .‬قطعا ‪ 100‬بار دستور خواندن و جمع کردن راه حل چندان‬
‫مناسبی نیست‪.‬‬
‫راه حل بهتر آنستکه بگونهای به مجری الگوریتم بگوییم بنحوی عمل‬
‫خواندن معدل و جمع زدن آنها را ‪ 100‬بار تکرار کند‪.‬‬
‫حلقه تکرار مکانیزمی است که مجموعهای از دستوزات را تا زمانیکه شرط‬
‫خاصی برقرار باشد تکرار میکند‪.‬‬
‫حلقه تکرار به دو شکل مورد استفاده قرار میگیرد‬
‫‪ 3-4‬مکانیزم حلقه تکرار‬
‫‪‬‬
‫‪‬‬
‫شرط در ابتدای حلقه که مکانیزم کلی آن به شکل زیر است ‪:‬‬
‫تا زمانیکه (شرط مورد نظر) دستورات ‪ a‬تا ‪ b‬را تکرارکن‬
‫‪… (a‬‬
‫‪.‬‬
‫‪.‬‬
‫‪.‬‬
‫‪… (b‬‬
‫در این حالت ابتدا شرط موردنظر بررسی میگردد؛ درصورتیکه شرط‬
‫برقرار نباشد به اولین دستور پس از ‪ b‬میرود‪ .‬اما در صورتیکه شرط‬
‫درست ارزیابی شود‪ ،‬دستورات شماره ‪ a‬تا ‪ b‬انجام میشوند و سپس‬
‫مجددا به ابتدای حلقه بازگشته و عملیات فوق را مجددا تکرار میکند‪.‬‬
‫‪ 3-4‬مکانیزم حلقه تکرار‬
‫‪‬‬
‫‪‬‬
‫شرط در انتهای حلقه که مکانیزم کلی آن به شکل زیر است ‪:‬‬
‫تکرار کن‬
‫‪…(a‬‬
‫‪.‬‬
‫‪.‬‬
‫‪.‬‬
‫‪… (b‬‬
‫تا زمانیکه (شرط مورد نظر)‬
‫در این روش ابتدا دستورات حلقه یکبار انجام میشوند و در پایان حلقه‬
‫شرط بررسی میگردد‪ .‬چنانچه شرط برقرار نبود به دستور بعدی‬
‫میرود و در صورت برقرار بودن شرط‪ ،‬مجددا به ابتدای حلقه باز‬
‫میگردد‪.‬‬
‫‪ 3-4‬مکانیزم حلقه تکرار‬
‫‪‬‬
‫‪‬‬
‫‪(1‬‬
‫‪(2‬‬
‫‪(3‬‬
‫‪(4‬‬
‫‪(5‬‬
‫‪(6‬‬
‫‪(7‬‬
‫الگوریتمی بنویسید که یک عدد را دریافت و فاکتوریال آن را محاسبه و‬
‫چاپ کند‪.‬‬
‫‪N! = 1 × 2 × 3 × … × (N-1) × N‬‬
‫الگوریتم‬
‫‪ n‬را بخوان‬
‫‪ i ←1‬و ‪fact ←1‬‬
‫تا زمانیکه )‪ ( i ≤ n‬دستورات ‪ 4-5‬را تکرار کن‬
‫‪fact ← fact × i‬‬
‫‪i←i+1‬‬
‫‪ fact‬را چاپ کن‬
‫توقف کن‬
‫‪ 3-5‬آزمایش الگوریتم‬
‫‪(1‬‬
‫‪(2‬‬
‫‪(3‬‬
‫‪(4‬‬
‫‪(5‬‬
‫‪(6‬‬
‫‪(7‬‬
‫‪ n‬را بخوان‬
‫‪ i ← 1‬و ‪fact ← 1‬‬
‫تا زمانیکه )‪ ( i ≤ n‬دستورات ‪ 4-5‬را تکرار کن‬
‫‪fact ←fact × i‬‬
‫‪i ←i + 1‬‬
‫‪ fact‬را چاپ کن‬
‫‪n=4‬‬
‫توقف کن‬
‫اجرای الگوریتم ←‬
‫‪i= 4‬‬
‫‪1‬‬
‫‪2‬‬
‫‪3‬‬
‫‪5‬‬
‫‪fact =1‬‬
‫‪2‬‬
‫‪6‬‬
‫‪24‬‬
‫‪ = 24‬خروجی‬
‫‪ 3-6‬چند الگوریتم نمونه‬
‫‪‬‬
‫الگوریتمی بنویسید که ‪ n‬عدد را دریافت و حداکثر و حداقل آنها را چاپ‬
‫کند‪.‬‬
‫‪ n (1‬را بخوان‬
‫‪ adad (2‬را بخوان‬
‫‪ min ← adad (3‬و ‪max ← adad‬‬
‫‪i ← 2 (4‬‬
‫‪ (5‬تا زمانیکه ) ‪ ( i ≤ n‬دستورات ‪ 6‬تا ‪ 8‬را تکرار کن‬
‫‪ adad (6‬را بخوان‬
‫‪ (7‬اگر )‪ ( adad > max‬آنگاه ‪max ← adad‬‬
‫در غیر اینصورت اگر ) ‪ ( adad < min‬آنگاه ‪min ← adad‬‬
‫‪i←i+1‬‬
‫‪(8‬‬
‫‪ max (9‬و ‪ min‬را چاپ کن‬
‫‪ (10‬توقف کن‬
‫‪ 3-6‬چند الگوریتم نمونه‬
‫‪ n (1‬را بخوان‬
‫‪ adad (2‬را بخوان‬
‫‪ min ← adad (3‬و ‪max ← adad‬‬
‫‪i ← 2 (4‬‬
‫‪ (5‬تا زمانیکه ) ‪ ( i ≤ n‬دستورات ‪ 6‬تا ‪ 8‬را تکرار کن‬
‫‪ adad (6‬را بخوان‬
‫‪ (7‬اگر )‪ ( adad > max‬آنگاه ‪max ← adad‬‬
‫در غیر اینصورت اگر ) ‪ ( adad < min‬آنگاه ← ‪min‬‬
‫‪adad‬‬
‫‪n=5‬‬
‫‪i ← i + 1 (8‬‬
‫‪2‬‬
‫‪4‬‬
‫‪3‬‬
‫‪i=6‬‬
‫‪5‬‬
‫‪ max (9‬و ‪ min‬را چاپ کن‬
‫‪39‬‬
‫‪27‬‬
‫‪21‬‬
‫‪6‬‬
‫‪adad = 18‬‬
‫‪ (10‬توقف کن‬
‫‪27‬‬
‫‪39‬‬
‫‪max = 18‬‬
‫اجرای الگوریتم ←‬
‫‪6‬‬
‫‪min = 18‬‬
‫‪ = 39 6‬خروجی‬
‫‪ 3-6‬چند الگوریتم نمونه‬
‫‪‬‬
‫الگوریتمی بنویسید که یک عدد را دریافت و وارون آن را محاسبه و بهمراه‬
‫خود عدد چاپ کند‪ .‬مثال وارون عدد ‪ 3872‬برابر ‪ 2783‬میباشد‪.‬‬
‫‪ adad (1‬را بخوان‬
‫‪ (2‬اگر ) ‪ ( adad < 0‬آنگاه ‪a ← -adad‬‬
‫در غیر اینصورت ‪a ← adad‬‬
‫‪varoon ← 0 (3‬‬
‫‪ (4‬تازمانیکه ) ‪ ( a > 0‬آنگاه دستورات ‪ 5‬تا ‪ 7‬را تکرار کن‬
‫‪remain ← a mod 10 (5‬‬
‫‪a ← a / 10 (6‬‬
‫‪varoon ← varoon × 10 + remain (7‬‬
‫‪ (8‬اگر ) ‪ ( adad < 0‬آنگاه ‪varoon ← -varoon‬‬
‫‪ adad (9‬و ‪ varoon‬را چاپ کن‬
‫‪ (10‬توقف کن‬
‫‪ 3-6‬چند الگوریتم نمونه‬
‫‪ adad (1‬را بخوان‬
‫‪ (2‬اگر ) ‪ ( adad < 0‬آنگاه ‪a ← -adad‬‬
‫در غیر اینصورت ‪a ← adad‬‬
‫‪varoon ← 0 (3‬‬
‫‪ (4‬تازمانیکه ) ‪ ( a > 0‬آنگاه دستورات ‪ 5‬تا ‪ 7‬را تکرار کن‬
‫‪remain ← a mod 10 (5‬‬
‫‪a ← a / 10 (6‬‬
‫‪varoon ← varoon × 10 + remain (7‬‬
‫‪ (8‬اگر ) ‪ ( adad < 0‬آنگاه ‪varoon ← -varoon‬‬
‫‪adad = -275‬‬
‫‪ adad (9‬و ‪ varoon‬را چاپ کن‬
‫‪275‬‬
‫‪27‬‬
‫‪0‬‬
‫‪a=2‬‬
‫‪ (10‬توقف کن‬
‫‪0‬‬
‫‪57‬‬
‫‪-572‬‬
‫‪5‬‬
‫‪varoon =572‬‬
‫اجرای الگوریتم ←‬
‫‪remain = 2‬‬
‫‪5‬‬
‫‪7‬‬
‫‪ = -275 -572‬خروجی‬
‫‪ 3-6‬چند الگوریتم نمونه‬
‫‪ ‬الگوریتمی بنویسید که مقدار ‪ x‬و تعداد جمالت را دریافت و سپس )‪ sin(x‬را‬
‫با استفاده از فرمول زیر تخمین بزند‪:‬‬
‫‪3‬‬
‫‪5‬‬
‫‪7‬‬
‫‪x‬‬
‫‪x‬‬
‫‪x‬‬
‫‪sin(x)  x     ...‬‬
‫!‪3! 5! 7‬‬
‫‪ x (1‬و ‪ n‬را بخوان‬
‫‪ sin ← x (2‬و ‪ fact ← 1‬و ‪ xPower ← x‬و‪sign ← 1‬‬
‫‪i ← 1 (3‬‬
‫‪ (4‬تازمانیکه ) ‪ ( i < n‬دستورات ‪ 5‬تا ‪ 9‬را تکرار کن‬
‫‪fact ← fact × 2i × (2i+1) (5‬‬
‫‪xPower ← xPower × x2 (6‬‬
‫‪sign ← -1 × sign (7‬‬
‫‪sin ← sin + sign × (xPower / fact) (8‬‬
‫‪i ← i + 1 (9‬‬
‫‪ sin (10‬را چاپ کن‬
‫‪ (11‬توقف کن‬
‫‪ 3-6‬چند الگوریتم نمونه‬
‫‪‬‬
‫‪(1‬‬
‫‪(2‬‬
‫‪(3‬‬
‫‪(4‬‬
‫‪(5‬‬
‫‪(6‬‬
‫‪(7‬‬
‫‪(8‬‬
‫الگوریتمی بنویسید که یک عدد را دریافت و تعیین کند که آیا اول است یا‬
‫خیر؟‬
‫‪ adad‬رابخوان‬
‫‪‬‬
‫‪adad‬‬
‫‪‬‬
‫‪root ‬‬
‫‪ i ← 2‬و ‪primeSw ← 1‬‬
‫تازمانیکه ) ‪ ( i ≤ root and primeSw=1‬دستورات ‪ 5‬تا ‪ 6‬را تکرار کن‬
‫اگر ) ‪ ( adad mod i = 0‬آنگاه ‪primeSw ← 0‬‬
‫‪i←i+1‬‬
‫اگر )‪ ( primeSw = 1‬آنگاه چاپ کن "عدد اول است“‬
‫درغیراینصورت چاپ کن "عدد اول نیست"‬
‫توقف کن‬
‫‪ 3-6‬چند الگوریتم نمونه‬
‫‪‬‬
‫‪(1‬‬
‫‪(2‬‬
‫‪(3‬‬
‫‪(4‬‬
‫‪(5‬‬
‫‪(6‬‬
‫‪(7‬‬
‫‪(8‬‬
‫‪(9‬‬
‫الگوریتمی بنویسید که یک عدد صحیح مثبت در مبنای ‪ 10‬را دریافت و‬
‫سپس آن را به مبنای ‪ b‬ببرد‪ .‬مبنای ‪ b‬نیز از کاربر دریافت میگردد‪.‬‬
‫‪ adad‬و ‪ b‬را بخوان‬
‫‪ hasel ← 0‬و ‪i ← 0‬‬
‫تازمانیکه ) ‪ ( adad > 0‬دستورات ‪ 4‬تا ‪ 7‬را تکرار کن‬
‫‪remain ← adad mod b‬‬
‫‪hasel ← hasel + remain × 10 i‬‬
‫‪( adad ← adad / b‬تقسیم صحیح بر صحیح)‬
‫‪i←i+1‬‬
‫‪ hasel‬را چاپ کن‬
‫توقف کن‬
‫‪ 3-6‬چند الگوریتم نمونه‬
‫‪‬‬
‫‪(1‬‬
‫‪(2‬‬
‫‪(3‬‬
‫‪(4‬‬
‫‪(5‬‬
‫‪(6‬‬
‫‪(7‬‬
‫‪(8‬‬
‫‪(9‬‬
‫‪(10‬‬
‫‪(11‬‬
‫‪(12‬‬
‫الگوریتمی بنویسید که برای تعدادی مشترک این اطالعات را بخواند ‪ :‬شماره حساب‪ ،‬موجودی اولیه‪،‬‬
‫تعداد برداشت یا واریز و سپس برای هر برداشت یا واریز این اطالعات را دریافت کند‪:‬‬
‫کد عمل (‪ = 1‬برداشت‪ = 2 ،‬واریز) و مبلغ‪.‬‬
‫در نهایت برای هرمشترک ‪ ،‬شماره حساب و موجودی نهایی را چاپ کند‪.‬‬
‫‪ n‬را بخوان‬
‫‪i←1‬‬
‫تا زمانیکه )‪ ( i ≤ n‬دستورات ‪ 4‬تا ‪ 11‬را تکرار کن‬
‫‪ shomare‬و ‪ mojodi‬و ‪ tedad‬را بخوان‬
‫‪j←1‬‬
‫تا زمانیکه )‪ ( j ≤ tedad‬دستورات ‪ 7‬تا ‪ 9‬را تکرار کن‬
‫‪ code‬و ‪ mablagh‬را بخوان‬
‫اگر )‪ ( code = 1‬آنگاه ‪mojodi ← mojodi – mablagh‬‬
‫درغیراینصورت اگر ) ‪ ( code = 2‬آنگاه ‪mojodi ← mojodi + mablagh‬‬
‫درغیراینصورت چاپ کن "کد اشتباه است"‬
‫‪j←j+1‬‬
‫‪ shomare‬و ‪ mojodi‬را چاپ کن‬
‫‪i←i+1‬‬
‫توقف کن‬
‫‪ 3-6‬چند الگوریتم نمونه‬
‫‪‬‬
‫‪(1‬‬
‫‪(2‬‬
‫‪(3‬‬
‫‪(4‬‬
‫‪(5‬‬
‫‪(6‬‬
‫‪(7‬‬
‫‪(8‬‬
‫‪(9‬‬
‫‪(10‬‬
‫‪(11‬‬
‫‪(12‬‬
‫الگوریتمی بنویسید که معدل و کد جنسیت دانشجویان (پسران=‪ m‬و دختران= ‪ )f‬یک کالس را‬
‫دریافت و در پایان میانگین معدل پسران و میانگین معدل دختران و میانگین معدل کل کالس را چاپ‬
‫کند‪.‬‬
‫‪ n‬را بخوان‬
‫‪ mSum ← 0‬و ‪ mCount ← 0‬و ‪ fSum ← 0‬و ‪fCount ← 0‬‬
‫‪i←1‬‬
‫تا زمانیکه ) ‪ ( i ≤ n‬دستورات ‪ 5‬تا ‪ 7‬را تکرار کن‬
‫‪code‬و ‪ average‬را بخوان‬
‫اگر ) ‪ ( code = f‬آنگاه‬
‫‪ fSum ← fSum + average‬و ‪fCount ← fCount + 1‬‬
‫در غیراینصورت اگر ) ‪ ( code = m‬آنگاه‬
‫‪ mSum ← mSum + average‬و ‪mCount ← mCount + 1‬‬
‫در غیراینصورت چاپ کن "کد اشتباه است" و ‪i ← i – 1‬‬
‫‪i←i+1‬‬
‫اگر ) ‪ ( fCount > 0‬آنگاه ‪fAve ← fSum / fCount‬‬
‫اگر ) ‪ ( mCount > 0‬آنگاه ‪mAve ← mSum / mCount‬‬
‫‪totalAverage ← (mSum + fSum) / n‬‬
‫‪ mAve‬و ‪ fAve‬و ‪ totalAverage‬را چاپ کن‬
‫توقف کن‬
‫‪ 3-6‬چند الگوریتم نمونه‬
‫‪‬‬
‫‪(1‬‬
‫‪(2‬‬
‫‪(3‬‬
‫‪(4‬‬
‫‪(5‬‬
‫‪(6‬‬
‫‪(7‬‬
‫‪(8‬‬
‫‪(9‬‬
‫‪(10‬‬
‫‪(11‬‬
‫‪(12‬‬
‫‪(13‬‬
‫‪(14‬‬
‫‪(15‬‬
‫‪(16‬‬
‫‪(17‬‬
‫الگوریتمی بنویسید که برای تعدادی دانشجو‪ ،‬نام و شماره دانشجویی و تعداد دروس را دریافت کند و سپس برای‬
‫هر دانشجو نمره و تعداد واحد هر درس وی را دریافت و معدل وی را محاسبه نماید‪ .‬در پایان شماره دانشجویی‪ ،‬نام‬
‫و معدل دو دانشجوی برتر کالس را چاپ کند‪.‬‬
‫‪ n‬را بخوان‬
‫‪ maxAve1 ← -1‬و ‪maxAve2 ← -1‬‬
‫‪i←1‬‬
‫تازمانیکه )‪ ( i ≤ n‬دستورات ‪ 5‬تا ‪ 15‬را تکرار کن‬
‫‪ name‬و ‪id‬و ‪tedad‬را بخوان‬
‫‪ sum ← 0‬و ‪sumVahed ← 0‬‬
‫‪j←1‬‬
‫تازمانیکه )‪ ( j ≤ tedad‬دستورات ‪ 9‬تا ‪ 12‬را تکرار کن‬
‫‪ nomre‬و ‪ vahed‬را بخوان‬
‫‪sum ← sum + nomre × vahed‬‬
‫‪sumVahed ← sumVahed + vahed‬‬
‫‪j←j+1‬‬
‫‪average ← sum / sumVahed‬‬
‫اگر ) ‪ ( average > maxAve1‬آنگاه‬
‫‪ maxName2 ← maxName1‬و ‪maxId2 ← maxId1‬‬
‫‪ maxAve2 ← maxAve1‬و ‪maxName1 ← name‬‬
‫‪ maxId1 ← id‬و ‪maxAve1 ← average‬‬
‫درغیراینصورت اگر )‪ ( average > maxAve2‬آنگاه‬
‫‪ maxName2 ← name‬و ‪ maxId2 ← id‬و ‪maxAve2 ← average‬‬
‫‪i←i+1‬‬
‫‪ maxId1‬و ‪ maxName1‬و ‪ maxAve1‬و ‪ maxId2‬و ‪ maxName2‬و ‪ maxAve‬را چاپ کن‬
‫توقف کن‬
‫‪ 3-6‬چند الگوریتم نمونه‬
‫‪‬‬
‫‪(1‬‬
‫‪(2‬‬
‫‪(3‬‬
‫‪(4‬‬
‫‪(5‬‬
‫‪(6‬‬
‫‪(7‬‬
‫‪(8‬‬
‫‪(9‬‬
‫‪(10‬‬
‫‪(11‬‬
‫‪(12‬‬
‫‪(13‬‬
‫‪(14‬‬
‫‪(15‬‬
‫‪(16‬‬
‫‪(17‬‬
‫الگوریتمی بنویسید که برای تعدادی شهر ابتدا نام شهر را دریافت و سپس دمای هوا در ‪ 24‬ساعت گذشته دریافت نماید و میانگین دما‬
‫را برای هر شهر محاسبه و چاپ نماید‪ .‬درپایان موارد زیر را چاپ نماید ‪:‬‬
‫شهری که از همه سردتر بوده است‬
‫شهری که از همه گرمتر بوده است‬
‫تعداد شهرهای سرد (کمتر از ‪ ، )10‬معتدل (بین ‪ 10‬تا ‪ )30‬و گرم (بیشتر از ‪ )30‬به تفکیک‬
‫درضمن تعداد شهرها مشخص نیست و در پایان هر شهر سوال میشود که آیا ادامه بدهیم یا خیر؟‬
‫‪ warmDegree ← -100‬و ‪coldDegree ← 100‬‬
‫‪ coldCount ← 0‬و ‪ warmCount ← 0‬و ‪mediumCount ← 0‬‬
‫تکرار کن‬
‫‪ city‬را بخوان‬
‫‪sum ← 0‬و ‪hour ← 1‬‬
‫تازمانیکه ) ‪ ( hour ≤ 24‬دستورات ‪ 7‬تا ‪ 9‬را تکرار کن‬
‫‪ degree‬را بخوان‬
‫‪sum ← sum + degree‬‬
‫‪hour ← hour + 1‬‬
‫‪sum ← sum / 24‬‬
‫اگر )‪ ( sum > warmDegree‬آنگاه‬
‫‪ warmDegree ← sum‬و ‪warmCity ← city‬‬
‫درغیراینصورت اگر ) ‪ ( sum < coldDegree‬آنگاه‬
‫‪ coldDegree ← sum‬و ‪coldCity ← city‬‬
‫اگر ) ‪ ( sum < 10‬آنگاه ‪coldCount ← coldCount +1‬‬
‫در غیر اینصورت اگر ) ‪ ( sum < 30‬آنگاه‬
‫‪mediumCount ← mediumCount + 1‬‬
‫درغیراینصورت ‪warmCount ← warmCount + 1‬‬
‫چاپ کن "آیا مایل به ادامه هستید ؟"‬
‫‪ answer‬را بخوان‬
‫تا زمانیکه ) ’‪( answer = ‘yes‬‬
‫اطالعات الزم را چاپ کن‬
‫توقف کن‬
‫مبانی کامپیوتر و برنامه سازی‬
‫فصل‌چهارم‌‪ :‬آرایه‌ها‬
‫مدرس‌‪ :‬رضا‌رمضانی‬
‫‪ 4‬متغیرهای اندیسدار یا آرایه ها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫در مثالهایی که در فصل قبل بیان گردید‪ ،‬از متغیرهای معمولی استفاده گردید‪.‬‬
‫اما گاهی نیاز به تعداد زیادی متغیر برای نگاهداری دادهها داریم‪ .‬درچنین‬
‫مواردی نه تنها برای نامگذاری این متغیرها دچار مشکل میشویم‪ ،‬بلکه‬
‫دسترسی به تک تک آنها نیز مشکل است‪.‬‬
‫مثال ) الگوریتمی بنویسید که شماره دانشجویی‪ ،‬نام و معدل تعدادی دانشجو‬
‫را بخواند و مشخصات دانشجویانی را که معدل آنها از میانگین معدل کالس‬
‫بیشتر است را چاپ کند‪.‬‬
‫برای حل این مثال ابتدا باید مشخصات و معدل کلیه دانشجویان دریافت شود تا‬
‫بتوانیم میانگین معدلهای آنان را محاسبه کنیم‪ .‬سپس باید معدل تک تک‬
‫دانشجویان با میانگین کالس مقایسه گردد‪ .‬مسلما نمیتوانیم مجددا از کاربر‬
‫بخواهیم که همان اطالعات قبلی را مجددا وارد کند‪ ،‬بلکه باید از قبل آنها را در‬
‫درون متغیرهایی ذخیره کرده باشیم تا اکنون بتوانیم مقایسه را انجام دهیم‪.‬‬
‫برای اینکار نیاز به تعدادی متغیر (به تعداد دانشجویان مثال ‪ 100‬عدد) داریم که‬
‫ما را دچار ‪ 2‬مشکل اساسی میکند ‪:‬‬
‫‪‬‬
‫‪‬‬
‫این متغیرها را چگونه نامگذاری کنیم ؟‬
‫بر فرض نامگذاری متغیرها برطبق یک روش خاص‪ ،‬چگونه تک تک آنها را با میانگین‬
‫کل مقایسه کنیم؟ آیا باید برای مقایسه هر کدام یک دستور مجزا بنویسیم ؟‬
‫‪ 4-1‬آرایههای یک بعدی‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫تعریف آرایه ‪ :‬مجموعهای از دادههای همنوع است که تحت یک نام‬
‫مشترک ذخیره میگردند‪.‬‬
‫برای دسترسی به هریک از اعضا یا عناصر آرایه از نام آرایه بعالوه یک‬
‫اندیس استفاده میشود‪ .‬بنابراین هر عنصر آرایه درحقیقت یک متغیر‬
‫مستقل از همان نوع مورد نظر است‪.‬‬
‫یک آرایه پیش از آنکه استفاده گردد باید اعالن شود‪ .‬اعالن آرایه شامل نام‬
‫آرایه و اندازه آن است‪ .‬عناصر آرایه برای سهولت در دسترسی (معموال) در‬
‫خانههای پشت سرهم حافظه ذخیره میگردند‪.‬‬
‫مثال) آرایه ‪ A‬را با ‪ 100‬عضو درنظر بگیر‬
‫‪.....‬‬
‫‪.....‬‬
‫)‪A(100‬‬
‫)‪A(i‬‬
‫)‪A(1) A(2) A(3) A(4‬‬
‫‪ 4-1‬آرایههای یک بعدی‬
‫‪‬‬
‫‪(1‬‬
‫‪(2‬‬
‫‪(3‬‬
‫‪(4‬‬
‫‪(5‬‬
‫‪(6‬‬
‫‪(7‬‬
‫‪(8‬‬
‫‪(9‬‬
‫‪(10‬‬
‫‪(11‬‬
‫‪(12‬‬
‫‪(13‬‬
‫‪(14‬‬
‫مثال) الگوریتمی بنویسید که شماره دانشجویی‪ ،‬نام و معدل تعدادی دانشجو را‬
‫بخواند و مشخصات دانشجویانی را که معدل آنها از میانگین معدل کالس بیشتر‬
‫است را چاپ کند‬
‫‪ n‬را بخوان‬
‫آرایههای ‪ idList‬و ‪ nameList‬و ‪ aveList‬را با ‪ n‬عنصر درنظر بگیر‪.‬‬
‫‪ i ← 1‬و ‪sum ← 0‬‬
‫تازمانیکه ) ‪ ( i ≤ n‬دستورات ‪ 5‬تا ‪ 8‬را تکرار کن‬
‫چاپ کن "مشخصات دانشجوی " ‪" ، i ،‬را وارد کنید"‬
‫)‪ idList(i‬و )‪ nameList(i‬و )‪ aveList(i‬را بخوان‬
‫)‪sum ← sum + aveList(i‬‬
‫‪i←i+1‬‬
‫‪totalAve ← sum / n‬‬
‫‪i←1‬‬
‫تازمانیکه ) ‪ ( i ≤ n‬دستورات ‪ 12‬تا ‪ 13‬را تکرار کن‬
‫اگر )‪ ( aveList(i) ≥ totalAve‬آنگاه‬
‫)‪ idList(i‬و )‪ nameList(i‬و )‪ aveList(i‬را چا پ کن‬
‫‪i ← i +1‬‬
‫توقف کن‬
‫‪ 4-1‬آرایههای یک بعدی‬
‫‪‬‬
‫‪(1‬‬
‫‪(2‬‬
‫‪(3‬‬
‫‪(4‬‬
‫‪(5‬‬
‫‪(6‬‬
‫‪(7‬‬
‫‪(8‬‬
‫‪(9‬‬
‫‪(10‬‬
‫‪(11‬‬
‫‪(12‬‬
‫‪(13‬‬
‫‪(14‬‬
‫‪(15‬‬
‫‪(16‬‬
‫‪(17‬‬
‫‪(18‬‬
‫الگوریتمی بنویسید که تعدادی عدد را دریافت و سپس ابتدا اعداد مثبت و سپس اعداد منفی را بطور جداگانه چاپ‬
‫کند‪.‬‬
‫‪ n‬را بخوان‬
‫آرایههای ‪ positive‬و ‪ negative‬را با ‪ n‬عنصر درنظر بگیر‬
‫‪ i ← 1‬و ‪ posCount ← 0‬و ‪negCount ← 0‬‬
‫تازمانیکه ) ‪ ( i ≤ n‬دستورات ‪ 5‬تا ‪ 7‬را تکرار کن‬
‫‪ adad‬را بخوان‬
‫اگر ) ‪ ( adad ≥ 0‬آنگاه‬
‫‪ posCount ← posCount + 1‬و ‪positive(posCount) ← adad‬‬
‫در غیراینصورت ‪ negCount ← negCount + 1‬و ‪negative(negCount) ← adad‬‬
‫‪i←i+1‬‬
‫چاپ کن "لیست اعداد مثبت"‬
‫‪i←1‬‬
‫تازمانیکه ) ‪ ( i ≤ posCount‬دستورات ‪ 11‬تا ‪ 12‬را تکرار کن‬
‫چاپ کن )‪positive(i‬‬
‫‪i←i+1‬‬
‫چاپ کن "لیست اعداد منفی"‬
‫‪i←1‬‬
‫تازمانیکه ) ‪ ( i ≤ negCount‬دستورات ‪ 16‬تا ‪ 17‬را تکرار کن‬
‫چاپ کن )‪negative(i‬‬
‫‪i←i+1‬‬
‫توقف کن‬
‫‪ 4-1‬آرایههای یک بعدی‬
‫‪‬‬
‫‪(1‬‬
‫‪(2‬‬
‫‪(3‬‬
‫‪(4‬‬
‫‪(5‬‬
‫‪(6‬‬
‫‪(7‬‬
‫‪(8‬‬
‫‪(9‬‬
‫‪(10‬‬
‫‪(11‬‬
‫‪(12‬‬
‫‪(13‬‬
‫‪(14‬‬
‫‪(15‬‬
‫‪(16‬‬
‫‪(17‬‬
‫الگوریتمی بنویسید که دو مجموعه از اعداد را خوانده و در دو آرایه قرار دهد‪ .‬سپس اشتراک‬
‫آن دو را محاسبه و در یک آرایه دیگر قرار دهد‪ .‬در پایان اشتراک حاصل را چاپ کند‪.‬‬
‫‪ n‬و ‪ m‬را بخوان‬
‫آرایه ‪ A‬را با ‪ n‬عنصر و آرایه ‪ B‬را با ‪ m‬عنصر در نظر بگیر‪.‬‬
‫آرایه ‪ C‬را با )‪ min(n,m‬عنصر درنظر بگیر‬
‫آرایههای ‪ A‬با ‪ n‬عنصر و ‪ B‬با ‪ m‬عنصر را بخوان (البته نیاز به حلقه دارد)‬
‫‪ aCount ← 1‬و ‪cCount ← 0‬‬
‫تا زمانیکه )‪ ( aCount ≤ n‬دستورات ‪ 7‬تا ‪ 11‬را تکرار کن‬
‫‪ bCount ← 1‬و ‪sw ← 1‬‬
‫تازمانیکه )‪ ( bCount ≤ m and sw = 1‬دستورات ‪ 9‬تا ‪ 10‬را تکرار کن‬
‫اگر ) )‪ ( A(aCount) = B(bCount‬آنگاه‬
‫‪ cCount ← cCount + 1‬و )‪ C(cCount) ← A(aCount‬و ‪sw ← 0‬‬
‫‪bCount ← bCount + 1‬‬
‫‪aCount ← aCount + 1‬‬
‫چاپ کن "اشتراک دو مجموعه برابر است با ‪":‬‬
‫‪i←1‬‬
‫تازمانیکه ) ‪ ( i ≤ cCount‬دستورات ‪ 15‬تا ‪ 16‬را تکرار کن‬
‫)‪ C(i‬را چاپ کن‬
‫‪i←i+1‬‬
‫توقف کن‬
‫‪ 4-1‬آرایههای یک بعدی‬
‫‪‬‬
‫آزمایش الگوریتم اشتراک‬
‫‪C‬‬
‫‪23‬‬
‫‪10‬‬
‫‪35‬‬
‫‪17‬‬
‫‪A‬‬
‫‪B‬‬
‫‪cCount‬‬
‫‪19‬‬
‫‪35‬‬
‫‪23‬‬
‫‪33‬‬
‫‪17‬‬
‫‪21‬‬
‫‪10‬‬
‫‪bCount‬‬
‫‪23‬‬
‫‪45‬‬
‫‪10‬‬
‫‪35‬‬
‫‪8‬‬
‫‪17‬‬
‫‪26‬‬
‫‪12‬‬
‫‪aCount‬‬
‫‪ 4-1‬آرایههای یک بعدی‬
‫‪‬‬
‫‪(1‬‬
‫‪(2‬‬
‫‪(3‬‬
‫‪(4‬‬
‫‪(5‬‬
‫‪(6‬‬
‫‪(7‬‬
‫‪(8‬‬
‫‪(9‬‬
‫‪(10‬‬
‫‪(11‬‬
‫‪(12‬‬
‫‪(13‬‬
‫‪(14‬‬
‫‪(15‬‬
‫‪(16‬‬
‫‪(17‬‬
‫‪(18‬‬
‫‪(19‬‬
‫‪(20‬‬
‫‪(21‬‬
‫‪(22‬‬
‫الگوریتمی مثال قبل را برای اجتماع دو مجموعه تکرار کنید‪.‬‬
‫‪ n‬و ‪ m‬را بخوان‬
‫آرایه ‪ A‬را با ‪ n‬عنصر و آرایه ‪ B‬را با ‪ m‬عنصر در نظر بگیر‪.‬‬
‫آرایه ‪ C‬را با ‪ n+m‬عنصر درنظر بگیر‬
‫آرایههای ‪ A‬با ‪ n‬عنصر و ‪ B‬با ‪ m‬عنصر را بخوان (البته نیاز به حلقه دارد)‬
‫‪i←1‬‬
‫تا زمانیکه ) ‪ ( i ≤ n‬دستورات ‪ 7‬تا ‪ 8‬را تکرار کن‬
‫)‪C(i) ← A(i‬‬
‫‪i←i+1‬‬
‫‪ bCount ← 1‬و ‪cCount ← n‬‬
‫تا زمانیکه )‪ ( bCount ≤ m‬دستورات ‪ 11‬تا ‪ 16‬را تکرار کن‬
‫‪ aCount ← 1‬و ‪sw ← 1‬‬
‫تازمانیکه )‪ ( aCount ≤ n and sw = 1‬دستورات ‪ 13‬تا ‪ 14‬را تکرار کن‬
‫اگر ) )‪ ( A(aCount) = B(bCount‬آنگاه ‪sw ← 0‬‬
‫‪aCount ← aCount + 1‬‬
‫اگر )‪ (sw = 1‬آنگاه‬
‫‪ cCount ← cCount + 1‬و )‪C(cCount) ← B(bCount‬‬
‫‪bCount ← bCount + 1‬‬
‫چاپ کن "اجتماع دو مجموعه برابر است با ‪":‬‬
‫‪i←1‬‬
‫تازمانیکه ) ‪ ( i ≤ cCount‬دستورات ‪ 20‬تا ‪ 21‬را تکرار کن‬
‫)‪ C(i‬را چاپ کن‬
‫‪i←i+1‬‬
‫توقف کن‬
‫‪ 4-1‬آرایههای یک بعدی‬
‫‪ ‬الگوریتمی بنویسید که یک لیست را گرفته و پس از حذف اعداد تکراری آن‪ ،‬حاصل را‬
‫در یک لیست دیگر قرار دهد‪.‬‬
‫‪ n (1‬را بخوان‬
‫‪ (2‬آرایه ‪ A‬و ‪ B‬را با ‪ n‬عنصر درنظر بگیر‬
‫‪ (3‬آرایه ‪ A‬را با ‪ n‬عنصر بخوان‬
‫‪B(1) ← A(1) (4‬‬
‫‪ i ← 2 (5‬و ‪k ← 1‬‬
‫‪ (6‬تا زمانیکه ) ‪ ( i ≤ n‬دستورات ‪ 7‬تا ‪ 11‬را تکرار کن‬
‫‪j ← 1 (7‬‬
‫‪ (8‬تازمانیکه ) )‪ ( j ≤ k and A(i) ≠ B(j‬دستور ‪ 9‬را تکرار کن‬
‫‪j ← j + 1 (9‬‬
‫‪ (10‬اگر )‪ ( j > k‬آنگاه‬
‫‪ k ← k + 1‬و )‪B(k) ← A(i‬‬
‫‪i ← i + 1 (11‬‬
‫‪ (12‬لیست ‪ B‬را با ‪ k‬عضو چاپ کن (نیاز به حلقه دارد)‬
‫‪ (13‬توقف کن‬
‫‪ 4-1‬آرایههای یک بعدی‬
‫‪ ‬الگوریتم مثال قبل را بگونهای تغییر دهید که عملیات حذف دادههای تکراری را‬
‫درخود آرایه اصلی (بدون کمک آرایه دیگر) انجام دهد‪.‬‬
‫‪ n (1‬را بخوان‬
‫‪ (2‬لیست ‪ A‬را با ‪ n‬عنصر در نظر بگیر‬
‫‪ (3‬لیست ‪ A‬را با ‪ n‬عنصر بخوان (نیاز به حلقه دارد)‬
‫‪i ← 1 (4‬‬
‫‪ (5‬تازمانیکه ) ‪ ( i ≤ n‬دستورات ‪ 6‬تا ‪ 9‬را تکرار کن‬
‫‪j ← i + 1 (6‬‬
‫‪ (7‬تازمانیکه ) ‪ ( j ≤ n‬دستور ‪ 8‬را تکرار کن‬
‫‪ (8‬اگر ) )‪ ( A(i) = A(j‬آنگاه‬
‫)‪ A(j) ← A(n‬و ‪n ← n – 1‬‬
‫درغیراینصورت ‪j ← j + 1‬‬
‫‪i ← i + 1 (9‬‬
‫‪ (10‬لیست ‪ A‬را با ‪ n‬عضو چاپ کن‬
‫‪ (11‬توقف کن‬
‫‪ 4-1‬آرایههای یک بعدی‬
‫‪ ‬الگوریتمی بنویسید که یک لیست مرتب بصورت صعودی از اعداد و یک عدد را از‬
‫کاربر دریافت و سپس بدنبال داده در لیست جستجو کند و آن را حذف نماید‪.‬‬
‫‪ n (1‬را بخوان‬
‫‪ (2‬آرایه ‪ A‬را با ‪ n‬عنصر درنظر بگیر‬
‫‪ (3‬آرایه ‪ A‬را با ‪ n‬عنصر بخوان (بصورت مرتب شده)‬
‫‪ x (4‬را بخوان‬
‫‪i ← 1 (5‬‬
‫‪ (6‬تازمانیکه ) )‪ ( i ≤ n and x≠A(i‬دستور ‪ 7‬را تکرار کن‬
‫‪i ← i + 1 (7‬‬
‫‪ (8‬اگر ) ‪ ( i > n‬چاپ کن "عدد پیدا نشد" و توقف کن‬
‫‪ (9‬تازمانیکه ) ‪ ( i < n‬دستورات ‪ 10‬تا ‪ 11‬را تکرار کن‬
‫‪A(i) ← A(i + 1) (10‬‬
‫‪i ← i + 1 (11‬‬
‫‪n ← n -1 (12‬‬
‫‪ (13‬آرایه ‪ A‬را با ‪ n‬عضو چاپ کن‬
‫‪ (14‬توقف کن‬
‫‪ 4-1‬آرایههای یک بعدی‬
‫‪‬‬
‫آزمایش الگوریتم حذف از لیست مرتب‬
‫‪A‬‬
‫‪n=9‬‬
‫‪10‬‬
‫‪x = 38‬‬
‫‪12‬‬
‫‪18‬‬
‫‪21‬‬
‫‪35‬‬
‫‪38‬‬
‫‪42‬‬
‫‪44‬‬
‫‪48‬‬
‫‪52‬‬
‫‪58‬‬
‫‪i‬‬
‫‪ 4-1‬آرایههای یک بعدی‬
‫‪ ‬الگوریتمی بنویسید که یک لیست مرتب بصورت صعودی از اعداد را از کاربر‬
‫دریافت و سپس با دریافت یک عدد جدید از کاربر آن را درمکان مناسب لیست‬
‫درج کند بطوریکه ترتیب حفظ شود‪.‬‬
‫‪ n (1‬را بخوان‬
‫‪ (2‬آرایه ‪ A‬را با ‪ n + 1‬عنصر درنظر بگیر‬
‫‪ (3‬آرایه ‪ A‬را با ‪ n‬عنصر بخوان (بصورت مرتب شده)‬
‫‪ x (4‬را بخوان‬
‫‪i ← n (5‬‬
‫‪ (6‬تازمانیکه ) )‪ ( i ≥ 1 and x < A(i‬دستورات ‪ 7‬تا ‪ 8‬را تکرار کن‬
‫‪A(i + 1) ← A(i) (7‬‬
‫‪i ← i – 1 (8‬‬
‫‪A(i + 1) ← x (9‬‬
‫‪n ← n + 1 (10‬‬
‫‪ (11‬آرایه ‪ A‬را با ‪ n‬عضو چاپ کن‬
‫‪ (12‬توقف کن‬
‫‪ 4-1‬آرایههای یک بعدی‬
‫‪‬‬
‫آزمایش الگوریتم درج در لیست مرتب‬
‫‪A‬‬
‫‪n = 10‬‬
‫‪11‬‬
‫‪x = 39‬‬
‫‪12‬‬
‫‪18‬‬
‫‪21‬‬
‫‪35‬‬
‫‪38‬‬
‫‪39‬‬
‫‪42‬‬
‫‪44‬‬
‫‪48‬‬
‫‪52‬‬
‫‪58‬‬
‫‪i‬‬
‫‪ 4-1‬آرایههای یک بعدی‬
‫‪ ‬الگوریتمی بنویسید که یک لیست از اعداد را دریافت و عددی که بیشترین تکرار را دارد چاپ کند‪.‬‬
‫‪ n (1‬را بخوان‬
‫‪ (2‬آرایههای ‪ A‬و ‪ sw‬را با ‪ n‬عضو درنظر بگیر‬
‫‪ (3‬آرایه ‪ A‬را با ‪ n‬عضو بخوان‬
‫‪ (4‬تمام عناصر آرایه ‪ sw‬را برابر ‪ 0‬قرار بده‬
‫‪ mod ← 0 (5‬و ‪modNo ← 0‬‬
‫‪i ← 1 (6‬‬
‫‪ (7‬تازمانیکه )‪ ( i ≤ n‬دستورات ‪ 8‬تا ‪ 9‬را تکرار کن‬
‫‪ (8‬اگر ) ‪ ( sw(i) = 0‬آنگاه‬
‫‪sw(i) ← 1 .a‬‬
‫‪ j ← i + 1 .b‬و ‪sum ← 1‬‬
‫‪ .c‬تازمانیکه ) ‪ ( j ≤ n‬دستورات ‪ d‬تا ‪ e‬را تکرار کن‬
‫‪ .d‬اگر ) )‪ ( A(i) = A(j‬آنگاه‬
‫‪ sum ← sum + 1‬و ‪sw(j) ← 1‬‬
‫‪j ← j + 1 .e‬‬
‫‪ .f‬اگر )‪ ( sum > modNo‬آنگاه‬
‫)‪ mod ← A(i‬و ‪modNo ← sum‬‬
‫‪i ← i + 1 (9‬‬
‫‪ mod (10‬و ‪ modNo‬را چاپ کن‬
‫‪ (11‬توقف کن‬
‫‪ 4-1‬آرایههای یک بعدی‬
‫‪ ‬الگوریتمی بنویسید که برای تعداد دانشجو‪ ،‬کد رشته (از ‪ 1‬تا ‪ )15‬را خوانده‬
‫و سپس تعداد دانشجویان هررشته را به تفکیک چاپ کند‪.‬‬
‫‪ (1‬آرایه ‪ count‬را با ‪ 15‬عضو درنظر بگیر‬
‫‪ (2‬تمام عناصر آرایه ‪count‬را برابر ‪ 0‬قرار بده‬
‫‪ n (3‬را بخوان‬
‫‪i ← 1 (4‬‬
‫‪ (5‬تازمانیکه )‪ ( i ≤ n‬دستورات ‪ 6‬تا ‪ 8‬را تکرار کن‬
‫‪ code (6‬را بخوان‬
‫‪count(code) ← count(code) + 1 (7‬‬
‫‪i ← i + 1 (8‬‬
‫‪ (9‬آرایه ‪ count‬را با ‪ 15‬عضو چاپ کن‬
‫‪ (10‬توقف کن‬
‫‪ 4-2‬آرایههای چندبعدی‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫مسائلی که تاکنون حل شدند نیاز به آرایههای یک بعدی داشتند‪ .‬هر عنصر‬
‫از این آرایهها تنها با یک اندیس مشخص میگردد‪.‬‬
‫اما گاهی در مسائل پیچیده تر نیاز به آرایههایی است که هر عضو آنها نیاز‬
‫به بیش از یک اندیس دارد‪ ،‬که به آنها آرایههای چند بعدی گفته میشود‪.‬‬
‫چنانچه هر عنصر آرایه به ‪ 2‬اندیس نیاز داشته باشد‪ ،‬به آن آرایه دو بعدی‬
‫میگوییم‪.‬‬
‫برای تعریف یک آرایه دوبعدی باید تعداد سطرها و ستونهای آن را مشخص‬
‫کنیم‪ .‬معموال یک آرایه دو بعدی بصورت ‪ m × n‬اعالن میگردد که ‪ m‬تعداد‬
‫سطرها و ‪ n‬تعداد ستونها است‪.‬‬
‫‪ 4-2‬آرایههای چندبعدی‬
‫‪‬‬
‫بعنوان مثال چنانچه آرایه ‪ A‬را بعنوان یک آرایه دوبعدی به ابعاد ‪ 5 × 8‬تعریف‬
‫کنیم‪ ،‬آنگاه داریم ‪:‬‬
‫ستون‬
‫‪8‬‬
‫‪7‬‬
‫‪6‬‬
‫‪5‬‬
‫‪4‬‬
‫‪3‬‬
‫‪2‬‬
‫‪1‬‬
‫‪1‬‬
‫)‪A(2,7‬‬
‫‪2‬‬
‫‪3‬‬
‫‪4‬‬
‫‪5‬‬
‫)‪A(3,4‬‬
‫سطر‬
‫‪ 4-2‬آرایههای چندبعدی‬
‫‪‬‬
‫‪‬‬
‫برای آرایههای سه بعدی نیز مفاهیم مشابهی قابل طرح است‪ .‬در این آرایهها هر‬
‫عنصر نیاز به ‪ 3‬اندیس دارد و برای تعریف آنها را بصورت ‪ p × m × n‬اعالن میکنیم‬
‫که ‪ p‬عمق‪ m ،‬تعداد سطرها و ‪ n‬تعداد ستونها است‪.‬‬
‫بعنوان مثال چنانچه آرایه ‪ B‬بعنوان یک آرایه سه بعدی به ابعاد ‪ 3 × 4 × 6‬تعریف‬
‫شود‪ ،‬خواهیم داشت ‪:‬‬
‫)‪B(2,1,4‬‬
‫عمق‬
‫)‪B(1,2,6‬‬
‫سطر‬
‫ستون‬
‫‪ 4-2‬آرایههای چندبعدی‬
‫‪‬‬
‫‪(1‬‬
‫‪(2‬‬
‫‪(3‬‬
‫‪(4‬‬
‫‪(5‬‬
‫‪(6‬‬
‫‪(7‬‬
‫‪(8‬‬
‫‪(9‬‬
‫‪(10‬‬
‫‪(11‬‬
‫‪(12‬‬
‫‪(13‬‬
‫‪(14‬‬
‫‪(15‬‬
‫‪(16‬‬
‫‪(17‬‬
‫‪(18‬‬
‫‪(19‬‬
‫الگوریتمی بنویسید که برای تعدادی دانشجو‪ ،‬شماره دانشجویی و کد رشته (از ‪ 1‬تا ‪ )15‬را خوانده و سپس‬
‫دانشجویان هررشته را به تفکیک چاپ کند‪.‬‬
‫‪ n‬را بخوان‬
‫آرایه دوبعدی ‪ student‬را به ابعاد ‪ 15 × n‬در نظر بگیر‬
‫آرایه ‪ count‬را با ‪ 15‬عنصر در نظر بگیر و تمام عناصر آن را برابر ‪ 0‬قرار بده‬
‫‪i←1‬‬
‫تازمانیکه ) ‪ ( i ≤ n‬دستورات ‪ 6‬تا ‪ 9‬را تکرار کن‬
‫‪ id‬و ‪ code‬را بخوان‬
‫‪count(code) ← count(code) + 1‬‬
‫‪student( code , count(code) ) ← id‬‬
‫‪i←i+1‬‬
‫چاپ کن "لیست دانشجویان"‬
‫‪i←1‬‬
‫تازمانیکه )‪ ( i ≤ 15‬دستورات ‪ 13‬تا ‪ 18‬را تکرار کن‬
‫چاپ کن "رشته "‪i ،‬‬
‫‪j←1‬‬
‫تازمانیکه ) )‪ ( j ≤ count(i‬دستورات ‪ 16‬تا ‪ 17‬را تکرار کن‬
‫) ‪ student( i , j‬را چاپ کن‬
‫‪j←j+1‬‬
‫‪i←i+1‬‬
‫توقف کن‬
‫‪ 4-2‬آرایههای چندبعدی‬
‫‪‬‬
‫‪(1‬‬
‫‪(2‬‬
‫‪(3‬‬
‫‪(4‬‬
‫‪(5‬‬
‫‪(6‬‬
‫‪(7‬‬
‫‪(8‬‬
‫‪(9‬‬
‫‪(10‬‬
‫‪(11‬‬
‫‪(12‬‬
‫‪(13‬‬
‫‪(14‬‬
‫‪(15‬‬
‫‪(16‬‬
‫‪(17‬‬
‫‪(18‬‬
‫‪(19‬‬
‫‪(20‬‬
‫‪(21‬‬
‫‪(22‬‬
‫‪(23‬‬
‫الگوریتمی بنویسید که برای تعدادی دانشجو‪ ،‬شماره دانشجویی و کد محل تحصیل (از ‪ 1‬تا ‪ )10‬و کد رشته (از ‪ 1‬تا ‪ )15‬را‬
‫خوانده و سپس دانشجویان هر رشته را به تفکیک چاپ کند‪.‬‬
‫‪ n‬را بخوان‬
‫آرایه سه بعدی ‪ student‬را به ابعاد ‪ 10 × 15 × n‬در نظر بگیر‬
‫آرایه ‪ count‬را با ‪ 10 × 15‬عنصر در نظر بگیر و تمام عناصر آن را برابر ‪ 0‬قرار بده‬
‫‪i←1‬‬
‫تازمانیکه ) ‪ ( i ≤ n‬دستورات ‪ 6‬تا ‪ 9‬را تکرار کن‬
‫‪ id‬و ‪ cityCode‬و ‪ studyCode‬را بخوان‬
‫‪count(cityCode,studyCode) ← count(cityCode,studyCode) + 1‬‬
‫‪student( cityCode , studyCode , count(cityCode , studyCode) ) ← id‬‬
‫‪i←i+1‬‬
‫چاپ کن "لیست دانشجویان"‬
‫‪i←1‬‬
‫تازمانیکه )‪ ( i ≤ 10‬دستورات ‪ 13‬تا ‪ 22‬را تکرار کن‬
‫چاپ کن "محل "‪i ،‬‬
‫‪j←1‬‬
‫تازمانیکه ) ‪ ( j ≤ 15‬دستورات ‪ 16‬تا ‪ 21‬را تکرار کن‬
‫چاپ کن "کد رشته "‪j ،‬‬
‫‪k←1‬‬
‫تازمانیکه ) )‪ ( k ≤ count(i,j‬دستورات ‪ 19‬تا ‪ 20‬را چاپ کن‬
‫)‪ student( i , j , k‬را چاپ کن‬
‫‪k←k+1‬‬
‫‪j←j+1‬‬
‫‪i←i+1‬‬
‫توقف کن‬
‫‪ 4-2‬آرایههای چندبعدی‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪ 50‬موضوع مختلف را به رای گذاشته ایم و هرکس میتواند آرای خود را بشرح زیر اعالم‬
‫کند‪:‬‬
‫‪ -1‬موافق ‪ -2‬مخالف ‪ -3‬ممتنع ‪ -4‬بی اطالع‬
‫الگوریتمی بنویسید که تعداد افراد رای دهنده را دریافت و سپس پس از دریافت آرای‬
‫هریک از آنان برای هر ‪ 50‬موضوع‪ ،‬تعداد آرای مختلف هر موضوع را به تفکیک چاپ کند‪.‬‬
‫‪ n‬را بخوان‬
‫‪i←1‬‬
‫تازمانیکه ) ‪ ( i ≤ n‬دستورات را ‪ 5‬تا ‪ 11‬تکرار کن‬
‫‪j←1‬‬
‫تازمانیکه ) ‪ ( j ≤ 50‬دستورات ‪ 7‬تا ‪ 10‬را تکرار کن‬
‫چاپ کن "موضوع " ‪j ،‬‬
‫‪ vote‬را بخوان‬
‫‪count(j , vote) ← count(j , vote) + 1‬‬
‫‪j←j+1‬‬
‫‪i←i+1‬‬
‫آرایه ‪ count‬را به ابعاد ‪ 50 × 4‬چاپ کن (نیاز به حلقه دارد)‬
‫توقف کن‬
‫‪ 4-2‬آرایههای چندبعدی‬
‫‪‬‬
‫‪(1‬‬
‫‪(2‬‬
‫‪(3‬‬
‫‪(4‬‬
‫‪(5‬‬
‫‪(6‬‬
‫‪(7‬‬
‫‪(8‬‬
‫‪(9‬‬
‫‪(10‬‬
‫‪(11‬‬
‫‪(12‬‬
‫‪(13‬‬
‫‪(14‬‬
‫‪(15‬‬
‫‪(16‬‬
‫‪(17‬‬
‫‪(18‬‬
‫‪(19‬‬
‫‪(20‬‬
‫الگوریتمی بنویسید که دو ماتریس را از کاربر دریافت و حاصلضرب آن دو را محاسبه و چاپ کند‪.‬‬
‫‪ m‬و ‪ n‬را بخوان (ابعاد ماتریس اول ‪)m × n‬‬
‫‪ p‬و ‪ q‬را بخوان (ابعاد ماتریس دوم ‪)p × q‬‬
‫اگر ) ‪ ( n ≠ p‬آنگاه‬
‫چاپ کن "ماتریسها قابل ضرب نیستند" و توقف کن‬
‫آرایه ‪ A‬را به ابعاد ‪ m × n‬و آرایه ‪ B‬را به ابعاد ‪ p × q‬درنظر بگیر‬
‫آرایه ‪ C‬را به ابعاد ‪ m × q‬درنظر بگیر‬
‫آرایه ‪ A‬را به ابعاد ‪ m × n‬بخوان‬
‫آرایه ‪ B‬را به ابعاد ‪ p × q‬بخوان‬
‫‪i←1‬‬
‫تازمانیکه )‪ ( i ≤ m‬دستورات ‪ 10‬تا ‪ 18‬را تکرار کن‬
‫‪j←1‬‬
‫تازمانیکه )‪ ( j ≤ q‬دستورات ‪ 12‬تا ‪ 17‬را تکرار کن‬
‫‪ sum ← 0‬و ‪k ← 1‬‬
‫تازمانیکه )‪ (k ≤ n‬دستورات ‪ 14‬تا ‪ 15‬را تکرار کن‬
‫)‪sum ← sum + A(i,k) × B(k,j‬‬
‫‪k←k+1‬‬
‫‪C(i,j) ← sum‬‬
‫‪j←j+1‬‬
‫‪i←i+1‬‬
‫آرایه ‪ C‬را به ابعاد ‪m × q‬چاپ کن‬
‫توقف کن‬
‫‪ 4-2‬آرایههای چندبعدی‬
‫‪‬‬
‫‪(1‬‬
‫‪(2‬‬
‫‪(3‬‬
‫‪(4‬‬
‫‪(5‬‬
‫‪(6‬‬
‫‪(7‬‬
‫‪(8‬‬
‫‪(9‬‬
‫‪(10‬‬
‫‪(11‬‬
‫‪(12‬‬
‫‪(13‬‬
‫‪(14‬‬
‫‪(15‬‬
‫‪(16‬‬
‫‪(17‬‬
‫‪(18‬‬
‫‪(19‬‬
‫‪(20‬‬
‫‪(21‬‬
‫‪(22‬‬
‫‪(23‬‬
‫دریک آزمون دانشجویان باید به ‪ 20‬سوال ‪ 4‬گزینهای جواب بدهند‪ .‬الگوریتمی بنویسید که ابتدا جواب درست سواالت را دریافت و سپس‬
‫برای تعدادی دانشجو برگه پاسخنامه را دریافت و نمره آنها را محاسبه و چاپ کند‪ .‬پاسخنامهها در یک آرایه دوبعدی ‪ 20 × 4‬است که در‬
‫جاهایی که دانشجو عالمت زده است کاراکتر ‪ X‬قرار گرفته است و سایر مکانها خالی است‪ .‬هر پاسخ غلط ⅓ نمره منفی دارد‪.‬‬
‫آرایه ‪ correct‬را با ‪ 20‬عضو درنظر بگیر‬
‫آرایه ‪ answers‬را به ابعاد ‪ 20 × 4‬در نظر بگیر‬
‫‪i←1‬‬
‫تازمانیکه )‪ (i ≤ 20‬دستورات ‪ 5‬تا ‪ 7‬را تکرار کن‬
‫چاپ کن "پاسخ درست سوال"‪i ،‬‬
‫)‪ correct(i‬را بخوان‬
‫‪i←i+1‬‬
‫‪ n‬را بخوان‬
‫‪i←1‬‬
‫تازمانیکه )‪ ( i ≤ n‬دستورات ‪ 11‬تا ‪ 22‬را تکرار کن‬
‫آرایه ‪ answers‬را بخوان (از داخل فایل یا ورودی صفحه کلید)‬
‫‪grade ← 0‬‬
‫‪j←1‬‬
‫تازمانیکه )‪ ( j ≤ 20‬دستورات ‪ 15‬تا ‪ 20‬را تکرار کن‬
‫‪ k ← 1‬و ‪count ← 0‬‬
‫تازمانیکه )‪ (k ≤ 4‬دستورات ‪ 17‬تا ‪ 18‬را تکرار کن‬
‫اگر ) ’‪ ( answers(j , k) = ‘X‬آنگاه ‪count ← count + 1‬‬
‫‪k←k+1‬‬
‫اگر )‪ ( count > 1‬آنگاه ‪grade ← grade – 1/3‬‬
‫درغیراینصورت اگر )‪ (count = 1‬آنگاه‬
‫اگر )’‪grade ← grade + 1 ( answers(j , correct(j) ) = ‘X‬‬
‫درغیراینصورت ‪grade ← grade – 1/3‬‬
‫‪j←j+1‬‬
‫چاپ کن "نمره این دانشجو برابر است با "‪grade ،‬‬
‫‪i←i+1‬‬
‫توقف کن‬
‫مبانی کامپیوتر و برنامه سازی‬
‫فصل‌پنجم‌‪ :‬زیرالگوریتمها‬
‫مدرس‌‪ :‬رضا‌رمضانی‬
‫‪ 5‬زیرالگوریتمها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫در الگوریتمهای نوشته شده تا کنون‪ ،‬بعضی اعمال مانند خواندن یک آرایه دوبعدی و یا‬
‫چاپ آن مرتبا مورد استفاده قرار میگرفتند‪.‬‬
‫این قبیل الگوریتمها را میتوان یکبار نوشت و چندین بار مورد استفاده قرار داد‪ ،‬که به آنها‬
‫زیرالگوریتم گفته میشود‪.‬‬
‫درحقیقت زیرالگوریتم یک قطعه الگوریتم کمکی است که دادههایی را بعنوان ورودی از‬
‫الگوریتم اصلی دریافت و پس از انجام پردازش برروی آنها‪ ،‬داده یا دادههایی را بعنوان‬
‫خروجی باز میگرداند‪ .‬هر زیرالگوریتم دارای یک نام است که الگوریتم اصلی میتواند آن‬
‫را توسط نامش فراخوانی نماید‪.‬‬
‫زیرالگوریتم دارای مزایای متعدی است که اهم آنها عبارتند از ‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫جلوگیری از تکرار الگوریتمهایی که مرتبا مورد استفاده قرار میگیرند‪.‬‬
‫ساده شدن عیب یابی و اشکالزدایی الگوریتم‬
‫باال رفتن خوانایی برنامه‬
‫امکان تقسیم کار به چند بخش و واگذاری آن به افراد مختلف‬
‫هر الگوریتم میتواند برای تبادل اطالعات با زیرالگوریتم‪ ،‬تعدادی از متغیرهای خود را به‬
‫زیرالگوریتم ارسال کند و یا دادههایی را از آن دریافت کند‪ .‬متغیرهایی را که برای تبادل‬
‫اطالعات بین الگوریتم و زیرالگوریتم بکار میروند را پارامتر میگوییم‪ .‬بنابراین پارامترها‬
‫میتوانند ورودی (به زیرالگوریتم) یا خروجی (از زیرالگوریتم) باشند‪ .‬هر زیرالگوریتم‬
‫میتواند هر تعداد پارامتر ورودی یا خروجی داشته باشد و یا میتواند اصال پارامتر‬
‫نداشته باشد‪.‬‬
‫‪ 5-1‬نحوه استفاده از زیرالگوریتم‬
‫‪‬‬
‫استفاده از یک زیرالگوریتم دارای دو مرحله است ‪:‬‬
‫‪‬‬
‫‪‬‬
‫تعریف زیرالگوریتم‪ :‬برای تعریف زیرالگوریتم ابتدا نام آن و سپس در داخل پرانتز‬
‫لیست پارامترهای آن را مشخص مینماییم‪ .‬پس از آن نیز دستورات تشکیل‬
‫دهنده زیرالگوریتم را مینویسیم‪ .‬الزم به ذکر است که زیرالگوریتم حتما باید‬
‫دارای دستور برگشت باشد‪ .‬این دستور باعث میشود که کنترل اجرا به‬
‫الگوریتم اصلی بازگردد‪.‬‬
‫فراخوانی زبرالگوریتم‪ :‬پس از تعریف یک زیرالگوریتم‪ ،‬میتوان آن را از هر‬
‫نقطهای از الگوریتم اصلی فراخوانی کرد‪ .‬فراخوانی شامل نام زیرالگوریتم و‬
‫سپس در داخل پرانتز لیست پارامترهای ارسالی و دریافتی به زیرالگوریتم‬
‫میباشد‪.‬‬
‫‪ 5-2‬انواع زیرالگوریتم‬
‫‪‬‬
‫زیرالگوریتمها به سه دسته اصلی تقسیم میگردند ‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫زیرالگوریتمهایی که مقدار خروجی ندارند‪.‬‬
‫زیرالگوریتمهایی که یک مقدار خروجی دارند‪ .‬معموال در این قبیل‬
‫زیرالگوریتمها‪ ،‬بجای اینکه از یک پارامتر خروجی استفاده شود‪ ،‬نام خود‬
‫زیرالگوریتم نماینده مقدار بازگشتی است‪ .‬اینکار در ریاضیات معمولی نیز‬
‫رایج است‪ .‬مثال وقتی از عبارت )‪ a = 2 × sin(30‬استفاده میکنیم‪ ،‬خود نام‬
‫تابع یعنی )‪ sin(30‬جایگزین مقدار حاصل یعنی ‪ 2/1‬شده است‪.‬‬
‫زیر الگوریتمهایی که چندین مقدار باز میگردانند‪ .‬این زیر الگوریتمها از‬
‫پارامترهای خروجی برای بازگرداندن مقادیر استفاده میکنند‪ .‬البته برای‬
‫بازگرداندن یک مقدار نیز میتوان از پارامترها استفاده کرد ولی اینکار متداول‬
‫نیست‪.‬‬
‫‪ 5-3‬چند نمونه از زیرالگوریتمها‬
‫‪‬‬
‫الگوریتمی بنویسید که مقدار ترکیب ‪ n‬به ‪ k‬را محاسبه کند‪.‬‬
‫‪ n‬‬
‫!‪n‬‬
‫‪  ‬‬
‫!) ‪ k  k!(n  k‬‬
‫زیرالگوریتم )‪Factorial(x‬‬
‫‪ i ← 1 (1‬و‌‪F ← 1‬‬
‫‪ (2‬تازمانیکه‌( ‪ ) i ≤ x‬دستورات‌‪ 3‬تا‌‪ 4‬را‌تکرار‌کن‬
‫‪F←F×i‬‬
‫‪(3‬‬
‫‪i ← i + 1 (4‬‬
‫‪Factorial ← F (5‬‬
‫‪ (6‬برگشت‬
‫‪ 5-3‬چند نمونه از زیرالگوریتمها‬
‫الگوریتم اصلی‬
‫‪(1‬‬
‫‪(2‬‬
‫‪(3‬‬
‫‪(4‬‬
‫‪(5‬‬
‫‪(6‬‬
‫‪ n‬و ‪ k‬را بخوان‬
‫اگر ) ‪ ( n < k‬چاپ کن "مسئله جواب ندارد" و توقف کن‬
‫اگر )‪ ( n=k or k=0‬چاپ کن "جواب = ‪ "1‬و توقف کن‬
‫) )‪result ← Factorial(n) / ( Factorial(k) × Factorial(n-k‬‬
‫‪ result‬را چاپ کن‬
‫توقف کن‬
‫‪ 5-3‬چند نمونه از زیرالگوریتمها‬
‫‪‬‬
‫روش دیگر حل این مسئله با استفاده از یک پارامتر خروجی‬
‫زیرالگوریتم )‪Factorial(x,f‬‬
‫‪ i ← 1 (1‬و ‪f ← 1‬‬
‫‪ (2‬تازمانیکه ) ‪ ( i ≤ x‬دستورات ‪ 3‬تا ‪ 4‬را تکرار کن‬
‫‪f←f×i‬‬
‫‪(3‬‬
‫‪i ← i + 1 (4‬‬
‫‪ (5‬برگشت‬
‫‪ 5-3‬چند نمونه از زیرالگوریتمها‬
‫الگوریتم اصلی‬
‫‪(1‬‬
‫‪(2‬‬
‫‪(3‬‬
‫‪(4‬‬
‫‪(5‬‬
‫‪(6‬‬
‫‪(7‬‬
‫‪(8‬‬
‫‪(9‬‬
‫‪ n‬و ‪ k‬را بخوان‬
‫اگر ) ‪ ( n < k‬چاپ کن "مسئله جواب ندارد" و توقف کن‬
‫اگر ) ‪ ( n=k or k=1‬چاپ کن "جواب = ‪ "1‬و توقف کن‬
‫)‪Factorial(n,fn‬‬
‫)‪Factorial(k,fk‬‬
‫)‪Factorial(n-k,fnk‬‬
‫)‪result ← fn / (fk × fnk‬‬
‫‪ result‬را چاپ کن‬
‫توقف کن‬
‫‪ 5-3‬چند نمونه از زیرالگوریتمها‬
‫‪‬‬
‫الگوریتمی بنویسید که تعدادی عدد را دریافت و هربار با فراخوانی یک‬
‫زیرالگوریتم عدد را در مکان مناسب یک آرایه درج کند بطوریکه در انتها‬
‫آرایه مرتب باشد‪.‬‬
‫زیرالگوریتم )‪insert(A,t,x‬‬
‫‪i ← t (1‬‬
‫‪ (2‬تازمانیکه ) )‪ ( i ≥ 1 and x < A(i‬دستورات ‪ 3‬تا ‪ 4‬را تکرار کن‬
‫‪A(i + 1) ← A(i) (3‬‬
‫‪i ← i – 1 (4‬‬
‫‪A(i + 1) ← x (5‬‬
‫‪ (6‬برگشت‬
‫‪ 5-3‬چند نمونه از زیرالگوریتمها‬
‫الگوریتم اصلی‬
‫‪(1‬‬
‫‪(2‬‬
‫‪(3‬‬
‫‪(4‬‬
‫‪(5‬‬
‫‪(6‬‬
‫‪(7‬‬
‫‪(8‬‬
‫‪(9‬‬
‫‪ n‬رابخوان‬
‫آرایه ‪ List‬را با ‪ n‬عضو درنظر بگیر‬
‫‪i←1‬‬
‫تازمانیکه ) ‪ ( i ≤ n‬دستورات ‪ 5‬تا ‪ 7‬را تکرار کن‬
‫‪ adad‬را بخوان‬
‫)‪insert(List, i-1, adad‬‬
‫‪i←i+1‬‬
‫آرایه ‪ List‬را با ‪ n‬عضو چاپ کن‬
‫توقف کن‬
‫‪ 5-3‬چند نمونه از زیرالگوریتمها‬
‫‪‬‬
‫الگوریتمی بنویسید که لیستی از اعداد را دریافت و در یک آرایه قرار دهد‪.‬‬
‫سپس با دریافت هرعدد از کاربر تعیین کند که آیا این عدد در لیست‬
‫موجود بوده یا خیر و درصورت وجود مکان آن را چاپ کند‪ .‬در پایان اعداد‪،‬‬
‫عدد صفر قرار گرفته است‪.‬‬
‫زیرالگوریتم )‪Search(A, t, x‬‬
‫‪i ← 1 (1‬‬
‫‪ (2‬تازمانیکه ) ‪ ( i ≤ t‬دستورات ‪ 3‬تا ‪ 4‬را تکرار کن‬
‫‪ (3‬اگر )‪ ( A(i) = x‬آنگاه ‪ Search ← i‬و برگشت‬
‫‪i ← i + 1 (4‬‬
‫‪Search ← 0 (5‬‬
‫‪ (6‬برگشت‬
‫‪ 5-3‬چند نمونه از زیرالگوریتمها‬
‫الگوریتم اصلی‬
‫‪(1‬‬
‫‪(2‬‬
‫‪(3‬‬
‫‪(4‬‬
‫‪(5‬‬
‫‪(6‬‬
‫‪(7‬‬
‫‪(8‬‬
‫‪(9‬‬
‫‪ n‬را بخوان‬
‫آرایه ‪ List‬را با ‪ n‬عضو درنظر بگیر‬
‫آرایه ‪ List‬را با ‪ n‬عضو بخوان‬
‫‪ adad‬را بخوان‬
‫تازمانیکه ) ‪ ( adad ≠ 0‬دستورات ‪ 6‬تا ‪ 8‬را تکرار کن‬
‫)‪place = Search(List, n , adad‬‬
‫اگر ) ‪ ( place = 0‬آنگاه چاپ کن "عدد در لیست موجود نیست"‬
‫درغیراینصورت چاپ کن "مکان عدد = "‪place ،‬‬
‫‪ adad‬را بخوان‬
‫توقف کن‬
‫مبانی کامپیوتر و برنامه سازی‬
‫فصل‌ششم‌‪ :‬مقدمه‌ای بر‌زبان‌‪C‬‬
‫مدرس‌‪ :‬رضا‌رمضانی‬
‫‪ 6‬مقدمهای بر زبان ‪C‬‬
‫‪‬‬
‫زبانهای برنامه سازی به سه دسته کلی تقسیم میگردند ‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫زبان ماشین (سطح پایین) ‪ :‬این زبان مستقیما با صفر و یک نوشته میشود و بدون هیچ‬
‫واسطهای برروی کامپیوتر قابل اجرا است‪ .‬هر برنامهای که به زبان ماشین نوشته شود‪،‬‬
‫فقط برروی همان ماشین خاص کار میکند‪ ،‬بهمین دلیل برنامههای نوشته شده به زبان‬
‫ماشین را غیر قابل حمل مینامند‪ .‬از طرف دیگر یادگیری این زبان بسیار مشکل بوده و‬
‫برنامهنویسی با آن نیز بسیار سخت است و همچنین احتمال بروز خطا نیز در آن زیاد‬
‫است‪.‬‬
‫زبان اسمبلی ‪ :‬این زبان شکل ساده تر زبان ماشین است‪ ،‬بدین صورت که برای هر‬
‫دستورالعمل زبان ماشین‪ ،‬یک اسم نمادین انتخاب شده است (مانند دستور ‪ ADD‬بجای‬
‫کد دودویی دستورالعمل جمع) که بخاطر سپردن و برنامهنویسی با آنها برای انسانها‬
‫ساده تر است‪ .‬اما این برنامهها برای ماشین قابل فهم نیست و باید قبل از اجرا شدن‬
‫توسط برنامه مترجمی بنام اسمبلر به زبان ماشین تبدیل شود‪.‬‬
‫زبانهای سطح باال ‪ :‬دستورالعملهای این زبانها بسیار نزدیک به زبان انسانها (بطور‬
‫مشخص زبان انگلیسی) میباشد و بهمین دلیل برنامهنویسی به آنها بسیار ساده تر‬
‫بوده و میتوان الگوریتمها را به راحتی به این زبانها تبدیل کرد‪.‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫بیسیک )‪ :(Basic‬برای کاربردهای آموزشی‬
‫فرترن )‪ : (Fortran‬برای کاربردهای علمی و مهندسی‬
‫پاسکال )‪ :(Pascal‬برای کاربردهای آموزشی و علمی‬
‫زبان ‪C‬‬
‫‪ 6-1‬تاریخچه زبان ‪C‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫در سال ‪ 1967‬مارتین ریچاردز زبان ‪ BCPL‬را برای نوشتن نرم افزارهای سیستم عامل و کامپایلر در‬
‫دانشگاه کمبریج ابداع کرد‪.‬‬
‫در سال ‪ 1970‬کن تامپسون زبان ‪ B‬را بر مبنای ویژگیهای زبان ‪ BCPL‬نوشت و از آن برای ایجاد اولین‬
‫نسخههای سیستم عامل ‪ Unix‬در آزمایشگاههای بل استفاده کرد‪.‬‬
‫زبان ‪ C‬در سال ‪ 1972‬توسط دنیس ریچی از روی زبان ‪ B‬و ‪ BCPL‬در آزمایشگاه بل ساخته شد و‬
‫ویژگیهای جدیدی همچون نظارت بر نوع دادهها نیز به آن اضافه شد‪ .‬ریچی از این زبان برای ایجاد‬
‫سیستم عامل ‪ Unix‬استفاده کرد اما بعدها اکثر سیستم عاملهای دیگر نیز با همین زبان نوشته‬
‫شدند‪ .‬این زبان با سرعت بسیاری گسترش یافت و چاپ کتاب ”‪ “The C Programming Language‬در‬
‫سال ‪ 1978‬توسط کرنیگان و ریچی باعث رشد روزافزون این زبان در جهان شد‪.‬‬
‫در سال ‪ 1983‬انستیتوی ملی استاندارد آمریکا )‪ (ANSI‬کمیتهای موسوم به ‪ X3J11‬را را مامور کرد تا‬
‫یک تعریف فاقد ابهام و مستقل از ماشین را از این زبان تدوین نماید‪.‬در سال ‪ 1989‬این استاندارد‬
‫تحت عنوان ‪ ANSI C‬به تصویب رسید و سپس در سال ‪ ،1990‬سازمان استانداردهای بین المللی‬
‫)‪ (ISO‬نیز این استاندارد را پذیرفت و مستندات مشترک آنها تحت عنوان ‪ ANSI/ISO C‬منتشر گردید‪.‬‬
‫در سالهای بعد و با ظهور روشهای برنامهنویسی شی گرا نسخه جدیدی از زبان ‪ C‬بنام ‪C++‬‬
‫توسط بیارنه استراوستروپ در اوایل ‪ 1980‬در آزمایشگاه بل توسعه یافت‪ .‬در ‪ C++‬عالوه بر امکانات‬
‫جدیدی که به زبان ‪ C‬اضافه شده است‪ ،‬خاصیت شی گرایی را نیز به آن اضافه کرده است‪.‬‬
‫شرکت سان مایکروسیستمز در سال ‪ 1995‬میالدی زبان ‪ Java‬را برمبنای ‪ C‬و ‪ C++‬ایجاد کرد که هم‬
‫اکنون از آن در سطح وسیعی استفاده میشود و برنامههای نوشته شده به آن برروی هر کامپیوتری‬
‫که از ‪ Java‬پشتیبانی کند(تقریبا تمام سیستمهای شناخته شده) قابل اجرا میباشد‪ .‬شرکت‬
‫مایکروسافت در رقابت با شرکت سان‪ ،‬در سال ‪ 2002‬زبان جدیدی بنام ‪( C#‬سی شارپ) را ارائه داد‬
‫که رقیبی برای ‪ Java‬بشمار میرود‪.‬‬
‫‪ 6-2‬برنامهنویسی ساختیافته‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫در دهه ‪ 1960‬میالدی توسعه نرم افزار دچار مشکالت عدیدهای شد‪ .‬در آن زمان سبک خاصی برای‬
‫برنامهنویسی وجود نداشت و برنامهها بدون هیچگونه ساختار خاصی نوشته میشدند‪.‬‬
‫فعالیتهای پژوهشی در این دهه باعث بوجود آمدن سبک جدیدی از برنامهنویسی بنام روش ساختیافته‬
‫گردید؛ روش منظمی که باعث ایجاد برنامههایی کامال واضح و خوانا گردید که اشکال زدایی و خطایابی آنها‬
‫نیز بسیار ساده تر بود‪.‬‬
‫اصلی ترین نکته در این روش عدم استفاده از دستور پرش )‪ (goto‬است‪ .‬تحقیقات بوهم و ژاکوپینی نشان داد‬
‫که میتوان هر برنامهای را بدون دستور پرش و فقط با استفاده از ‪ 3‬ساختار کنترلی ترتیب‪ ،‬انتخاب و تکرار‬
‫نوشت‪.‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫ساختار ترتیب‪ ،‬همان اجرای دستورات بصورت متوالی (یکی پس از دیگری) است‪.‬‬
‫ساختار انتخاب به برنامهنویس اجازه میدهد که براساس درستی یا نادرستی یک شرط‪ ،‬تصمیم بگیرد کدام مجموعه از‬
‫دستورات اجرا شود‪.‬‬
‫ساختار تکرار نیز به برنامهنویسان اجازه میدهد مجموعه خاصی از دستورات را تا زمانیکه شرط خاصی برقرار باشد‪ ،‬تکرار‬
‫نماید‪.‬‬
‫هر برنامه ساختیافته از تعدادی بلوک تشکیل میشود که این بلوکها به ترتیب اجرا میشوند تا برنامه خاتمه‬
‫یابد(ساختار ترتیب)‪ .‬هر بلوک میتواند یک دستورساده مانند خواندن‪ ،‬نوشتن یا تخصیص مقدار به یک متغیر‬
‫باشد و یا اینکه شامل دستوراتی باشد که یکی از ‪ 3‬ساختار فوق را پیاده سازی کنند‪ .‬نکته مهم اینجاست که‬
‫درمورد دستورات داخل هر بلوک نیز همین قوانین برقرار است و این دستورات میتوانند از تعدادی بلوک به‬
‫شرح فوق ایجاد شوند و تشکیل ساختارهایی مانند حلقههای تودرتو را دهند‪ .‬نکته مهم اینجاست که طبق‬
‫قوانین فوق یک حلقه تکرار یا بطور کامل داخل حلقه تکرار دیگر است و یا بطور کامل خارج آن قرار میگیرد و‬
‫هیچگاه حلقههای روی هم افتاده نخواهیم داشت‪.‬‬
‫از جمله اولین تالشها در زمینه ساخت زبانهای برنامهنویسی ساختیافته‪ ،‬زبان پاسکال بود که توسط پروفسور‬
‫نیکالس ویرث در سال ‪ 1971‬برای آموزش برنامهنویسی ساختیافته در محیطهای آموزشی ساخته شد و‬
‫بسرعت در دانشگاهها رواج یافت‪.‬‬
‫کمی بعد زبان ‪ C‬ارائه گردید که عالوه بر دارا بودن ویژگیهای برنامهنویسی ساختیافته بدلیل سرعت و کارایی‬
‫باال مقبولیتی همه گیر یافت و هم اکنون سالهاست که بعنوان بزرگترین زبان برنامهنویسی دنیا شناخته شده‬
‫است‪.‬‬
‫‪ 6-3‬مراحل اجرای یک برنامه ‪C‬‬
‫ورودیها‬
‫اجرای برنامه‬
‫خروجیها‬
‫فایل قابل اجرا‬
‫‪A.exe‬‬
‫پیوند دهنده‬
‫توابع آماده‬
‫برنامه مقصد‬
‫‪A.obj‬‬
‫کامپایلر‬
‫برنامه مبدا‬
‫‪A.C‬‬
‫ویرایشگر‬
‫‪ 6-3‬مراحل اجرای یک برنامه ‪C‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫مسلما طی این مراحل برای برنامهنویسان بسیار زمانبر است‪.‬‬
‫راه حل این مشکل استفاده از محیط مجتمع توسعه نرم افزار یا ‪ IDE‬است‪.‬‬
‫)‪IDE (Integrated Development Environment‬‬
‫یک ‪ IDE‬شامل مواردی همچون‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫ویرایشگر متن با امکانات ویژه برای زبان‬
‫امکان کامپایل و پیوند برنامه‬
‫امکان اشکال زدایی )‪ (Debug‬برنامه‬
‫چند محیط معروف برنامهنویسی عبارتند از ‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪ Borland C++ 3.1‬برای محیط ‪DOS‬‬
‫‪ Borland C++‬از نسخه ‪ 4‬به باال برای ‪Windows‬‬
‫‪ Microsoft Visual C++‬برای محیط ‪Windows‬‬
‫‪ Borland C++ Builder‬برای محیط ‪Windows‬‬
‫‪ 6-3‬مراحل اجرای یک برنامه ‪C‬‬
‫‪Borland C++ 3.1‬‬
‫‪ 6-4‬خطاهای برنامهنویسی‬
‫‪‬‬
‫‪‬‬
‫بنظر میرسد خطاها جزء جداناپذیر برنامهها هستند‪ .‬بندرت میتوان برنامهای نوشت که در‬
‫همان بار اول بدرستی و بدون هیچگونه خطایی اجرا شود‪.‬‬
‫بطور کلی خطاها به دو دسته تقسیم میشوند‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫خطاهای نحوی (خطاهای زمان کامپایل)‪ :‬این خطاها در اثر رعایت نکردن قواعد دستورات زبان ‪ C‬و یا‬
‫تایپ اشتباه یک دستور بوجود میآیند و در همان ابتدا توسط کامپایلر به برنامهنویس اعالم میگردد‪.‬‬
‫معموال این قبیل خطاها خطر کمتری را در بردارند‪.‬‬
‫خطاهای منطقی (خطاهای زمان اجرا)‪ :‬این دسته خطاها در اثر اشتباه برنامهنویس در طراحی الگوریتم‬
‫درست برای برنامه و یا گاهی در اثر درنظر نگرفتن بعضی شرایط خاص در برنامه ایجاد میشوند‪.‬‬
‫متاسفانه این دسته خطاها در زمان کامپایل اعالم نمیشوند و در زمان اجرای برنامه خود را نشان‬
‫میدهند‪ .‬ممکن است یک برنامهنویس خطای منطقی برنامه خود را تشخیص ندهد و این خطا پس از‬
‫مدتها و تحت یک شرایط خاص توسط کاربر برنامه کشف شود‪ .‬بهمین دلیل این دسته از خطاها‬
‫خطرناکتر هستند‪ .‬خود این خطاها به دو دسته تقسیم میگردند‪:‬‬
‫‪ ‬خطاهای مهلک‪ :‬در این دسته خطاها کامپیوتر بالفاصله اجرای برنامه را متوقف کرده و خطا را به‬
‫کاربر گزارش میکند‪ .‬مثال معروف این خطاها‪ ،‬خطای تقسیم بر صفر میباشد‪.‬‬
‫‪ ‬خطاهای غیرمهلک‪ :‬در این دسته خطا‪ ،‬اجرای برنامه ادامه مییابد ولی برنامه نتایج اشتباه تولید‬
‫مینماید‪ .‬بعنوان مثال ممکن است دراثر وجود یک خطای منطقی در یک برنامه حقوق و دستمزد‪،‬‬
‫حقوق کارمندان اشتباه محاسبه شود و تا مدتها نیز کسی متوجه این خطا نشود!‬
‫بسیار مهم است که در ابتدا سعی کنید برنامهای بنویسید که حداقل خطاها را داشته باشد‪،‬‬
‫در گام دوم با آزمایش دقیق برنامه خود هرگونه خطای احتمالی را پیدا کنید و در گام سوم‬
‫بتوانید دلیل بروز خطا را پیدا کرده و آنرا رفع نمایید‪ .‬هر سه عمل فوق کار سختی بوده و نیاز به‬
‫تجربه و مهارت دارد‪ .‬در اصطالح برنامهنویسی به هر گونه خطا‪ bug ،‬و به رفع خطا ‪ debug‬گفته‬
‫میشود‪.‬‬
‫‪ 6-5‬یک برنامه نمونه ‪C‬‬
‫‪// This Program Computes the Area of a Circle‬‬
‫توضیحات‬
‫دستور پیش پردازنده‬
‫تابع اصلی‬
‫اجرای برنامه از این تابع آغاز میگردد‬
‫برای افزودن توابع کتابخانهای به برنامه‬
‫>‪#include <stdio.h‬‬
‫{ )(‪void main‬‬
‫اعالن متغیرهای برنامه‬
‫; ‪int radius‬‬
‫عالمت شروع بلوک‬
‫اطالعات‬
‫تابع چاپ‬
‫; ‪float area‬‬
‫‪ begin‬در پاسکال‬
‫معادل دستور‬
‫; )" ‪printf("please enter radius :‬‬
‫اطالعات‬
‫تابع دریافت‬
‫انتهای بلوک‬
‫عالمت‬
‫; )‪scanf("%d",&radius‬‬
‫معادل دستور ‪ end‬در پاسکال‬
‫; ‪area = 2 * 3.14 * radius‬‬
‫; )‪printf("Area is %f",area‬‬
‫عالمت پایان دهنده دستورات‬
‫}‬
‫نتیجه اجرای برنامه‬
‫‪please enter radius : 10‬‬
‫‪Area is 62.8‬‬
‫دندانه گذاری‬
‫مبانی کامپیوتر و برنامه سازی‬
‫فصل‌هفتم‌‪ :‬مفاهیم‌اولیه‌زبان‌‪C‬‬
‫مدرس‌‪ :‬رضا‌رمضانی‬
‫‪ 7-1‬شناسهها در ‪C‬‬
‫‪‬‬
‫‪‬‬
‫شناسه )‪ (identifier‬نامی است که به یک قسمت از برنامه مانند متغیر‪ ،‬تابع‪ ،‬ثابت و یا‬
‫‪ ...‬داده میشود‪.‬‬
‫در زبان ‪ C‬برای انتخاب شناسهها فقط میتوان از عالئم زیر استفاده کرد‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫حروف انگلیسی کوچک و بزرگ )‪(A…Z a…z‬‬
‫ارقام )‪(0…9‬‬
‫عالمت خط پایین یا _‬
‫البته یک شناسه نمیتواند با یک رقم شروع شود‪.‬‬
‫چند شناسه مجاز‪ name2 ، average ، sum :‬و ‪student_average‬‬
‫چند شناسه غیرمجاز ‪ 2name‬و یا ‪student average‬‬
‫در برنامهنویسی امروزی پیشنهاد میشود بجای شناسههایی همانند ‪student_average‬‬
‫از ‪ studentAverage‬استفاده گردد‪.‬‬
‫نکته مهم دیگری که باید به آن اشاره کرد آنستکه زبان ‪ C‬برخالف بسیاری از زبانهای‬
‫دیگر به کوچک و بزرگی حروف حساس است )‪ .(case sensitive‬در نتیجه شناسههای زیر‬
‫با یکدیگر متفاوتند ‪:‬‬
‫‪Sum ≠ sum ≠ SUM‬‬
‫آخرین نکته اینستکه در هنگام انتخاب شناسه نمیتوانید از کلمات کلیدی که برای‬
‫منظورهای خاص در زبان ‪ C‬رزرو شدهاند استفاده کنید‪.‬‬
C ‫ شناسهها در‬7-1
: C ‫کلمات کلیدی در زبان‬
auto
break
case
char
const
continue
default
do
double
else
enum
extern
float
for
goto
if
int
long
register
return
short
signed
sizeof
static
struct
switch
typedef
union
unsigned
void
volatile
while
‫‪ 7-2‬انواع دادهها در ‪C‬‬
‫محدوده‬
‫اندازه (بیت)‬
‫توضیح‬
‫نوع داده‬
‫‪-128 to +127‬‬
‫‪8‬‬
‫کاراکتر‬
‫‪char‬‬
‫‪-32768 to +32767‬‬
‫‪16‬‬
‫عدد صحیح‬
‫‪int‬‬
‫‪3.4e-38 to 3.4e+38‬‬
‫‪32‬‬
‫عدد اعشاری‬
‫‪float‬‬
‫‪1.7e-308 to 1.7e+308‬‬
‫‪64‬‬
‫عدد اعشاری با‬
‫دقت مضاعف‬
‫‪double‬‬
‫‪ 7-2‬انواع دادهها در ‪C‬‬
‫‪‬‬
‫البته چند نکته مهم درمورد این جدول قابل ذکر است ‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫اندازه ‪ int‬در محیطهای ‪ 16‬بیتی مانند ‪ DOS‬برابر ‪ 16‬بیت است‪ .‬اما در‬
‫محیطهای ‪ 32‬بیتی همانند ‪ Windows‬اندازه آن ‪ 32‬بیت میباشد که در‬
‫اینصورت محدودهای برابر ‪ -2,147,483,648‬تا ‪ +2,147,483,647‬را پوشش‬
‫میدهد‪.‬‬
‫در بعضی از کامپایلرهای ‪ ،C‬نوع داده ‪ bool‬نیز وجود دارد که میتواند‬
‫یکی از مقادیر ‪(true‬درست) یا ‪( false‬غلط) را نشان دهد ‪ .‬اما در‬
‫نسخههای اولیه ‪ C‬از همان نوع داده صحیح یا ‪ int‬برای اینکار استفاده‬
‫میشد‪ .‬بدین صورت که ‪ 0‬نشاندهنده ‪ false‬و هر عدد غیر صفر (معموال‬
‫‪ )1‬نشاندهنده ‪ true‬است‪.‬‬
‫از آنجا که برای ذخیره سازی کاراکترها کد اسکی آنها ذخیره میگردد‪،‬‬
‫از یک متغیر کاراکتری یا ‪ char‬میتوان بعنوان یک عدد صحیح کوچک نیز‬
‫استفاده کرد و اعمال ریاضی برروی آنها نیز مجاز است‪.‬‬
‫‪ 7-2‬انواع دادهها در ‪C‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫عالوه براین چندین اصالح کننده نیز وجود دارد که به ما اجازه میدهد نوع داده مورد‬
‫نظر را با دقت بیشتری برای نیازهای مختلف استفاده نماییم‪.‬‬
‫این اصالح کنندهها عبارتند از ‪short, long, signed, unsigned :‬‬
‫تمام این اصالح کنندهها میتوانند به نوع داده ‪ int‬اعمال شوند و اثر آنها بستگی به‬
‫محیط دارد‪.‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫در یک محیط ‪16‬بیتی‪short int ،‬بازهم برابر ‪16‬بیت است ولی ‪ long int‬برابر‪32‬بیت‬
‫میباشد‪.‬‬
‫‪ unsigned int‬باعث میشود که یک عدد ‪ 16‬بیتی بدون عالمت داشته باشیم که بازه بین‬
‫‪ 0‬تا ‪ 65535‬را پوشش میدهد‪ signed int .‬نیز همانند ‪ int‬معمولی بوده و تفاوتی ندارد‪.‬‬
‫ترکیب این اصالح کنندهها نیز ممکن است‪ .‬مثال ‪ unsigned long int‬یک عدد ‪ 32‬بیتی بدون‬
‫عالمت است که بازه ‪ 0‬تا ‪ 4,294,967,295‬را پوشش میدهد‪.‬‬
‫برروی نوع داده ‪ char‬فقط اصالح کنندههای ‪ signed‬و ‪ unsigned‬را میتوان اعمال‬
‫کرد‪ .‬معموال از اصالح کننده ‪ unsigned‬وقتی استفاده میشود که قصد داشته‬
‫باشیم از ‪ char‬بعنوان یک عدد صحیح مثبت بین ‪ 0‬تا ‪ 255‬استفاده کنیم‪.‬‬
‫برروی نوع داده ‪ double‬تنها اصالح کننده ‪ long‬قابل اعمال است و دراینصورت عدد‬
‫اعشاری با ‪ 80‬بیت را خواهیم داشت که قادر است هر عددی در بازه ‪ 3.4e-4932‬تا‬
‫‪ 1.1e+4932‬را در خود نگاه دارد‪.‬‬
‫‪ 7-3‬تعریف متغیرها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫برای تعریف متغیرها به شکل زیر عمل میکنیم‪:‬‬
‫;>‪<type> <variable-list‬‬
‫که ‪ type‬یکی از نوع دادههای گفته شده و ‪ variable-list‬لیستی از متغیرها است که‬
‫با کاما از یکدیگر جدا شده اند‪ .‬بعنوان مثال ‪:‬‬
‫;‪int sum‬‬
‫;‪float average‬‬
‫; ‪long int a, b, c‬‬
‫; ‪unsigned long int i, j, k‬‬
‫عالوه براین میتوان در هنگام تعریف متغیر به آن مقدار اولیه نیز داد‪ .‬مثال ‪:‬‬
‫; ‪int d = 0‬‬
‫نکته مهم آنکه زبان ‪ C‬به متغیرها مقدار اولیه نمی دهد (حتی ‪ )0‬و برنامهنویس خود‬
‫باید اینکار را صریحا انجام دهد‪ ،‬درغیر اینصورت مقدار اولیه متغیر‪ ،‬نامعین خواهد بود‪.‬‬
‫تعریف متغیرها طبق اصول زبان ‪ C‬میتواند درهرجایی از برنامه صورت پذیرد‪ ،‬و‬
‫متغیرهای تعریف شده از همان خط به بعد قابل استفاده خواهد بود‪ .‬اما معموال‬
‫توصیه میگردد که تعریف متغیرها در همان خط ابتدایی تابع و بالفاصله پس از {‬
‫صورت پذیرد‪.‬‬
‫‪ 7-4‬ثوابت‬
‫‪‬‬
‫‪‬‬
‫ثابتها مقادیر ثابتی هستند که مقدار آنها در حین اجرای برنامه تغییر نمییابد‪.‬‬
‫ثابتها میتوانند از هریک از نوع دادههای اصلی باشند‪.‬‬
‫ثوابت عددی صحیح ‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫برای نمایش این دسته از ثوابت‪ ،‬از دنبالهای از ارقام بعالوه عالمت ‪ +‬یا ‪ -‬استفاده‬
‫میکنیم‪ .‬بعنوان مثال ‪ -45‬و یا ‪ 3489‬ثوابت صحیح هستند‪.‬‬
‫در حالت عادی ‪ C‬هر عدد را در کوچکترین نوع دادهای که میتواند قرار میدهد‪ .‬مثال‬
‫عدد ‪ 85‬در یک ‪ int‬قرار میگیرد‪ ،‬اما عدد ‪ 145398‬در یک ‪ long int‬قرار خواهد گرفت‪.‬‬
‫اگر قصد دارید یک عدد کوچک بعنوان ‪ long‬محسوب گردد‪ ،‬میتوانید از پسوند ‪ L‬در‬
‫انتهای آن استفاده میکنیم‪ .‬مثال ‪245L‬یک عدد ‪ long‬محسوب میشود‪.‬‬
‫;‪long int a = 20L‬‬
‫پسوند ‪ U‬در انتهای عدد نیز نشانه بدون عالمت بودن آن است‪.‬‬
‫نکته دیگر آنکه ‪ C‬به شما اجازه میدهد درصورت لزوم ثوابت صحیح خود را در مبنای‬
‫‪ 8‬یا ‪ 16‬نیز که از مبناهای متداول در برنامهنویسی هستند‪ ،‬بنویسید‪.‬‬
‫‪‬‬
‫‪‬‬
‫برای نوشتن عدد در مبنای ‪ 8‬باید آن را با ‪ 0‬آغاز کنید‪ ،‬مثال ‪ 0342‬یک عدد در منای ‪8‬‬
‫محسوب میگردد‪.‬‬
‫برای نوشتن یک عدد در مبنای ‪ 16‬باید آن را با ‪ 0x‬آغاز نمایید‪ ،‬مانند ‪.0x27A4‬‬
‫‪ 7-4‬ثوابت‬
‫ثوابت عددی اعشاری‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫برای نمایش اعداد اعشاری‪ ،‬باید از نقطه اعشار استفاده کنیم‪ ،‬مانند ‪237.45‬‬
‫اما نکته جالب آنستکه میتوانید از نماد علمی نیز برای نمایش اعداد اعشاری‬
‫استفاده کنید‪ .‬برای اینکار کافی است از حرف ‪ e‬برای نمایش قسمت توان‬
‫استفاده نمایید‪ .‬بعنوان مثال ‪:‬‬
‫‪-23.47 × 10 5 = -23.47e5‬‬
‫‪42.389 × 10 -3 = 42.389e-3‬‬
‫دقت کنید که قسمت توان‪ ،‬حتما یک عدد صحیح است‪.‬‬
‫نکته جالب اینجاست که برخالف مورد قبل‪ ،‬کامپایلر بطور پیش فرض دادههای‬
‫اعشاری را از نوع ‪ double‬فرض میکند‪.‬چنانچه دوست دارید ثابت شما ازنوع‬
‫‪ float‬درنظر گرفته شود‪ ،‬در انتهای آن ‪ F‬قرار دهید‪ .‬ضمنا پسوند ‪ L‬نیز داده‬
‫اعشاری را از نوع ‪ long double‬درنظر میگیرد‪.‬‬
‫‪ 7-4‬ثوابت‬
‫ثوابت کاراکتری ‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫برای نشان دادن ثوابت کاراکتری‪ ،‬آنها را در داخل ' قرار میدهیم‪ .‬بعنوان مثال‬
‫'‪'A‬یک ثابت کاراکتری است‪.‬‬
‫; '‪char ch = 'S‬‬
‫دقت کنید که همانطور که قبال گفته شد‪ ،‬کد اسکی کاراکترها در متغیر ذخیره‬
‫میگردد‪ ،‬بنابراین میتوان از آن بعنوان یک عدد صحیح نیز استفاده کرد‪ .‬نکته‬
‫مهم دیگر آنستکه به تفاوت عدد ‪ 5‬و کاراکتر '‪ '5‬دقت داشته باشید‪ .‬در حقیقت‬
‫'‪ '5‬برابر است با عدد ‪ 53‬که همان کد اسکی آن است‪.‬‬
‫روش فوق‪ ،‬برای نمایش کاراکترهای قابل چاپ مناسب است‪ ،‬اما بعضی‬
‫کاراکترها مانند ‪ enter‬قابل نمایش نبوده و برای آنها شکل خاصی وجود ندارد‪.‬‬
‫در چنین مواردی‪ ،‬زبان ‪ C‬از ترکیب عالمت \ به همراه یک کاراکتر دیگر‪ ،‬برای‬
‫نمایش این قبیل کاراکترها استفاده مینماید‪ .‬بعنوان مثال‪ ،‬کاراکتر '‪ '\n‬نشان‬
‫دهنده خط جدید یا همان ‪ enter‬میباشد‪.‬‬
‫جدول صفحه بعد این قبیل کاراکترها و معادل آنها را نشان میدهد‪.‬‬
‫‪ 7-4‬ثوابت‬
‫نحوه نمایش در ‪C‬‬
‫کد اسکی‬
‫صدای بوق کامپیوتر‬
‫‪\a‬‬
‫‪7‬‬
‫حرکت به عقب ‪backspace‬‬
‫‪\b‬‬
‫‪8‬‬
‫شروع صفحه ‪form feed‬‬
‫‪\f‬‬
‫‪12‬‬
‫سطر جدید ‪line feed‬‬
‫‪\n‬‬
‫‪10‬‬
‫برگشت به ابتدای سطر ‪carriage return‬‬
‫‪\r‬‬
‫‪13‬‬
‫فاصله افقی ‪horizontal tab‬‬
‫‪\t‬‬
‫‪9‬‬
‫فاصله عمودی ‪vertical tab‬‬
‫‪\v‬‬
‫‪11‬‬
‫عالمت سوال‬
‫?\‬
‫‪63‬‬
‫عالمت '‬
‫'\‬
‫‪39‬‬
‫عالمت "‬
‫"\‬
‫‪34‬‬
‫عالمت \‬
‫\\‬
‫‪92‬‬
‫عالمت تهی‬
‫‪\0‬‬
‫‪0‬‬
‫نام کاراکتر‬
‫‪ 7-4‬ثوابت‬
‫‪‬‬
‫ثوابت رشتهای ‪ C :‬عالوه بر ثوابت فوق‪ ،‬از یک ثابت دیگر بنام رشته نیز‬
‫حمایت میکند‪ .‬رشته‪ ،‬دنبالهای از کاراکترها است که در داخل " قرار‬
‫میگیرند‪ .‬بعنوان مثال "‪ "this is a test‬یک رشته است‪ .‬دقت کنید که '‪'a‬‬
‫یک کاراکتر است‪ ،‬اما "‪ "a‬یک رشته است که فقط شامل یک کاراکتر‬
‫میباشد‪.‬‬
‫‪ 7-5‬عملگرها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫عملگر‪ ،‬نمادی است که به کامپایلر میگوید تا عملیات محاسباتی یا‬
‫منطقی خاصی را برروی یک یا چند عملوند‪ ،‬انجام دهد‪.‬‬
‫به عملگرهایی که فقط یک عملوند دارند‪ ،‬عملگر یکانی میگوییم و‬
‫همواره عملگر در سمت چپ عملوند قرار میگیرد(مانند عدد ‪.)-125‬‬
‫اما عملگرهایی که برروی دو عملوند اثر میکنند را عملگر دودویی‬
‫نامیده و عملگر را بین دو عملوند قرار میدهیم (مانند ‪.)23+86‬‬
‫هر ترکیب درستی از عملگرها و عملوندها را یک عبارت مینامیم‪.‬‬
‫‪ C‬از نقطه نظر عملگرها یک زبان بسیار قوی است‪ .‬این عملگرها به چند‬
‫دسته اصلی تقسیم میگردند که آنها را به ترتیب بررسی میکنیم‪.‬‬
‫‪ 7-5-1‬عملگرهای محاسباتی‬
‫این عملگرها‪ ،‬همان اعمال متداول ریاضی هستند که در زبان ‪ C‬مورد استفاده‬
‫قرار میگیرند‪ .‬این اعمال عبارتند از ‪:‬‬
‫عمل‬
‫نوع‬
‫عملگر‬
‫منفی کردن عملوند سمت راست‬
‫یکانی‬
‫‪-‬‬
‫جمع دو عملوند‬
‫دودویی‬
‫‪+‬‬
‫تفریق دو عملوند‬
‫دودویی‬
‫‪-‬‬
‫ضرب دو عملوند‬
‫دودویی‬
‫*‬
‫تقسیم دو عملوند‬
‫دودویی‬
‫‪/‬‬
‫محاسبه باقیمانده تقسیم دو عملوند‬
‫دودویی‬
‫‪%‬‬
‫‪ 7-5-1‬عملگرهای محاسباتی‬
‫‪‬‬
‫‪‬‬
‫این عملگرهای برروی همه انواع دادههای ‪ C‬عمل میکنند‪ ،‬بجز عملگر ‪ %‬که فقط برروی‬
‫نوع دادههای صحیح عمل میکند‪.‬‬
‫اما نکته مهمی که باید به آن اشاره کرد‪ ،‬نحوه کار عملگر تقسیم برروی نوع دادههای‬
‫مختلف است‪.‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫درصورتیکه هردو عملوند صحیح باشند‪ ،‬تقسیم بصورت صحیح بر صحیح انجام خواهد شد‪.‬‬
‫اگر یکی یا هر دو عملوند اعشاری باشند‪ ،‬تقسیم بصورت اعشاری انجام خواهد پذیرفت‪.‬‬
‫فرض کنید تعاریف زیر را داریم ‪:‬‬
‫; ‪int a,b‬‬
‫; ‪float c,d‬‬
‫; ‪a = 10 ; b = 4‬‬
‫;‪c = 8.2; d = 4.0‬‬
‫اکنون به نتایج عملیات زیر دقت کنید ‪:‬‬
‫‪a / b => 2‬‬
‫‪c / d => 2.05‬‬
‫‪a / d => 2.5‬‬
‫‪a / 4 => 2‬‬
‫چنانچه به آخرین مورد توجه کنید‪ ،‬متوجه میشوید که از آنجا که ‪a‬یک متغیر صحیح است و ‪4‬‬
‫نیز یک ثابت صحیح در نظر گرفته شده‪ ،‬درنتیجه تقسیم بصورت صحیح بر صحیح انجام گرفته‬
‫است‪ .‬چنانچه بخواهیم تقسیم بصورت اعشاری صورت پذیرد‪ ،‬به دو شکل میتوانیم عمل کنیم‪.‬‬
‫‪ 7-5-1‬عملگرهای محاسباتی‬
‫‪‬‬
‫برای اینکه عبارت تقسیم صفحه قبل بصورت اعشاری انجام شود‪ ،‬به دو روش‬
‫میتوان عمل کرد‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫این عبارت را بصورت ‪ a / 4.0‬بنویسیم‪.‬‬
‫از عملگر قالب ریزی استفاده کنیم‪.‬‬
‫عملگر قالب ریزی میتواند یک نوع را به نوع دیگری تبدیل نماید‪ .‬شکل کلی آن‬
‫به شکل زیر است ‪:‬‬
‫>‪(<type>) <expression‬‬
‫که ‪ type‬نوع مورد نظر است که قصد تبدیل عبارت ‪ expression‬به آن نوع را داریم‪.‬‬
‫بعنوان مثال در مورد قبلی میتوان به شکل زیر عمل کرد ‪:‬‬
‫‪(float) a / 4‬‬
‫در این حالت از کامپایلر خواسته ایم که ابتدا عدد ‪ a‬را به اعشاری تبدیل کند‬
‫(البته بصورت موقت) و سپس آن را بر ‪ 4‬تقسیم نماید‪ ،‬که مسلما حاصل‬
‫اعشاری خواهد بود‪.‬‬
‫بطورکلی هرگاه ثابتها و متغیرهایی از انواع مختلف در یک عبارت محاسباتی‬
‫داشته باشیم‪ ،‬کامپایلر ‪ C‬همه آنها را به یک نوع یکسان که همان بزرگترین‬
‫عملوند موجود است تبدیل خواهد کرد‪ .‬بعنوان مثال به مورد صفحه بعد دقت‬
‫کنید ‪:‬‬
‫ عملگرهای محاسباتی‬7-5-1
char ch;
int i;
float f;
double d;
result = (ch / i)
int
+
(f *d)
(f+i)
float
double
double
int
-
double
double
float
‫‪ 7-5-1‬عملگرهای محاسباتی‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫اولویت عملگرها ‪ :‬در عبارتی که شامل چندین عملگر است‪ ،‬کدامیک در‬
‫ابتدا اعمال خواهد گردید‪.‬‬
‫اولویت عملگرهای محاسباتی از باال به پایین بشرح زیر است‪:‬‬
‫‪ ‬عملگر یکانی –‬
‫‪ ‬عملگرهای * و ‪ /‬و ‪%‬‬
‫‪ ‬عملگرهای ‪ +‬و –‬
‫چنانچه اولویت دو عملگر یکسان باشد‪ ،‬این عملگرها از چپ به راست‬
‫محاسبه خواهند شد‪.‬‬
‫چنانچه بخواهیم یک عمل با اولویت پایین زودتر انجام شود‪ ،‬باید از پرانتز‬
‫استفاده کنیم‪ .‬بنابراین اولویت عملگر پرانتز از همه موارد فوق‬
‫بیشتراست‪ .‬در مورد پرانتزهای متداخل‪ ،‬ابتدا پرانتز داخلی محاسبه‬
‫میشود؛ اما در مورد پرانتزهای هم سطح‪ ،‬ابتدا پرانتز سمت چپتر‬
‫محاسبه میگردد‪.‬‬
‫‪ 7-5-1‬عملگرهای محاسباتی‬
‫‪‬‬
‫چند مثال در مورد اولویت عملگرها‬
‫ترتیب اجرای عملگرها‬
‫عبارت زبان ‪C‬‬
‫ابتدا عمل * و سپس عمل ‪+‬‬
‫‪b+c*d‬‬
‫ابتدا عمل ‪ +‬و سپس عمل *‬
‫‪(b + c) * d‬‬
‫ابتدا عمل ‪ /‬سپس عمل * و در انتها عمل ‪+‬‬
‫‪b+c/d*e‬‬
‫ابتدا عمل * سپس عمل ‪ /‬و در انتها عمل ‪+‬‬
‫)‪b + c / (d * e‬‬
‫‪ 7-5-1‬عملگرهای محاسباتی‬
‫‪‬‬
‫یک مثال کامل در مورد اولویت عملگرها‬
‫‪result = a + b * (f – (g + b) / d) – c * (a – d) / e‬‬
‫‪6‬‬
‫‪1‬‬
‫‪7‬‬
‫‪2‬‬
‫‪8‬‬
‫‪3‬‬
‫‪4‬‬
‫‪5‬‬
‫‪9‬‬
‫‪ 7-5-2‬عملگرهای انتساب‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫در زبان ‪ C‬برای انتساب چندین عملگر وجود دارد‪ .‬ساده ترین عملگر انتساب‪ ،‬همان عملگر =‬
‫است که در بسیاری از زبانها استفاده میشود‪ .‬بعنوان مثال ‪:‬‬
‫;‪a = 5‬‬
‫;‪b = c + 2 * d‬‬
‫این عملگر باعث میشود که عبارت سمت راست در عبارت سمت چپ قرار گیرد‪.‬‬
‫توجه کنید که مقدار سمت چپ باید عبارتی باشد که بتوان به آن یک مقدار را نسبت داد‬
‫(مانند یک متغیر) که به آن ‪ Lvalue‬گفته میشود‪ ،‬بنابراین یک ثابت نمیتواند در سمت چپ‬
‫قرار گیرد‪.‬‬
‫نکته دیگر اینکه اولویت عملگر = از عملگرهای ریاضی کمتر است و درنتیجه ابتدا آن عملیات‬
‫انجام شده و در پایان حاصل در عبارت سمت چپ ریخته میشود‪.‬‬
‫الزم به ذکر است که در هنگام انتساب‪ ،‬درصورت لزوم نوع عبارت سمت راست به نوع عبارت‬
‫سمت چپ تبدیل میشود‪ .‬مثال‪:‬‬
‫;‪int a‬‬
‫;‪a = 2.5 * 5.0‬‬
‫که دراینصورت عدد ‪ 12‬در ‪ a‬ذخیره خواهد شد‪.‬‬
‫شرکت پذیری این عملگر از راست به چپ میباشد‪ ،‬بدین معنا که چنانچه چندین عملگر‬
‫نسبت دهی داشته باشیم‪ ،‬این عملگرها از راست به چپ محاسبه میشوند‪ .‬مثال پس از‬
‫اجرای دستور زیر‪ ،‬مقدار هر ‪ 3‬متغیر برابر ‪ 10‬خواهد شد‪.‬‬
‫;‪a = b = c = 10‬‬
‫‪ 7-5-2‬عملگرهای انتساب‬
‫‪‬‬
‫نکته جالب درمورد زبان ‪ C‬آنستکه دارای یک سری عملگرهای انتساب‬
‫خالصه شده است که باعث میشوند در بعضی موارد بتوانیم عبارات‬
‫کوتاهتری را بنویسیم‪ .‬این عملگرها عبارتند از ‪:‬‬
‫عملگرد‬
‫مثال‬
‫عبارت انتساب معادل‬
‫‪++‬‬
‫;‪a ++‬‬
‫;‪a = a + 1‬‬
‫‪--‬‬
‫;‪a --‬‬
‫;‪a = a – 1‬‬
‫=‪+‬‬
‫;‪a += 5‬‬
‫;‪a = a + 5‬‬
‫=‪-‬‬
‫;‪a -= 8‬‬
‫;‪a= a – 8‬‬
‫=*‬
‫;‪a *= 10‬‬
‫;‪a= a * 10‬‬
‫=‪/‬‬
‫;‪a /= 2‬‬
‫;‪a = a / 2‬‬
‫=‪%‬‬
‫;‪a %= 10‬‬
‫;‪a = a % 10‬‬
‫‪ 7-5-2‬عملگرهای انتساب‬
‫‪‬‬
‫‪‬‬
‫عملگر اول و دوم‪ ،‬عملگرهای یکانی هستند که بترتیب عملگر افزایش و عملگر کاهش‬
‫نامیده میشوند‪.‬‬
‫نکته جالب آنستکه این دو عملگر به دوصورت مورد استفاده قرار میگیرند‪.‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫در حالتی که از دستور ;‪ a ++‬استفاده شود به آن پس افزایش میگویند و بدین معناست که‬
‫ابتدا از مقدار فعلی ‪ a‬در عبارت موردنظر استفاده کن و سپس آن را افزایش بده‪.‬‬
‫اما دستور ;‪ ++ a‬که به آن پیش افزایش گفته میشود‪ ،‬ابتدا ‪ a‬را افزایش داده و سپس از آن در‬
‫عبارت استفاده میکند‪.‬‬
‫به برنامه زیر دقت کنید‪:‬‬
‫>‪#include <stdio.h‬‬
‫{ )(‪void main‬‬
‫; ‪int a ,b‬‬
‫;‪a=5‬‬
‫;‪b = a ++‬‬
‫;)‪printf(“a=%d and b=%d \n”,a,b‬‬
‫;‪a=5‬‬
‫;‪b = ++ a‬‬
‫;)‪printf(“a=%d and b=%d \n”,a,b‬‬
‫}‬
‫‪a=6 b=5‬‬
‫‪a=6 b=6‬‬
‫‪ 7-5-3‬عملگرهای مقایسه ای‬
‫‪‬‬
‫‪‬‬
‫این عملگرها دو عبارت را بایکدیگر مقایسه کرده و نتیجه را باز میگردانند‪.‬‬
‫نتیجه میتواند درست )‪ (true‬یا غلط )‪ (false‬باشد‪.‬‬
‫نتیجه این عملگرها یک عدد صحیح است که درصورت درست بودن ‪ 1‬و‬
‫درصورت غلط بودن ‪ 0‬باز میگردانند‪ .‬این عملگرها عبارتند از‪:‬‬
‫عملگر‬
‫مفهوم عملگر‬
‫مثال‬
‫<‬
‫بزرگتر (<)‬
‫‪a>b‬‬
‫>‬
‫کوچکتر (>)‬
‫‪a<b‬‬
‫=<‬
‫بزرگتر یا مساوی (≤)‬
‫‪a >= b‬‬
‫=>‬
‫کوچکتر یا مساوی (≤)‬
‫‪a <= b‬‬
‫==‬
‫مساوی (=)‬
‫‪a == b‬‬
‫=!‬
‫نامساوی (≠)‬
‫‪a != b‬‬
‫‪ 7-5-3‬عملگرهای مقایسه ای‬
‫‪‬‬
‫‪‬‬
‫نکته مهمی که باید به آن دقت کرد عملگر مساوی (==) است‪ ،‬چرا که یک‬
‫اشتباه بسیار متداول برنامهنویسان ‪ C‬استفاده اشتباه از عملگر انتساب‬
‫(=) بجای عملگر تساوی (==) است که باعث ایجاد خطا در برنامه‬
‫میشود‪.‬‬
‫اولویت این عملگرها از باال به پایین بشرح زیر است‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫عملگرهای <‪ <= ، > ،‬و =>‬
‫عملگرهای == و =!‬
‫الزم بذکر است که در هنگام مساوی بودن اولویت چند عملگر‪ ،‬این عملگرها‬
‫از چپ به راست محاسبه میگردند‪.‬‬
‫‪ 7-5-4‬عملگرهای منطقی‬
‫‪‬‬
‫این عملگرها به شما اجازه میدهند که شرطهای سادهای را که با‬
‫استفاده از عملگرهای مقایسهای ایجاد شدهاند را با یکدیگر ترکیب نموده و‬
‫شرطهای پیچیده تری را بسازید‪ .‬این عملگرها عبارتند از ‪:‬‬
‫مثال‬
‫عملگر‬
‫مفهوم عملگر‬
‫نحوه کار‬
‫&&‬
‫‪ AND‬منطقی‬
‫اگر هر دو عملوند درست باشند‪,‬‬
‫درست و در غیر اینصورت نادرست باز‬
‫میگرداند‪.‬‬
‫‪a>0 && sw==1‬‬
‫||‬
‫‪ OR‬منطقی‬
‫اگر هر دو عملوند نادرست باشند‪,‬‬
‫نادرست و در غیر اینصورت درست باز‬
‫میگرداند‪.‬‬
‫‪a<=100 || b!=0‬‬
‫!‬
‫‪ NOT‬منطقی‬
‫اگر عملوند درست باشد‪ ،‬نادرست و‬
‫اگر نادرست باشد‪ ،‬درست برمی‬
‫گرداند‪.‬‬
‫)‪! (a==1 || b<10‬‬
‫‪ 7-5-5‬عملگر شرطی‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫گاهی الزم است که ابتدا یک شرط بررسی شده و سپس برمبنای نتیجه (درست یا نادرست بودن آن) یکی‬
‫از دو عبارت ممکن بازگردانده شود‪ .‬برای اینکار میتوان از یک عملگر ‪3‬تایی (با ‪ 3‬عملوند) بنام عملگر‬
‫شرطی استفاده نمود‪.‬‬
‫شکل کلی این عملگر بصورت زیر است‪:‬‬
‫>عبارت‪> :<2‬عبارت‪> ?<1‬شرط<‬
‫نحوه کار بدینصورت است که درصورت درست بودن شرط‪ ،‬عبارت‪ 1‬و در غیراینصورت عبارت ‪ 2‬بازگردانده‬
‫میشود‪.‬‬
‫به عنوان مثال به دستور زیر توجه کنید‪:‬‬
‫که این عبارت معادل دستور زیر است‪:‬‬
‫;‪a = (k<10) ? 100 : 50‬‬
‫;‪if (k<10) a=100‬‬
‫;‪else a=50‬‬
‫‪‬‬
‫البته این عبارت به شکلهای پیچیده نیز میتواند مورد استفاده قرار گیرد‪ .‬مثال ‪:‬‬
‫;‪c += (a>0 && a<10) ? ++a : a/b‬‬
‫که معادل است با ‪:‬‬
‫{ )‪if (a>0 && a<10‬‬
‫;‪a= a + 1‬‬
‫;‪c = c + a‬‬
‫}‬
‫;‪else c = c + a/b‬‬
‫‪ 7-5-6‬اولویت عملگرها‬
‫عملگر‬
‫اولویت‬
‫شرکت پذیری‬
‫‪1‬‬
‫()‬
‫از چپ به راست‬
‫‪2‬‬
‫‪- + ! -- ++‬‬
‫از راست به چپ‬
‫‪3‬‬
‫‪* / %‬‬
‫از چپ به راست‬
‫‪4‬‬
‫‪+ -‬‬
‫از چپ به راست‬
‫‪5‬‬
‫>> <<‬
‫از چپ به راست‬
‫‪6‬‬
‫>= > <= <‬
‫از چپ به راست‬
‫‪7‬‬
‫=! ==‬
‫از چپ به راست‬
‫‪8‬‬
‫&&‬
‫از چپ به راست‬
‫‪9‬‬
‫||‬
‫از چپ به راست‬
‫‪10‬‬
‫‪?:‬‬
‫از راست به چپ‬
‫‪11‬‬
‫=‪= += -= *= /= %‬‬
‫از راست به چپ‬
‫‪12‬‬
‫‪,‬‬
‫از چپ به راست‬
‫‪ 7-6‬خواندن و نمایش اطالعات‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫همانطور که قبال نیز گفته شد‪ ،‬یکی از اهداف زبان ‪ C‬قابل حمل بودن آن‬
‫است‪ .‬بهمین منظور سعی شده است که از دستوراتی که ممکن است‬
‫وابسته به ماشین خاصی باشد‪ ،‬اجتناب گردد‪.‬‬
‫بهمین دلیل زبان ‪ C‬برخالف سایر زبانها دارای هیچ دستوری برای‬
‫خواندن از ورودی و یا نوشتن در خروجی نیست‪.‬‬
‫اما در عوض دارای تعدادی تابع (زیربرنامه) استاندارد میباشد که تقریبا‬
‫تمامی کامپایلرها از آنها حمایت میکنند‪.‬‬
‫این کار به کامپایلر این امکان را میدهد که بر حسب نوع سخت افزار‬
‫موردنظر‪ ،‬توابع ورودی و خروجی را طراحی نماید‪.‬‬
‫این توابع در یک فایل سرآمد بنام ‪ stdio.h‬تعریف شدهاند و بنابراین قبل‬
‫ازاینکه بتوانید از این توابع استفاده نمایید‪ ،‬باید این فایل را نیز با استفاده‬
‫از ‪ #include‬در برنامه خود بگنجانید‪.‬‬
‫‪ 7-6-1‬تابع نمایش در خروجی‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫برای نمایش اطالعات در خروجی از تابع ‪ printf‬استفاده میشود‪.‬‬
‫این تابع رشته موردنظر شما را به خروجی استاندارد (که در حالت‬
‫عادی همان صفحه نمایش یا مانیتور است) میفرستد‪ .‬شکل کلی این‬
‫تابع بصورت زیر است‪:‬‬
‫; )>لیست متغیرها< ‪> ,‬رشته کنترلی<(‪printf‬‬
‫رشته کنترلی همان متنی است که قصد چاپ آن را داریم‪ ،‬با ذکر این‬
‫نکته که در قسمتهایی که باید مقدار یک متغیر چاپ شود‪ ،‬از یک‬
‫مشخصه تبدیل استفاده میشود‪ .‬هر مشخصه تبدیل از یک عالمت ‪%‬‬
‫بعالوه یک کاراکتر که نوع متغیر مورد نظر را نشان میدهد تشکیل‬
‫میگردد‪.‬‬
‫لیست متغیرها نیز همان متغیرهایی هستند که قصد چاپ آنها را داریم‪.‬‬
‫این متغیرها باید بترتیب قرار گرفته و با کاما )‪ (,‬از یکدیگر جدا شوند‪.‬‬
‫‪ 7-6-1‬تابع نمایش در خروجی‬
‫مشخصه تبدیل‬
‫مفهوم‬
‫‪%c‬‬
‫کاراکتر‬
‫‪%d‬‬
‫عدد صحیح در مبنای ‪10‬‬
‫‪%f‬‬
‫عدد اعشاری بدون نماد علمی‬
‫‪%e‬‬
‫عدد اعشاری با نماد علمی‬
‫‪%g‬‬
‫عدد اعشاری با حالت کوتاهتر بین ‪ e‬و ‪f‬‬
‫‪%s‬‬
‫رشته‬
‫‪%ld‬‬
‫عدد صحیح بزرگ‬
‫‪%lf %le %lg‬‬
‫عدد اعشاری بزرگ‬
‫‪%o‬‬
‫عدد صحیح در مبنای ‪8‬‬
‫‪%x‬‬
‫عدد صحیحی در مبنای ‪16‬‬
‫‪%u‬‬
‫عدد صحیح بدون عالمت‬
‫ تابع نمایش در خروجی‬7-6-1
#include <stdio.h>
void main() {
int age = 20;
float average = 18.23;
printf("You are %d years old and your average is %f \n",age,average);
}
You are 20 years old and your average is 18.230000
‫‪ 7-6-1‬تابع نمایش در خروجی‬
‫‪‬‬
‫‪‬‬
‫برای کنترل نحوه چاپ اعداد میتوانید از مشخصه طول میدان استفاده‬
‫کنید‪.‬‬
‫مشخصه طول میدان برای اعداد به شکل زیر استفاده میشود‪:‬‬
‫‪ ‬برای اعداد صحیح از ‪ %nd‬استفاده میکنیم که ‪ n‬تعداد ارقام را نشان‬
‫میدهد (مثال ‪ .)%3d‬در اینصورت برای هر متغیر ‪ n‬رقم درنظر گرفته‬
‫میشود‪ .‬اگر اندازه عدد از ‪ n‬کوچکتر باشد‪ ،‬به سمت چپ آن فضای‬
‫خالی اضافه میشود و اگر اندازه عدد بیش از ‪ n‬رقم باشد‪ ،‬طول میدان‬
‫نادیده گرفته شده و عدد بطور کامل چاپ میشود‪.‬‬
‫‪ ‬برای اعداد اعشاری از ‪ %n.mf‬استفاده میکنیم که ‪ n‬اندازه کل عدد‬
‫(شامل عالمت ممیز) و ‪ m‬تعداد ارقام اعشار است (مثال ‪ .)%5.2f‬در‬
‫صورتیکه تعداد ارقام اعشاری عدد موردنظر از ‪ m‬بیشتر باشد‪ ،‬عدد به ‪m‬‬
‫عدد اعشار گرد میشود و در صورتیکه از ‪ m‬کمتر باشد‪ ،‬در سمت‬
‫راست آن ‪ 0‬قرار داده میشود‪.‬‬
‫‪ 7-6-2‬تابع خواندن از ورودی‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫برای خواندن اطالعات از ورودی از تابع ‪ scanf‬استفاده میشود‪.‬‬
‫این تابع اطالعات را ازورودی استاندارد (معموال صفحه کلید) خوانده و در‬
‫متغیرهای تعیین شده قرار میدهد‪.‬‬
‫شکل کلی این تابع بصورت زیر است‪:‬‬
‫; )>لیست آدرسهای متغیرها< ‪> ,‬رشته کنترلی<(‪scanf‬‬
‫همانطور که میبینید نحوه احضار تابع ‪ scanf‬نیز مشابه ‪ printf‬است‪ .‬تنها تفاوت‬
‫در آن است که در ‪ scanf‬باید لیست آدرسهای متغیرها ارسال شود‪.‬‬
‫مبحث مربوط به آدرسها در فصول بعدی بررسی خواهد شد ولی در حال حاضر‬
‫بخاطر بسپارید که برای بدست آوردن آدرس یک متغیر از عالمت & استفاده‬
‫میکنیم‪ .‬بعنوان مثال ‪ &age‬بمعنای آدرس متغیر ‪ age‬است‪.‬‬
‫بطور کلی در ‪ C‬قدیمی هرگاه که یک تابع دارای پارامترهای خروجی بود (یعنی‬
‫پارامترهایی که یک مقدار را باز میگرداندند) از آدرس متغیرها استفاده میشد‬
‫که امروزه این مسئله وجود ندارد‪.‬‬
‫ تابع خواندن از ورودی‬7-6-2
#include <stdio.h>
void main() {
int age;
float average ;
printf("Please enter your age and average : ");
scanf("%d %f",&age,&average);
printf("You are %d years old and your average is %5.2f \n",age,average);
}
Please enter your age and average : 19 16.72
You are 19 years old and your average is 16.72
‫‪ 7-6-3‬ورودی و خروجی اطالعات در ‪C++‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫زبان ‪ C++‬یک زبان شی گرا است‪ ،‬به همین دلیل در این زبان برای‬
‫ورودی و خروجی از اشیاء بجای توابع استفاده میگردد‪.‬‬
‫از آنجا که امروزه معموال برنامهنویسان ‪ C‬از کامپایلرهای ‪ C++‬استفاده‬
‫میکنند‪ ،‬میتوانند از اشیای ورودی و خروجی آن نیز استفاده کنند‪.‬‬
‫اینکار در بین بسیاری از برنامهنویسان ‪ C‬متداول است‪ ،‬بهمین دلیل ما‬
‫در اینجا نحوه کار با اشیای خواندن و نوشتن در ‪ C++‬را بطور مقدماتی‬
‫توضیح میدهیم؛ گرچه توضیح کامل این موارد نیاز به آشنایی با شی‬
‫گرایی و زبان ‪ C++‬دارد‪.‬‬
‫قبل از هرچیز الزم به ذکر است که کلیه اشیای مربوط به ورودی و‬
‫خروجی در فایل سرآمدی بنام ‪ iostream.h‬تعیریف شده اند‪ ،‬بنابراین ابتدا‬
‫باید این فایل به برنامه توسط دستور ‪ #include‬الحاق گردد‪.‬‬
‫‪ 7-6-3‬ورودی و خروجی اطالعات در ‪C++‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫زبان ‪ C++‬برای نمایش اطالعات از یک شیئ بنام ‪ cout‬استفاده مینماید‪.‬‬
‫برای ارسال اطالعات مورد نظر برای چاپ به ‪ cout‬باید از عملگر درج در جریان‬
‫یا >> استفاده نماییم‪ .‬بعنوان مثال ‪:‬‬
‫; ” ‪cout << “Please enter your name:‬‬
‫و یا مثال دیگر ‪:‬‬
‫;‪int a = 10‬‬
‫;‪float b = 2.86‬‬
‫;‪cout << a‬‬
‫;‪cout << b‬‬
‫نکته جالبی که در این مثالها دیده میشود‪ ،‬آنستکه برخالف تابع ‪ printf‬هیچ‬
‫نیازی به مشخص کردن نوع متغیری که قصد چاپ آن را داریم نیست و خود‬
‫شئ ‪ cout‬نوع آن را تشخیص میدهد‪.‬‬
‫عالوه براین میتوان چندین عملگر درج در جریان را با یکدیگر الحاق کرد و چندین‬
‫متغیر را با یک دستور چاپ کرد‪.‬‬
C++ ‫ ورودی و خروجی اطالعات در‬7-6-3
#include <iostream.h>
void main() {
int age = 20;
floate average = 18.23;
cout << “You are ” << age << “ years old and your average is ” << average ;
}
You are 20 years old and your average is 18.230000
‫‪ 7-6-3‬ورودی و خروجی اطالعات در ‪C++‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫برای دریافت اطالعات از کاربر‪ ،‬از شئ دیگری بنام ‪ cin‬استفاده میشود‪.‬‬
‫برای ارسال متغیر مورد نظر به ‪ cin‬باید از عملگر استخراج از جریان یا <<‬
‫استفاده نماییم‪ .‬بعنوان مثال‪:‬‬
‫;‪int a‬‬
‫;‪cin >> a‬‬
‫بازهم همانطور که میبینید نیازی به تعیین نوع متغیر موردنظر نیست و خود‬
‫شئ ‪ cin‬نوع متغیر را بطور اتوماتیک تشخیص داده و دادهای از همان نوع را از‬
‫کاربر دریافت و در متغیر مورد نظر قرار میدهد‪.‬‬
‫عملگرهای استخراج از جریان را نیز میتوان با یکدیگر الحاق کرد‪.‬‬
‫برای رفتن به خط بعد در شئ ‪ cout‬میتوان از دستکاری کننده ‪ endl‬استفاده‬
‫کرد‪ .‬مثال در دستور زیر پس از چاپ پیغام‪ ،‬مکان نما به خط بعد منتقل میشود‪:‬‬
‫;‪cout << “List of students : “ << endl‬‬
‫البته دستکاری کنندههای متعدد دیگری نیز ازجمله موارد مربوط به تعیین طول‬
‫میدان و نحوه چاپ مقادیر وجود دارد که توضیح آنها نیاز به یک مبحث مستقل‬
‫دارد‪.‬‬
C++ ‫ ورودی و خروجی اطالعات در‬7-6-3
#include <iostream.h>
void main() {
int age;
float average ;
cout << "Please enter your age and average : " ;
cin >> age >> average ;
cout << "You are " << age << "years old and your average is " << average;
}
Please enter your age and average : 19 16.72
You are 19 years old and your average is 16.72
‫‪ 7-7‬توابع کتابخانهای‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫همانطور که قبال نیز گفته شد‪ ،‬زبان ‪ C‬از زیر برنامهها نیز حمایت میکند‪ .‬هر زیر برنامه‬
‫در ‪ C‬یک تابع نامیده میشود‪ .‬تا کنون با توابعی همچون ‪ main‬و یا ‪ printf‬و ‪ scanf‬آشنا‬
‫شده ایم‪.‬‬
‫معموال عرضه کنندگان کامپایلرها و یا سایر فروشندگان نرم افزار‪ ،‬برخی از توابع عمومی‬
‫را که ممکن است مورد نیاز جمع کثیری از برنامهنویسان مختلف باشد را در قالب‬
‫کتابخانهای از توابع در اختیار برنامهنویسان میگذارند‪ .‬بعضی از این توابع کتابخانهای‬
‫مانند ‪ printf‬و ‪ scanf‬بصورت استاندارد درآمده و توسط عرضه کنندگان مختلف ارائه‬
‫میشوند‪.‬‬
‫در کامپایلر عرضه شده توسط شرکت بورلند )‪ (Borland C++ 3.1‬نیز کتابخانههای متعددی‬
‫از توابع برای شما عرضه شدهاند که بتدریج با آنها و کاربردشان آشنا خواهید شد‪.‬‬
‫نکته مهم آنستکه برای استفاده از این توابع ابتدا باید فایل سرآمد مربوط به آنها را نیز در‬
‫ابتدای برنامه خود اضافه نمایید ( با استفاده از ‪ .)#include‬هر فایل سرآمد شامل تعاریف‬
‫اولیه گروهی از توابع مرتبط با هم و دادههای مربوط به آنها بوده و در استاندارد قدیمی تر‬
‫دارای پسوند ‪ .h‬میباشد(در استاندارد جدید پسوند این فایلها حذف شده است)‪.‬‬
‫برخی از این فایلهای سرآمد عبارتند از‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪ : stdio.h‬توابع ورودی و خروجی استاندارد‬
‫‪ : math.h‬توابع ریاضی‬
‫‪ : graphics.h‬توابع مربوط به عملیات گرافیکی‬
‫‪ : string.h‬توابع مربوط به کار با رشته ها‬
‫مبانی کامپیوتر و برنامه سازی‬
‫فصل‌هشتم‌‪ :‬ساختارهای‌کنترلی‬
‫مدرس‌‪ :‬رضا‌رمضانی‬
‫‪ 8‬ساختارهای کنترلی‬
‫‪‬‬
‫‪‬‬
‫ساختارها در برنامهنویسی ساختیافته‬
‫‪ ‬ساختار ترتیب‬
‫‪ ‬ساختار انتخاب‬
‫‪ ‬ساختار تکرار‬
‫زبان ‪ C‬دارای ‪ 7‬نوع ساختار کنترلی است‬
‫‪ ‬ساختار ترتیب‪ :‬دستورهای زبان ‪ C‬در حالت عادی به همان ترتیبی که‬
‫نوشته شده اند‪ ،‬یکی پس از دیگری اجرا میشوند‪.‬‬
‫‪ 3 ‬نوع ساختار انتخاب‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫ساختار ‪ if‬یا ساختار تک انتخابی‬
‫ساختار ‪ if / else‬یا ساختار دو انتخابی‬
‫ساختار ‪ switch‬یا ساختار چند انتخابی‬
‫‪ 3‬نوع ساختار تکرار‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪while‬‬
‫‪for‬‬
‫‪do / while‬‬
‫‪ 8-1‬ساختار انتخاب ‪if‬‬
‫‪‬‬
‫این دستور به شکل زیر استفاده میشود‪:‬‬
‫;>‪if (<expresion>) <statement‬‬
‫‪‬‬
‫‪‬‬
‫نحوه کار بدینصورت است که ابتدا عبارت موجود در قسمت >‪ <expression‬ارزیابی‬
‫میشود‪ .‬در صورتیکه درست ارزیابی گردد‪ ،‬دستور قسمت >‪ <statement‬اجرا خواهد شد‬
‫و در صورتیکه نادرست باشد‪ ،‬بدون اینکه دستور قسمت >‪ <statement‬را اجرا کند به‬
‫دستور بعدی خواهد رفت‪.‬‬
‫این دستور میتواند بصورت زیر نیز استفاده گردد‪:‬‬
‫;>‪if (<expresion>) <statement 1‬‬
‫;>‪else <statement 2‬‬
‫‪‬‬
‫در اینصورت ابتدا عبارت موجود در قسمت >‪ <expression‬ارزیابی میشود‪ .‬در صورتیکه‬
‫درست ارزیابی گردد‪ ،‬دستور قسمت >‪ <statement 1‬اجرا خواهد شد‪ ،‬و در صورتیکه‬
‫نادرست باشد‪ ،‬دستور قسمت >‪ <statement 2‬اجرا خواهد شد‪ .‬در هر حال فقط یکی از‬
‫این دو قسمت اجرا خواهد گردید‪.‬‬
‫‪ 8-1‬ساختار انتخاب ‪if‬‬
‫‪‬‬
‫بعنوان مثال چنانچه متغیر ‪ grade‬حاوی نمره دانشجو باشد و بخواهیم بر‬
‫مبنای نمره وی‪ ،‬پیغام مناسبی چاپ کنیم‪ ،‬میتوانیم از دستور زیر‬
‫استفاده کنیم‪:‬‬
‫;)”! ‪if (grade >= 10) printf(“Passed‬‬
‫;)”!‪else printf(“Failed‬‬
‫‪‬‬
‫‪‬‬
‫در حالت عادی دستور ‪ if‬منتظر یک دستور در بدنه خود میباشد‪ ،‬اما‬
‫چنانچه میخواهید چندین دستور را در بدنه یک دستور ‪ if‬دهید‪ ،‬باید‬
‫آنها را در داخل آکوالد باز وبسته { } قرار دهید‪ .‬این مجموعه دستورات‬
‫را یک دستور مرکب میگویند‪.‬‬
‫بطور کلی در زبان ‪ C‬هرجا که میتوان یک دستور قرار داد‪ ،‬میتوان از‬
‫یک دستور مرکب نیز استفاده کرد‪ .‬به یک دستور مرکب‪ ،‬بلوک نیز گفته‬
‫میشود‪.‬‬
if ‫ ساختار انتخاب‬8-1
:‫ به شکل زیر است‬if ‫بنابراین صورت کلی دستور‬
if (<expression>) {
<statement 1> ;
<statement 2> ;
….
<statement n> ;
}
else {
<statement 1> ;
<statement 2> ;
…….
<statement m> ;
}

if ‫ ساختار انتخاب‬8-1
‫ را دریافت و ریشههای آن را‬2 ‫) برنامهای بنویسید که ضرایب یک معادله درجه‬1 ‫برنامه‬
.‫محاسبه و چاپ نماید‬
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <math.h>
void main() {
int a, b, c;
float x1, x2, delta;
clrscr();
printf(“Please enter a, b and c : “);
scanf(“%d %d %d”, &a, &b, &c);
if (a==0) {
printf(“wrong equation!”);
exit(1) ;
}
if (!a)

if ‫ ساختار انتخاب‬8-1
delta = b*b – 4*a*c;
if (delta < 0)
printf(“No answer !”);
else if (delta == 0) {
x1 = -b / (2*a);
printf(“There is one answer, x = %8.2f”,x1);
}
else {
delta = sqrt(delta);
x1 = (-b+delta) / (2*a);
x2 = (-b-delta) / (2*a);
printf(“There are two answers, x1= %8.2f and x2 = %8.2f”, x1, x2);
}
}
‫‪ 8-1‬ساختار انتخاب ‪if‬‬
‫‪‬‬
‫یک روش متداول استفاده از دستور ‪ ،if‬استفاده از ‪ if‬های تودرتو می‬
‫باشد‪.‬‬
‫;)"!‪if (grade >= 18) printf("good‬‬
‫;)"!‪else if (grade >= 15) printf("medium‬‬
‫;)"!‪else if (grade >= 12) printf("rather weak‬‬
‫;)"‪else if (grade >= 10) printf("weak‬‬
‫;)"!‪else printf("failed‬‬
‫‪‬‬
‫درچنین حالتی توصیه می گردد که شرطهای نادر را که امکان وقوع آنها‬
‫کم است‪ ،‬در انتهای کار بررسی نمایید‪ ،‬تا تعداد مقایسه کمتری صورت‬
‫پذیرد‪.‬‬
‫‪ 8-1‬ساختار انتخاب ‪if‬‬
‫‪‬‬
‫مشکل ‪ if‬های تودرتو ‪ :‬در دستور زیر‪ else ،‬به کدام ‪ if‬تعلق‬
‫دارد؟‬
‫)‪if (a < b‬‬
‫;>‪if (c < d) <statement 1‬‬
‫;>‪else <statement 2‬‬
‫‪‬‬
‫بطور کلی طبق قوانین گرامری‪ ،‬هر ‪ else‬مربوط به نزدیکترین ‪if‬‬
‫قبل از خود می باشد‪.‬‬
‫‪ 8-1‬ساختار انتخاب ‪if‬‬
‫‪‬‬
‫اما سوال این است که اگر بخواهیم ‪ else‬به ‪ if‬اول بازگردد از‬
‫چه روشی استفاده نماییم‪ .‬دراینصورت می توان از یکی از دو‬
‫روش زیر استفاده کرد‪:‬‬
‫{ )‪if (a < b‬‬
‫;>‪if (c < d) <statement 1‬‬
‫}‬
‫;>‪else <statement 2‬‬
‫)‪if (a < b‬‬
‫;>‪if (c < d) <statement 1‬‬
‫; ‪else‬‬
‫;>‪else <statement 2‬‬
if ‫ ساختار انتخاب‬8-1
.‫ عدد را دریافت و حداکثر آنها را چاپ کند‬3 ‫) برنامه ای بنویسید که‬2 ‫برنامه‬
#include <stdio.h>
void main() {
int a, b, c, max;
printf("Please enter 3 numbers :");
scanf("%d %d %d",&a, &b, &c);
if (a > b)
if (a > c) max = a;
else max= c;
else if (b > c) max = b;
else max = c;
printf("Maximum is %d",max);
}

while ‫ ساختار تکرار‬8-2
false
true ?)(≠=00) )
while (<expression>) {
<statement 1>
<statement 2>
…
<statement n>
}
<next statement>
while ‫ ساختار تکرار‬8-2
‫) برنامه ای بنویسید که یک عدد را دریافت و فاکتوریال آن را محاسبه و چاپ‬3 ‫برنامه‬
.‫نماید‬
#include <stdio.h>
void main() {
int i,number;
long int factorial;
printf("Please enter number :");
scanf("%d",&number);
factorial = 1;
i = 1;
while (i <= number) {
factorial *= i;
i ++;
}
printf("Factorial of %d is %ld“,number,factorial);
}

‫‪ 8-2‬ساختار تکرار ‪while‬‬
‫‪‬‬
‫برنامه ‪ )4‬برنامه ای بنویسید که یک متن را از کاربر دریافت و آن را با حروف‬
‫بزرگ چاپ کند‪.‬‬
‫>‪#include <stdio.h‬‬
‫{ )(‪void main‬‬
‫;‪char ch‬‬
‫; )(‪ch = getch‬‬
‫{ )‪while (ch != 13‬‬
‫)'‪if (ch >= 'a' && ch <= 'z‬‬
‫;‪ch -= 32‬‬
‫;)‪putch(ch‬‬
‫;)(‪ch = getch‬‬
‫}‬
‫}‬
‫‪ 8-3‬ساختار تکرار ‪for‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫همانگونه که در مثال مربوط به حل مسئله فاکتوریال دیده می شود‪،‬‬
‫گاهی نیاز به حلقه تکراری داریم که به تعداد دفعات مشخصی تکرار‬
‫گردد‪.‬‬
‫در چنین مواقعی با استفاده از یک متغیر شمارنده‪ ،‬تعداد تکرارها را تا‬
‫رسیدن به مقدار مورد نظر میشماریم و سپس به حلقه پایان‬
‫میدهیم‪ .‬به چنین حلقه هایی‪ ،‬تکرار تحت کنترل شمارنده یا تکرار‬
‫معین میگوییم‪ ،‬چرا که تعداد تکرارها از قبل مشخص است‪.‬‬
‫چنین حلقهای دارای ‪ 3‬جزء اصلی میباشد‪:‬‬
‫‪ ‬مقداردهی اولیه به متغیر شمارنده حلقه‬
‫‪ ‬شرط پایان حلقه (پایان شمارش)‬
‫‪ ‬نحوه افزایش متغیر شمارنده‬
‫از آنجا که در تمام حلقههایی که تکرار معین دارند‪ ،‬همین ساختار‬
‫استفاده میشود؛ در اکثر زبانهای برنامه سازی یک ساختار تکرار ویژه‪،‬‬
‫بنام حلقه ‪ ،for‬برای اینکار در نظر گرفته شده است‪.‬‬
‫‪ 8-3‬ساختار تکرار ‪for‬‬
‫;>‪for (<expression1> ; <expression2> ; <expression3>) <statement‬‬
‫نحوه افزایش متغیر حلقه‬
‫شرط تکرار حلقه‬
‫مقداردهی اولیه‬
for ‫ ساختار تکرار‬8-3
)≠
)‫بعد‬0)‫اجرا ( از اجرای دوم به‬
)‫ فقط در شروع حلقه‬false
(true
‫=(?اجرا‬
for (<exp1>;<exp2>;<exp3>) {
<statement 1>
<statement 2>
…
<statement n>
}
<next statement>
‫‪ 8-3‬ساختار تکرار ‪for‬‬
‫‪‬‬
‫درحقیقت هر حلقه ‪ for‬معادل با حلقه ‪ while‬زیر‬
‫است‪:‬‬
‫; >‪<exp1‬‬
‫{ )>‪while (<exp2‬‬
‫;>‪<statement‬‬
‫;>‪<exp3‬‬
‫}‬
‫‪ 8-3‬ساختار تکرار ‪for‬‬
‫‪‬‬
‫بعنوان یک مثال ساده‪ ،‬تکه برنامه زیر اعداد بین ‪ 0‬تا ‪ 100‬را چاپ مینماید‪:‬‬
‫;‪int count‬‬
‫)‪for (count = 0; count <= 100; count ++‬‬
‫;)‪printf(“%d “,count‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫اگر بخواهیم تنها مضارب ‪ 5‬چاپ شوند‪ ،‬حلقه را به شکل زیر تغییر می دهیم‪:‬‬
‫)‪for (count = 0; count <= 100; count += 5‬‬
‫حتی می توان مضارب ‪ 5‬را از آخر به اول چاپ کرد‪:‬‬
‫)‪for (count = 100; count >= 0; count -= 5‬‬
‫قسمت شرط می تواند یک شرط مرکب نیز باشد‪.‬‬
‫)‪for (count = 0; count < 100 && sw==1; count ++‬‬
‫نکته آخر اینکه قسمت مقدار دهی اولیه و افزایش متغیر نیز می توانند شامل چند عبارت‬
‫باشند که در اینصورت با کاما از یکدیگر جدا می شوند‪.‬‬
‫)‪for (a = 0, b = 100; b – a > 50; a++, b--‬‬
for ‫ ساختار تکرار‬8-3
.‫ عدد بزرگتر و مجموع کل اعداد را محاسبه و چاپ نماید‬2 ‫) برنامهای بنویسید که تعدادی عدد را از کاربر دریافت و‬5 ‫برنامه‬
#include <stdio.h>
void main() {
int i, n, number;
int sum, max1, max2;
printf(“please enter n : “);
scanf(“%d”,&n);
sum = 0;
max1 = max2 = -1;
for (i=0 ; i<n ; i++) {
printf(“enter number : “);
scanf(“%d”,&number);
sum += number;
if (number > max1) {
max2 = max1;
max1 = number;
}
else if (number > max2)
max2 = number;
} //end for
printf(“Sum = %d, Maximum 1=%d, Maximum 2= %d”, sum, max1, max2);
}

‫‪ 8-3‬ساختار تکرار ‪for‬‬
‫‪‬‬
‫نکته جالب در مورد حلقه ‪ for‬آنستکه می توان هریک از ‪ 3‬عبارت آن را‬
‫حذف کرد‪.‬‬
‫)‪for (; i<100; i++‬‬
‫);‪for (i=0; i<100‬‬
‫);‪for (; i<100‬‬
‫)‪for (i=0; ;i++‬‬
‫‪‬‬
‫در مورد آخر حتما باید در داخل حلقه با استفاده از دستور ‪( break‬که در‬
‫قسمتهای بعدی توضیح داده خواهد شد)‪ ،‬راهی برای خروج از حلقه‬
‫قرار داده شود‪.‬‬
do / while ‫ حلقه‬8-4
do {
<statement 1> ;
<statement 2> ;
…
<statement n> ;
} while (<expression>);
<next statement> ;
true )≠
false
(=0)
0)
?
‫‪ 8-4‬حلقه ‪do / while‬‬
‫‪‬‬
‫یک مثال کوچک‪:‬‬
‫‪ ‬فرض کنید از کاربر خواسته اید که اعالم کند آیا مایل به ادامه هست یا‬
‫خیر؟ وی باید پاسخ ‪ y‬یا ‪ n‬بدهد‪ ،‬اما ممکن است یک حرف اشتباه‬
‫(مانند ‪ )m‬وارد کند‪.‬‬
‫‪ ‬قصد داریم تکه برنامه ای بنویسیم که عمل دریافت پاسخ را تا زمانیکه‬
‫یک حرف درست وارد شود‪ ،‬تکرار کند‪.‬‬
‫‪ ‬مسلم است که باید ابتدا یک پاسخ وارد شود و سپس درستی آن‬
‫بررسی گردد‪.‬‬
‫;‪char answer‬‬
‫{ ‪do‬‬
‫;)"? )‪printf("Do you want to continue (y/n‬‬
‫;)(‪answer = getch‬‬
‫; )'‪} while (answer != 'y' && answer != 'n‬‬
do / while ‫ حلقه‬8-4
‫ برنامه ای بنویسید‬.‫( آماده شده است‬A, B, C and D) ‫) فرض کنید نمرات یک گروه از دانشجویان بصورت درجه بندی‬6 ‫برنامه‬
‫ در ضمن از آنجا که تعداد دانشجویان‬.‫که نمرات دانشجویان را دریافت و در پایان درصد هریک از نمرات را محاسبه و چاپ نماید‬
.‫) را وارد می نماید‬Quit ‫ (مخفف‬Q ‫ حرف‬،‫ کاربر در انتهای نمرات‬،‫از قبل مشخص نیست‬
#include <stdio.h>
void main() {
int aCount, bCount, cCount, dCount, n;
char grade;
aCount = bCount = cCount = dCount = n = 0;
do {
printf("Enter grade (Q for Quit) : ");
grade = getch() ;
n ++;
if (grade == 'A') aCount ++;
else if (grade == 'B') bCount ++;
else if (grade == 'C') cCount ++;
else if (grade == 'D') dCount ++;
else if (grade == 'Q') n --;
else {
printf("Wrong grade, try again.\n");
n --;
}
} while (grade != 'Q' ) ;

do / while ‫ حلقه‬8-4
printf("Statistics :\n");
printf("Grade A : %f percent\n", float(aCount)/float(n));
printf("Grade B : %f percent\n", float(bCount)/float(n));
printf("Grade C : %f percent\n", float(cCount)/float(n));
printf("Grade D : %f percent\n", float(dCount)/float(n));
} // end main
switch / case ‫ ساختار‬8-5
<expression> == <exp1> ?
false
switch (<expression>) {
case <exp1> : <statement 1> ;
<statement 2> ;
…
<statement n> ;
<expression> == <exp2> ?
case <exp2> : <statement 1> ;
<statement 2> ;
…
<statement n> ;
…
true
if there is no match
default : <statement 1> ;
<statement 2> ;
…
<statement n> ;
}
‫‪ 8-5‬ساختار ‪switch / case‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫توجه کنید که قسمت ‪ default‬اختیاری بوده و میتوان از آن استفاده نکرد‪.‬‬
‫این ساختار فقط برای عبارات کاراکتری و صحیح معتبر بوده و نمیتوان در آن از عبارات‬
‫اعشاری استفاده نمود‪.‬‬
‫نکته مهم دیگر در مورد این ساختار این است که چنانچه عبارت >‪ <expression‬با یک ثابت‬
‫مانند >‪ <constant i‬برابر باشد‪ ،‬آنگاه پس از اینکه دستورات مربوط به این حالت اجرا‬
‫گردید‪ ،‬اجرا ادامه یافته و دستورات مربوط حالتهای بعدی تا انتهای ‪ switch‬انجام خواهد‬
‫شد!‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫به عنوان مثال چنانچه عبارت >‪ <expression‬با ثابت >‪ <constant 2‬برابر باشد‪ ،‬پس از اجرای‬
‫دسترات مربوط به این حالت‪ ،‬دستورات حالتهای >‪ <constant 3‬و ‪ ...‬تا >‪ <constant m‬و حتی‬
‫قسمت ‪ default‬نیز اجرا خواهد گردید‪.‬‬
‫برای جلوگیری از این وضعیت که معموال دلخواه برنامهنویسان نیست‪ ،‬میتوان از دستور‬
‫‪ break‬استفاده کرد‪ .‬این دستور که بعدا در مورد آن توضیح بیشتری خواهیم داد‪ ،‬باعث‬
‫میشود که از ساختار ‪ switch‬خارج شده و به دستور پس از آن برویم‪.‬‬
‫بنابراین معموال برنامهنویسان در پایان دستورات هر ‪ ،case‬از یک دستور ‪ break‬استفاده‬
‫میکنند‪ .‬این کار باعث میشود که پس از اجرای دستورات مربوط به هر ‪ ،case‬با رسیدن‬
‫به دستور ‪ break‬بالفاصله از ساختار ‪ switch‬خارج شده و دستورات مربوط به ‪ case‬بعدی‬
‫اجرا نشوند‪.‬‬
‫‪ 8-5‬ساختار ‪switch / case‬‬
‫‪‬‬
‫اما چرا در زبان ‪ C‬از این روش استفاده شده است‬
‫بطوریکه برنامهنویسان مجبور به استفاده از دستور ‪break‬‬
‫شوند؟‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫جواب این است که میتوان با استفاده از این خاصیت‪ ،‬چندین‬
‫‪ case‬مختلف را با یکدیگر یای منطقی )‪ (or‬کرد‪.‬‬
‫فرض کنید چند ‪ case‬مختلف دارید که قصد دارید با وقوع هریک از‬
‫آنها‪ ،‬مجموعه دستورات مشترکی انجام شوند‪.‬‬
‫کافی است این ‪case‬ها را بصورت پشت سرهم قرار داده و‬
‫دستورات همگی آنها بجز ‪ case‬آخر را خالی قرار دهید‪.‬‬
‫حال دستورات مشترک را در ‪ case‬آخر قرار داده و در انتها نیز یک‬
‫دستور ‪ break‬بگذارید‪.‬‬
‫اکنون چنانچه عبارت با هریک از این ‪case‬ها برابر باشد‪ ،‬از آنجا که‬
‫هیچیک دارای دستور ‪ break‬نیستند‪ ،‬اجرا تا ‪ case‬آخر ادامه خواهد‬
‫یافت و در پایان دستورات مشترک اجرا خواهد شد‪.‬‬
‫‪ 8-5‬ساختار ‪switch / case‬‬
‫‪‬‬
‫بعنوان مثال فرض کنید یک متغیر صحیح بنام ‪ point‬داریم که امتیاز یک ورزشکار را بین ‪ 1‬تا‬
‫‪5‬مشخص مینماید‪ .‬اکنون قصد داریم بسته به امتیاز ورزشکار‪ ،‬پیام مناسبی را برای وی‬
‫چاپ نماییم‪ .‬امتیاز ‪ 1‬یا ‪ 2‬ضعیف‪ ،‬امتیاز ‪ 3‬متوسط‪ ،‬و امتیاز ‪ 4‬یا ‪ 5‬خوب ارزیابی میگردد‪.‬‬
‫ساختار زیر این کار را انجام میدهد‪.‬‬
‫{ )‪switch (point‬‬
‫‪case 1 :‬‬
‫;)"‪case 2 : printf("weak!\n‬‬
‫;‪break‬‬
‫;)"‪case 3 : printf("medium!\n‬‬
‫;‪break‬‬
‫‪case 4 :‬‬
‫;)"‪case 5 : printf("good!\n‬‬
‫;‪break‬‬
‫;)"!‪default : printf("out of range‬‬
‫}‬
switch / case ‫ ساختار‬8-5
‫ برنامه را‬.‫ بازنویسی نمایید‬switch / case ‫ را با استفاده از دستور‬6 ‫) برنامه‬7 ‫برنامه‬
.‫بگونه ای بنویسید که حروف بزرگ و کوچک هردو مورد قبول واقع شود‬
#include <stdio.h>
void main() {
int aCount, bCount, cCount, dCount, n;
char grade;
aCount = bCount = cCount = dCount = n = 0;
do {
printf("Enter grade (Q for Quit) : ");
grade = getch() ;
n ++;

switch / case ‫ ساختار‬8-5
switch (grade) {
case 'A' :
case 'a' : aCount ++;
break ;
case 'B' :
case 'b' : bCount ++;
break ;
case 'C' :
case 'c' : cCount ++;
break ;
case 'D' :
case 'd' : dCount ++;
break ;
case 'Q' :
case 'q' : n--;
break ;
default : printf("Wrong grade, try again.\n");
n --;
} //end switch
} while (grade != 'Q' && grade!=‘q’) ;
printf("Statistics :\n");
printf("Grade A : %f percent\n", float(aCount)/float(n));
printf("Grade B : %f percent\n", float(bCount)/float(n));
printf("Grade C : %f percent\n", float(cCount)/float(n));
printf("Grade D : %f percent\n", float(dCount)/float(n));
} // end main
switch / case ‫ ساختار‬8-5
‫ حاصل را‬،‫ یک عملگر و یک عدد دیگر را از کاربر دریافت و پس از اعمال عملگر برروی دو عدد‬،‫) برنامه ای بنویسید که یک عدد‬8 ‫برنامه‬
.‫چاپ نماید‬
#include <stdio.h>
void main() {
int number1, number2, result;
char op ;
printf("Please enter number1 operator number2 : ");
scanf("%d %c %d",&number1, &op, &number2);
result = 0;
switch (op) {
case '+' : result = number1 + number2 ; break;
case '-' : result = number1 - number2 ; break;
case '*' : result = number1 * number2 ; break;
case '/' : if (number2 != 0) result = number1 / number2 ;
else printf("There is no answer!\n");
break;
case '%' : if (number2 != 0) result = number1 % number2 ;
else printf("There is no answer!\n");
break;
default : printf("invalid operator!\n");
}
printf("Result = %d",result);
}

break ‫ دستور‬8-6
while (<expression>) {
>statements … <
true ?) ≠ 0 )
…
if (<exp1>) break ;
…
}
<next statement>
break ‫ دستور‬8-6
‫ بالفاصله به عملیات خاتمه‬،‫ و درصورتیکه عدد منفی وارد شد‬،‫ را بگونه ای تغییر دهید که فقط اعداد مثبت را بپذیرد‬5 ‫) برنامه‬9 ‫برنامه‬
.‫داده و نتایج تا همین نقطه را چاپ نماید‬
#include <stdio.h>
void main() {
int i, n, number;
int sum, max1, max2;
printf(“please enter n : “);
scanf(“%d”,&n);
sum = 0;
max1 = max2 = -1;
for (i=1 ; i<n ; i++) {
printf(“enter number : “);
scanf(“%d”,&number);
if (number < 0) break; // this is the difference
sum += number;
if (number > max1) {
max2 = max1;
max1 = number;
}
else if (number > max2)
max2 = number;
} //end for
printf(“Sum = %d, Maximum 1=%d, Maximum 2= d”, sum, max1, max2);
}

continue ‫ دستور‬8-6
while (<expression>) {
>statements … <
true ?) ≠ 0 )
…
if (<exp1>) continue ;
…
}
<next statement>
‫‪ 8-6‬دستور ‪continue‬‬
‫‪‬‬
‫بعنوان مثال‪ ،‬چنانچه بخواهیم برنامه ‪ 9‬را بگونه ای تغییر‬
‫دهیم که از اعداد منفی صرفنظر کند و آنها را در محاسبات‬
‫لحاظ نکند‪ ،‬کافیست دستور‬
‫را به دستور زیر تبدیل کنیم‪:‬‬
‫;‪if (number < 0) break‬‬
‫;‪if (number < 0) continue‬‬
‫‪‬‬
‫دراینصورت‪ ،‬چنانچه عدد منفی باشد‪ ،‬بدون اینکه‬
‫محاسبات بعدی انجام شوند‪ ،‬کنترل به ابتدای حلقه‬
‫بازگشته و عدد بعدی را دریافت می کند‪.‬‬
‫مبانی کامپیوتر و برنامه سازی‬
‫فصل‌نهم‌‪ :‬توابع‬
‫مدرس‌‪ :‬رضا‌رمضانی‬
‫‪ 9‬توابع‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫در مبحث الگوریتمها راجع به مزایای استفاده از زیرالگوریتمها صحبت‬
‫کردیم‪ .‬در برنامهنویسی نیز مباحث مشابهی در مورد زیربرنامهها وجود‬
‫دارد‪.‬‬
‫هنگامی که یک برنامه بیش از حد بزرگ می شود‪ ،‬برنامهنویسی و‬
‫مدیریت آن مشکل می گردد و ممکن است از عهده یک نفر خارج باشد‪.‬‬
‫در چنین شرایطی‪ ،‬برنامه را به چندین بخش کوچکتر (زیربرنامه) تقسیم‬
‫کرده و هریک را بعهده یک برنامهنویس می گذاریم‪ .‬هر قسمت به‬
‫تنهایی نوشته شده و رفع اشکال می گردد‪ .‬سپس این زیربرنامهها در‬
‫کنار یکدیگر قرار گرفته و تشکیل برنامه اصلی را می دهند‪.‬‬
‫اینکار نه تنها در تقسیم کار به ما کمک می کند‪ ،‬بلکه باعث می شود‬
‫خوانایی برنامه نیز باالتر رفته و اشکالزدایی آن ساده تر گردد‪ .‬عالوه‬
‫براین می توان یک زیربرنامه را یکبار نوشت‪ ،‬و سپس در برنامه های‬
‫مختلف از آن استفاده کرد که به این خاصیت‪ ،‬قابلیت استفاده مجدد از‬
‫نرم افزار گفته می شود‪.‬‬
‫‪ 9‬توابع‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫در زبان ‪ C‬به هر زیربرنامه‪ ،‬یک تابع گفته می شود‪.‬‬
‫یک تابع‪ ،‬تکه برنامه ای است که داده یا داده هایی را بعنوان ورودی دریافت‪ ،‬و داده یا‬
‫داده هایی را بعنوان خروجی باز می گرداند‪.‬‬
‫در زبان ‪ ،C‬هر برنامه از یک یا چند تابع تشکیل می گردد‪ ،‬که یکی از آنها باید بنام ‪main‬‬
‫نامیده گردد و برنامه از این تابع شروع خواهد گردید‪.‬‬
‫تابع ‪ main‬می تواند سایر توابع را فراخوانی نماید و هر یک از این توابع نیز می توانند به‬
‫نوبه خود‪ ،‬توابع دیگر را فراخوانی نمایند‪.‬‬
‫نکته جالب اینجاست که تابع فراخواننده نیازی به دانستن نحوه کار تابعی که فراخوانی‬
‫می کند‪ ،‬ندارد و تنها باید از نحوه فراخوانی و مقدار خروجی آن آگاه باشد‪ .‬این نحوه‬
‫پنهانسازی جزئیات پیاده سازی‪ ،‬نقش بسیار مهمی در مهندسی نرم افزار دارد‪.‬‬
‫تاکنون از توابع کتابخانه های استاندارد ‪ C‬استفاده نموده ایم‪ .‬کتابخانه استاندارد ‪C‬‬
‫مجموعه ای از توابع و نوع دادهها است که برای انجام عملیاتی که عموما مورد نیاز‬
‫برنامهنویسان است‪ ،‬طراحی شده و همراه کامپایلر در اختیار برنامهنویسان قرار داده‬
‫شده است‪.‬‬
‫بعنوان مثال توابع ورودی‪/‬خروجی مانند ‪ printf‬و ‪ scanf‬و یا توابع ریاضی مانند ‪ sqrt‬و ‪sin‬‬
‫که توسط بسیاری از برنامهنویسان مورد استفاده قرار می گیرند‪.‬‬
‫اما ازطرف دیگر‪ ،‬برنامهنویس نیز می تواند توابع مورد نیاز خود را تعریف کرده و از آنها در‬
‫برنامه خود استفاده نماید‪ .‬به این دسته از توابع‪ ،‬توابع کاربر می گوییم‪.‬‬
‫‪ 9-1‬توابع کاربر‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫بکار گیری توابع شامل دو قسمت است‪:‬‬
‫‪ ‬تعریف تابع‬
‫‪ ‬استفاده از تابع (فراخوانی تابع)‬
‫ما در مثالهای قبلی تابع ‪ main‬را در برنامه های خود تعریف می کردیم‪.‬‬
‫تعریف توابع دیگر نیز بطور مشابه می باشد‪ ،‬که جزئیات آن را بررسی‬
‫خواهیم کرد‪.‬‬
‫البته تابع اصلی بطور اتوماتیک در ابتدای اجرای برنامه فراخوانی می‬
‫گردد و برنامهنویس صریحا آن را احضار نمی کند‪ ،‬ولی سایر توابع باید از‬
‫داخل تابع دیگری (از جمله ‪ )main‬بطور صریح فراخوانی گردند‪.‬‬
‫البته ما قبال توابعی همچون‪ scanf‬و ‪ printf‬را فراخوانی کرده ایم‪ ،‬اما‬
‫جزئیات مربوط به نحوه فراخوانی را در قسمت بعدی بررسی می‬
‫نماییم‪.‬‬
‫‪ 9-1-1‬تعریف تابع‬
‫‪‬‬
‫قالب کلی تعریف تابع بصورت زیر است‪:‬‬
‫{ ) … ‪<return-type> <function-name> (<param-type param-name> ,‬‬
‫; >‪<local variable definitions‬‬
‫; >‪<statements‬‬
‫در این قسمت هرگونه متغیری که برای انجام‬
‫وظایف محوله به تابع مورد نیاز باشد‪،‬‬
‫}‬
‫تعریف می گردد‪ .‬این متغیرها نیز همانند‬
‫پارامترهای تابع‪ ،‬محلی محسوب می‬
‫شوند و در سایر توابع شناخته شده‬
‫پس از نام تابع و در داخل پرانتز‪ ،‬لیست‬
‫ها‬
‫نیستند‪ .‬با شروع هر تابع‪ ،‬این متغیر‬
‫پارامترهای تابع قرار می گیرد‪ .‬این‬
‫در این قسمت‪ ،‬دستورات‬
‫بطور اتوماتیک ایجاد شده و پس از خاتمه‬
‫تعدادی پارامتر‬
‫تعریف‬
‫مقدار شامل‬
‫نوعلیست‬
‫نوع داده‬
‫بازگشتی ‪:‬‬
‫دهنده بدنه تابع که‬
‫تشکیل‬
‫درمی‬
‫بین‬
‫آن از‬
‫روند‪.‬یک شناسه است که از‬
‫حقیقت‬
‫تابع‬
‫نام‬
‫موردشده‬
‫جدا‬
‫از یکدیگر‬
‫توسط '‪',‬‬
‫مقداری که با کاما‬
‫است‬
‫بازگشت‬
‫تابع‬
‫نظر‬
‫وظایف‬
‫به‬
‫مربوط‬
‫نامگذاری‬
‫قوانین‬
‫همان‬
‫و‬
‫نوع‬
‫شامل‬
‫پارامتر‬
‫هر‬
‫تعریف‬
‫‪.‬‬
‫اند‬
‫می دهد‬
‫داده می شود را نشان‬
‫نویس‪ .‬را انجام می‬
‫برنامه‬
‫شناسهها تبعیت می کند‪ .‬یک تابع‬
‫ا‬
‫پارامتره‬
‫‪.‬‬
‫باشد‬
‫می‬
‫سپس نام پارامتر دهند‪ ،‬قرار می گیرند‪.‬‬
‫‪.‬‬
‫گردد‬
‫حقیقت رابط بین تابع احضار کننده و‬
‫توسط نام خود فراخوانی می در‬
‫تابع احضار شونده هستند‪.‬‬
‫‪ 9-1-1‬تعریف تابع‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫مقدار بازگشتی تابع توسط دستور ‪ return‬به تابع فراخواننده برگشت‬
‫داده می شود‪ .‬برای این کار کافی است به شکل زیر عمل نماییم‪:‬‬
‫;>‪ return <exp‬و یا ; )>‪return (<exp‬‬
‫چنانچه تابع مقدار بازگشتی نداشته باشد‪ ،‬از کلمه کلیدی ‪ void‬بجای‬
‫نوع مقدار بازگشتی استفاده می نماییم‪ .‬دراینصورت‪ ،‬دستور ‪return‬‬
‫نیاز به مقدار بازگشتی ندارد‪.‬‬
‫البته چنانچه تابع بیش از یک مقدار بازگشتی داشته باشد‪ ،‬باید از‬
‫تکنیکهای گفته شده در قسمت بعد استفاده نماییم‪.‬‬
‫الزم به ذکر است که در قسمت دستورات تابع باید یک (یا چند)‬
‫دستور ‪ return‬داشته باشیم که کنترل را به تابع فراخواننده باز گرداند‪.‬‬
‫البته درمورد توابعی که مقداری را باز نمی گردانند‪ ،‬درصورت عدم‬
‫وجود دستور ‪ ،return‬کنترل پس از رسیدن به انتهای تابع یعنی } بطور‬
‫خودکار به تابع فراخواننده باز می گردد‪.‬‬
‫‪ 9-1-1‬تعریف تابع‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫نکته مهم دیگر مکان تعریف توابع در یک برنامه ‪ C‬است‪.‬‬
‫یک برنامه ‪ C‬می تواند دارای یک یا چند تابع باشد که‬
‫یکی از آنها باید حتما ‪ main‬نامیده شود و همانطور که قبال‬
‫نیز گفته شد‪ ،‬اجرای برنامه از این تابع آغاز می گردد‪.‬‬
‫توابع می توانند به هر ترتیبی تعریف شوند‪ ،‬اما معموال تابع‬
‫‪ main‬در آخر توابع دیگر تعریف می گردد؛ گرچه این مسئله‬
‫اجباری نیست‪.‬‬
‫توابع باید بصورت پشت سر هم تعریف گردند و برخالف‬
‫بعضی از زبانهای دیگر‪ ،‬نمی توان یک تابع را در داخل تابع‬
‫دیگر تعریف کرد‪ .‬بعبارت دیگر‪ ،‬کلیه توابع در یک سطح قرار‬
‫دارند و هیچ تابعی‪ ،‬شامل تابع دیگر نمی باشد‪.‬‬
‫‪ 9-1-2‬فراخوانی توابع‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫برای فراخوانی یک تابع باید از نام آن بعالوه لیست‬
‫آرگومانهای متناسب با پارامترهای تابع استفاده کرد‪.‬‬
‫نکته مهم آنستکه باید تعداد‪ ،‬ترتیب و نوع آرگومانهای‬
‫ارسالی با پارامترهای متناظرشان در تعریف تابع‪ ،‬منطبق‬
‫باشد‪ .‬در غیراینصورت ممکن است خطای نحوی و یا حتی‬
‫خطای منطقی رخ دهد‪.‬‬
‫هنگامیکه یک تابع فراخوانی می گردد‪ ،‬اجرای تابع‬
‫فراخواننده بطور موقت متوقف شده و کنترل اجرا به تابع‬
‫فراخوانی شده منتقل می گردد‪ .‬پس از اتمام تابع‬
‫فراخوانی شده و اجرای دستور ‪ return‬توسط آن‪ ،‬کنترل‬
‫اجرا به تابع فراخواننده بازگشته و اجرا را از دستور بعدی‪،‬‬
‫از سر می گیرد‪.‬‬
‫‪ 9-1-2‬فراخوانی توابع‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫چنانچه تابع هیچ مقداری را بازنگرداند‪ ،‬می توان آن را بصورت یک دستور‬
‫مستقل فراخوانی کرد‪ .‬بعنوان مثال ‪:‬‬
‫; )(‪clrscr‬‬
‫اما توابعی که یک مقدار خروجی را باز می گردانند‪ ،‬می توان در یک عبارت‬
‫نسبت دهی یا محاسباتی نیز بکاربرد‪ .‬بعنوان مثال می توان تابع ‪( sqrt‬که یک‬
‫عدد را دریافت و جذر آن را باز می گرداند) را بصورت زیر استفاده کرد ‪:‬‬
‫; )‪a = sqrt(10‬‬
‫; ‪a = 2 * sqrt(b) + c‬‬
‫نکته مهم آنستکه چنانچه فراخوانی تابع توسط مقدار باشد‪ ،‬آنگاه می توان‬
‫بجای یک متغیر یا یک ثابت‪ ،‬یک عبارت محاسباتی را نیز به تابع ارسال کرد‪.‬‬
‫بعنوان مثال فراخوانی زیر مجاز است‪:‬‬
‫; )‪a = sqrt(2*b+8‬‬
‫در اینحالت‪ ،‬ابتدا عبارت محاسباتی ارزیابی شده و سپس مقدار آن بعنوان‬
‫آرگومان به تابع ارسال می گردد‪ .‬البته درصورتیکه فراخوانی توسط ارجاع باشد‪،‬‬
‫فقط یک متغیر می تواند به تابع ارسال گردد و عبارت محاسباتی و یا حتی یک‬
‫ثابت به تنهایی نیز مورد قبول نخواهد بود‪.‬‬
‫ فراخوانی توابع‬9-1-2
void main() {
int a, b;
a=5;
b = square(a) ;
a:5
x:5
b : 25
return 25
printf(“a=%d and b=%d”,a,b)
}
a = 5 and b = 25
int square(int x) {
return(x * x) ;
}
‫ چند نمونه از توابع‬9-2
.‫ را محاسبه نماید‬k ‫ به‬n ‫) برنامه ای بنویسید که مقدار ترکیب‬1 ‫مثال‬
#include <stdio.h>
long int factorial(int number) {
int i;
long int f = 1L ;
for (i=1; i<=number; i++)
f *= i ;
return(f) ;
}
void main() {
int n, k ;
long int result;
printf("please enter n and k : ");
scanf("%d %d", &n, &k) ;
result = factorial(n) / ( factorial(k) * factorial(n-k) ) ;
printf("result = %ld", result) ;
}

‫ چند نمونه از توابع‬9-2
‫ برای اینکار از‬.‫) برنامه ای بنویسید که تعدادی عدد را دریافت و سپس حداکثر آنها را محاسبه و چاپ نماید‬2 ‫مثال‬
.‫ استفاده نمایید‬،‫ که دو عدد را دریافت و حداکثر آنها را باز می گرداند‬getMax ‫تابعی بنام‬
#include <stdio.h>
#include <values.h>
int getMax(int a, int b) {
if (a>b) return(a);
else return(b) ;
}
void main() {
int i, n, max, number ;
printf("Please enter n: ");
scanf("%d",&n);
max = -MAXINT-1;
for (i=0; i<n; i++) {
printf("Please enter number: ");
scanf("%d", &number);
max = getMax(number, max);
}
printf("Maximum is : %d", max);
}

‫‪ 9-2‬چند نمونه از توابع‬
‫‪‬‬
‫مثال ‪ )3‬تابعی بنویسید که یک خط ‪ 40‬تایی از عالمت * را چاپ نماید‪ .‬سپس با استفاده‬
‫از آن برنامهای بنویسید که ابتدا پیام ‪ Hello‬و سپس خط جداکننده فوق را چاپ کند‪.‬‬
‫>‪#include <stdio.h‬‬
‫{ )(‪void starLine‬‬
‫;‪int i‬‬
‫)‪for (i=0; i<40; i++‬‬
‫;)"*"(‪printf‬‬
‫;)"‪printf("\n‬‬
‫}‬
‫{ )(‪void main‬‬
‫;)"‪printf("Hello\n‬‬
‫; )(‪starLine‬‬
‫}‬
‫‪ 9-2‬چند نمونه از توابع‬
‫‪‬‬
‫البته درچنین مواردی‪ ،‬بهتر است برنامهنویس تابع رسم خط را بصورت کلی تری بنویسد؛ بطوریکه در‬
‫سایر برنامهها نیز بتواند از آن استفاده نماید‪ .‬مثال چنانچه تابع را بگونهای بنویسیم که کاراکتر‬
‫جداکننده و تعداد آن را بعنوان ورودی دریافت کند‪ ،‬حالت کلی تری پیدا خواهد کرد‪.‬‬
‫>‪#include <stdio.h‬‬
‫{ )‪void separatorLine(char sep,int n‬‬
‫;‪int i‬‬
‫)‪for (i=0; i<n; i++‬‬
‫;)‪printf("%c",sep‬‬
‫;)”‪printf(“\n‬‬
‫}‬
‫{ )(‪void main‬‬
‫;)"‪printf("Hello\n‬‬
‫; )‪separatorLine('-' ,60‬‬
‫}‬
‫‪ 9-3‬نمونه اولیه توابع‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫همانطور که قبال گفته شد‪ ،‬توابع میتوانند به هر ترتیبی تعریف شوند‪ ،‬ولی معموال تابع‬
‫‪ main‬در انتها قرار میگیرد‪.‬‬
‫اما اگر بخواهیم دقیقتر صحبت کنیم‪ ،‬تعریف هر تابع باید قبل از فراخوانی آن صورت پذیرد‪.‬‬
‫چنانچه تابعی قبل از آنکه تعریف شود‪ ،‬فراخوانی گردد؛ یک خطای کامپایل رخ خواهد داد‪.‬‬
‫اما میتوان این خطا را با استفاده از نمونه اولیه تابع )‪ (prototype‬که به آن پیش تعریف‬
‫نیز گفته میشود‪ ،‬از بین برد‪.‬‬
‫یک نمونه اولیه تابع‪ ،‬شامل نام تابع‪ ،‬نوع دادهای که باز میگرداند و همچنین تعداد و نوع‬
‫پارامترهای تابع است‪ .‬برای مثال‪ ،‬نمونه اولیه تابع ‪ factorial‬بصورت زیر است‪:‬‬
‫; )‪long int factorial(int‬‬
‫این نمونه اولیه میگوید که تابع ‪ factorial‬یک آرگومان از نوع ‪ int‬دریافت و یک مقدار از نوع‬
‫‪ long int‬باز میگرداند‪ .‬عالمت ; انتهای دستور نشان میدهد که این فقط یک نمونه اولیه‬
‫بوده و شامل تعریف بدنه تابع نمیباشد‪.‬‬
‫نمونه اولیه یک تابع حتما باید با تعریف آن (که در قسمتهای بعدی برنامه آمده است)‬
‫یکسان باشد و گرنه یک خطای کامپایل رخ خواهد داد‪.‬‬
‫کامپایلر از نمونه اولیه تابع برای بررسی درستی نحوه فراخوانی تابع (از نظر تعداد و نوع‬
‫آرگومانها) استفاده میکند‪.‬‬
‫‪ 9-4‬انواع فراخوانی توابع‬
‫‪‬‬
‫بطورکلی‪ ،‬به دو روش میتوان یک تابع را فراخوانی کرد‪:‬‬
‫‪‬‬
‫‪‬‬
‫فراخوانی توسط مقدار )‪(Call by Value‬‬
‫فراخوانی توسط ارجاع )‪(Call by Reference‬‬
‫‪ 9-4-1‬فراخوانی توسط مقدار‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫در فراخوانی توسط مقدار‪ ،‬آرگومانهای ارسالی توسط تابع فراخواننده‪،‬‬
‫در پارامترهای متناظر تابع فراخوانی شده‪ ،‬کپی میگردند‪.‬‬
‫بنابراین تابع فراخوانی شده عملیات خود را برروی یک کپی از‬
‫آرگومانهای ارسالی انجام میدهد‪ .‬درنتیجه‪ ،‬در صورت انجام هرگونه‬
‫تغییری برروی این کپی توسط تابع فراخوانی شده‪ ،‬متغیر اصلی در تابع‬
‫فراخواننده تغییر نخواهد کرد‪.‬‬
‫مزیت این نوع فراخوانی در این است که میتوان بدون هیچ نگرانی از‬
‫تغییر ناخواسته متغیرها‪ ،‬آنها را به هر تابعی ارسال کرد‪ ،‬چرا که فقط‬
‫یک کپی از آنها به تابع ارسال میشود‪.‬‬
‫در زبان ‪ ،C‬در حالت عادی فراخوانی توسط مقدار صورت میپذیرد‪.‬‬
‫برای روشن شدن موضوع به مثال اسالید بعدی توجه کنید‪.‬‬
‫ فراخوانی توسط مقدار‬9-4-1
void test(int a, int b) {
printf("Function test : a=%d and b=%d \n",a,b);
a ++ ;
b *= 2;
printf("Function test : a=%d and b=%d \n",a,b);
}
void main() {
int x, y;
x = 10;
y = 8;
printf("Function main : x=%d and y=%d \n",x,y);
test(x,y) ;
printf("Function main : x=%d and y=%d \n",x,y);
}
Function main : x=10 and y=8
Function test : a=10 and b=8
Function test : a=11 and b=16
Function main : x=10 and y=8
‫‪ 9-4-1‬فراخوانی توسط مقدار‬
‫{ )‪void test(int a, int b‬‬
‫…‬
‫‪a 10 11‬‬
‫…‬
‫…‬
‫‪b 8 16‬‬
‫…‬
‫}‬
‫‪10‬‬
‫‪8‬‬
‫‪ a‬و‌‪ b‬تغییر‌کرده‌اند‪،‬‬
‫اما‌متغیرهای‌اصلی‌‪ x‬و‌‪ y‬بدون‌تغییر‌مانده‌اند‪.‬‬
‫{ )(‪void main‬‬
‫…‬
‫…‬
‫‪x‬‬
‫…‬
‫‪y‬‬
‫…‬
‫…‬
‫; )‪test(x,y‬‬
‫…‬
‫}‬
‫‪ 9-4-2‬فراخوانی توسط ارجاع‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫در این روش فراخوانی‪ ،‬خود آرگومانهای اصلی (بجای یک کپی از آنها) به تابع‬
‫فراخوانی شده ارسال می گردد‪.‬‬
‫در نتیجه‪ ،‬هرگونه تغییری در پارامترهای تابع فراخوانی شده‪ ،‬مقدار آرگومانهای‬
‫اصلی در تابع فراخوانی کننده را نیز تغییر خواهد داد‪.‬‬
‫در ‪ C‬اولیه‪ ،‬تنها راه فراخوانی توابع توسط ارجاع‪ ،‬استفاده از متغیرهای اشاره‬
‫گر بود که در فصول بعدی این روش بررسی خواهد گردید‪ .‬اما در استاندارد جدید‬
‫‪ ،C‬می توان این کار را به روش ساده تر ی و با استفاده از متغیرهای‬
‫ارجاعی انجام داد‪.‬‬
‫یک متغیر ارجاعی‪ ،‬در حقیقت یک نام مترادف و یا یک جایگزین برای یک‬
‫متغیر دیگر است‪.‬‬
‫برای تعریف یک متغیر ارجاعی از عالمت & پس از نوع متغیر مورد ارجاع‬
‫استفاده می کنیم‪ .‬بعنوان مثال به نمونه زیر توجه کنید‪:‬‬
‫;‪int a = 10‬‬
‫;‪int &r = a‬‬
‫از این پس متغیر ‪ ،r‬نام دیگری برای متغیر ‪ a‬محسوب می شود و هرگونه‬
‫تغییری در ‪ ،r‬باعث تغییر ‪ a‬نیز خواهد شد‪.‬‬
‫ فراخوانی توسط ارجاع‬9-4-2
#include <stdio.h>
void main() {
int a = 10;
int &r = a;
r ++;
printf("a=%d and r=%d \n",a,r);
a ++;
printf("a=%d and r=%d \n",a,r);
}
a = 11 and r = 11
a = 12 and r = 12
‫‪ 9-4-2‬فراخوانی توسط ارجاع‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫برای ارسال آرگومانها توسط ارجاع‪ ،‬کافی است پارامترهای تابع مورد‬
‫نظر را بصورت متغیر ارجاعی تعریف نماییم‪.‬‬
‫در اینصورت‪ ،‬این پارامترها در حقیقت یک ارجاع به آرگومانهای ارسالی‬
‫خواهند بود و در نتیجه هرگونه تغییری در آنها‪ ،‬آرگومانهای اصلی در تابع‬
‫فراخوانی کننده را نیز تغییر خواهد داد‪.‬‬
‫بعبارت بهتر‪ ،‬در این روش بجای آنکه یک کپی از آرگومانها در پارامترها‬
‫قرار گیرد‪ ،‬خود آرگومانها به تابع فراخوانی شده ارسال خواهند شد‪.‬‬
‫بعنوان مثال‪ ،‬همان تابع قسمت قبلی را که بصورت فراخوانی توسط‬
‫مقدار عمل می کرد‪ ،‬مجددا به روش فراخوانی با ارجاع باز نویسی می‬
‫کنیم تا تفاوت این دو مشخص گردد‪.‬‬
‫ فراخوانی توسط ارجاع‬9-4-2
void test(int &a, int &b) {
printf("Function test : a=%d and b=%d \n",a,b);
a ++ ;
b *= 2;
printf("Function test : a=%d and b=%d \n",a,b);
}
void main() {
int x, y;
x = 10;
y = 8;
printf("Function main : x=%d and y=%d \n",x,y);
test(x,y) ;
printf("Function main : x=%d and y=%d \n",x,y);
}
Function main : x=10 and y=8
Function test : a=10 and b=8
Function test : a=11 and b=16
Function main : x=11 and y=16
‫ فراخوانی توسط ارجاع‬9-4-2
void main() {
…
x 10 11
…
…
y 8 16
…
…
test(x,y) ;
…
}
void test(int &a, int &b) {
…
a
…
…
b
…
}
‫‪ 9-4-2‬فراخوانی توسط ارجاع‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫اکنون سوال اصلی این است که در چه مواردی از فراخوانی توسط‬
‫ارجاع استفاده کنیم؟‬
‫فراخوانی توسط ارجاع در دو مورد کاربرد دارد‪ ،‬که هریک را با ذکر یک‬
‫مثال توضیح می دهیم‪.‬‬
‫می دانیم که هر تابع فقط می تواند یک مقدار را توسط دستور ‪ return‬باز‬
‫گرداند‪ .‬در مواردی که تابع باید بیش از یک مقدار را باز گرداند‪ ،‬تنها راه‬
‫استفاده از فراخوانی توسط ارجاع است‪.‬‬
‫بدین صورت که تعدادی پارامتر خروجی را بصورت ارجاعی به تابع‬
‫ارسال می کنیم‪ ،‬و تابع پس از انجام محاسبات‪ ،‬خروجی را در این‬
‫متغیرها قرار داده و باز می گرداند‪.‬‬
‫از آنجا که پارامترها توسط ارجاع ارسال شده اند‪ ،‬تغییرات اعمال شده‬
‫توسط تابع (قرار دادن مقدار خروجی در آنها) به تابع فراخواننده منتقل‬
‫خواهد شد‪.‬‬
‫ فراخوانی توسط ارجاع‬9-4-2
‫) برنامه ای بنویسید که ضرایب یک معادله درجه دوم را دریافت و ریشه های آن را محاسبه وچاپ‬5 ‫مثال‬
.‫نماید‬
#include <stdio.h>
#include <math.h>
int equation(int a, int b, int c, float &x1, float &x2) {
float delta;
if (a==0) return(0) ; //‫معادله جواب ندارد‬
delta = b*b – 4*a*c ;
if (delta < 0) return(0) ; //‫معادله جواب ندارد‬
else if (delta == 0) {
//‫معادله یک جواب دارد‬
x1 = -b / (2*a) ;
return(1) ;
}
else {
//‫معادله دو جواب دارد‬
delta = sqrt(delta) ;
x1 = (-b + delta) / (2*a) ;
x2 = (-b – delta) / (2*a) ;
return(2) ;
}
}

‫ فراخوانی توسط ارجاع‬9-4-2
void main() {
int resultNo, coef1, coef2, coef3;
float result1,result2 ;
printf("Please enter coefficients: ");
scanf("%d %d %d", &coef1, &coef2, &coef3);
resultNo = equation(coef1,coef2,coef3,result1,result2);
if (resultNo == 0)
printf("There is no answer! \n");
else if (resultNo==1)
printf("There is 1 answer, x=%f \n", result1);
else printf("There are 2 answers, x1=%f and x2=%f \n",
result1, result2);
}
Please enter coefficients : 3 -7 2
There are two answers, x1 = 2.000000 and x2 = 0.333333
‫‪ 9-4-2‬فراخوانی توسط ارجاع‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫کاربرد دوم در مواردی است که آرگومان ارسالی به تابع‪،‬‬
‫همزمان نقش ورودی و خروجی را دارا باشد‪.‬‬
‫یعنی آرگومان عالوه بر آنکه مقداری را به تابع ارسال‬
‫می کند‪ ،‬ممکن است مقداری را به تابع فراخواننده نیز‬
‫بازگرداند‪ .‬مثال زیر موضوع را روشن تر میکند‪.‬‬
‫مثال ‪ )6‬برنامه ای بنویسید که دو عدد را از کاربر دریافت‬
‫و با استفاده از یک تابع‪ ،‬مقدار آن دو را جابجا نموده و‬
‫حاصل را چاپ نماید‪.‬‬
‫به حل این مثال در اسالید بعدی توجه کنید‪.‬‬
‫ فراخوانی توسط ارجاع‬9-4-2
#include <stdio.h>
void swap(int &a, int &b) {
int temp;
temp = a;
a = b;
b = temp;
}
void main() {
int x, y;
printf("enter x : ");
scanf("%d", &x);
printf("enter y : ");
scanf("%d", &y);
printf("x = %d and y = %d \n", x, y);
swap(x,y);
printf("now x = %d and y = %d \n", x, y);
}
enter x : 8
enter y : 12
x = 8 and y = 12
now x = 12 and y = 8
‫‪ 9-5‬حوزه شناخت متغیر‬
‫‪‬‬
‫‪‬‬
‫یکی از مسائل مهم در مورد متغیرها‪ ،‬حوزه شناخت )‪ (Scope‬متغیر است که تعیین می‬
‫نماید متغیر در چه قسمتهایی از برنامه شناخته شده است و در چه قسمتهایی قابل‬
‫استفاده نیست‪.‬‬
‫بطور کلی متغیرها به دو دسته تقسیم می شوند‪:‬‬
‫‪‬‬
‫متغیرهای محلی )‪ :(local variable‬متغیرهایی هستند که در داخل یک بالک }{ تعریف شدهاند و‬
‫فقط در محدوده همان بالک شناخته شده هستند‪.‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫نمونه این دسته از متغیرها‪ ،‬متغیرهای محلی توابع هستند که در داخل بالک مربوط به تابع تعریف‬
‫می شوند و فقط در همان تابع شناخته شده هستند‪.‬‬
‫البته دو تابع مختلف می توانند دارای متغیرهای همنام باشند‪ ،‬که در اینصورت این دو متغیر مجزا‬
‫بوده و هیچ ارتباطی به یکدیگر ندارند‪.‬‬
‫الزم به ذکر است که پارامترهای یک تابع نیز جزو متغیرهای محلی آن تابع محسوب می گردند‪.‬‬
‫نکته دیگر اینکه گرچه معموال متغیرهای محلی در داخل تابع تعریف می شوند‪ ،‬اما هر بلوک دلخواه‬
‫می تواند دارای متغیرهای محلی باشد‪ .‬مثال یک دستور ‪ if‬مرکب می تواند در داخل بلوک خود‪،‬‬
‫متغیرهای محلی را تعریف کند که فقط در داخل همان بلوک شناخته شده باشند (به مثال بعدی‬
‫مراجعه کنید)‪.‬‬
‫متغیرهای سراسری )‪ :(global variable‬متغیرهای هستند که در خارج کلیه بلوکها (و توابع از‬
‫جمله ‪ )main‬تعریف شدهاند و در کل توابع برنامه (در حقیقت کل فایل مربوط به برنامه) شناخته‬
‫شده و قابل استفاده می باشند‪.‬‬
‫‪‬‬
‫از آنجا که کلیه توابع برنامه به این متغیرها دسترسی دارند‪ ،‬هرگونه تغییری در این متغیرها توسط‬
‫یکی از توابع‪ ،‬در سایر توابع نیز قابل رویت خواهد بود‪.‬‬
‫‪ 9-5‬حوزه شناخت متغیر‬
‫متغیر سراسری‬
‫متغیر محلی برای ‪main‬‬
‫متغیر سراسری ‪ a‬در اینجا قابل‬
‫دسترسی است‬
‫به متغیر محلی ‪ b‬نیز میتوان‬
‫بلوک ‪if‬‬
‫برای‬
‫محلی‬
‫متغیر‬
‫عالوه بر متغیر‬
‫قسمت‬
‫در این‬
‫داشت‬
‫دسترسی‬
‫محلی ‪ ،c‬به متغیرهای ‪ a‬و ‪ b‬نیز‬
‫میتوان دسترسی پیدا کرد‪.‬‬
‫در این قسمت به متغیرهای ‪ a‬و ‪ b‬دسترسی‬
‫داریم‪ ،‬اما دیگر نمیتوان به متغیر محلی ‪c‬‬
‫مربوط به بلوک ‪ if‬دسترسی پیدا کرد‪.‬‬
‫متغیر محلی برای ‪test‬‬
‫این متغیر ارتباطی با متغیر ‪ c‬در‬
‫تابع ‪ main‬ندارد‬
‫در این قسمت نه تنها میتوان به‬
‫متغیر محلی ‪ c‬دسترسی داشت‪،‬‬
‫بلکه متغیر سراسری ‪ a‬نیز قابل‬
‫استفاده است‪ .‬اما به متغیرهای‬
‫محلی سایر توابع‪ ،‬مانند متغیر ‪b‬‬
‫دسترسی نداریم‪.‬‬
‫; ‪int a‬‬
‫{ )(‪void main‬‬
‫;‪int b‬‬
‫; ‪a = 10‬‬
‫;‪b=5‬‬
‫{ )‪if (a>0‬‬
‫; ‪int c‬‬
‫;‪c = 8‬‬
‫‪….‬‬
‫}‬
‫}‬
‫{ )(‪void test‬‬
‫; ‪int c‬‬
‫;‪c = 5‬‬
‫;‪a = 20‬‬
‫}‬
‫‪ 9-5‬حوزه شناخت متغیر‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫نکته مهم آنستکه درصورتیکه یک تابع دارای یک متغیر محلی همنام با‬
‫یک متغیر سراسری باشد‪ ،‬در اینصورت هرگونه ارجاع به این نام‬
‫مشترک‪ ،‬به متغیر محلی رجوع خواهد کرد (به مثال قسمت بعدی‬
‫رجوع شود)‪.‬‬
‫چه موقع از متغیر های سراسری استفاده کنیم؟‬
‫‪ ‬متغیرهای سراسری هنگامی مفید هستند که یک داده بین چندین تابع‬
‫بصورت مشترک استفاده شود‪ .‬در این حالت نیازی به ارسال متغیرهای‬
‫مشترک از طریق پارامترها نمی باشد‪.‬‬
‫‪ ‬اما متاسفانه از آنجا که متغیرهای سراسری در کلیه توابع در دسترس‬
‫هستند‪ ،‬ممکن است بصورت ناخواسته دچار تغییر شوند‪.‬‬
‫‪ ‬عالوه براین اشکال زدایی آنها نیز بسیار مشکل است‪ ،‬چرا که محل بروز‬
‫خطا مشخص نیست و هریک از توابع ممکن است مقدار متغیر را تغییر‬
‫داده باشند‪.‬‬
‫بنابراین در برنامهنویسی ساختیافته توصیه می گردد تا حد ممکن از‬
‫متغیرهای سراسری استفاده نکنید‪.‬‬
‫‪ 9-6‬ردههای ذخیره سازی‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫رده ذخیره سازی یک متغیر‪ ،‬مدت زمان حضور آن را در برنامه تعیین‬
‫مینماید‪ .‬بعبارت دیگر‪ ،‬رده ذخیره سازی تعیین مینماید یک متغیر چه‬
‫موقع بوجود میآید و چه زمانی از بین میرود‪.‬‬
‫در هنگام تعریف یک متغیر‪ ،‬باید به همراه نوع آن‪ ،‬رده ذخیره سازی آن را‬
‫نیز مشخص کرد‪.‬‬
‫چنانچه اینکار صورت نپذیرد‪ ،‬کامپایلر از رده ذخیره سازی پیش فرض‬
‫استفاده خواهد کرد‪ .‬از آنجایی که در کلیه مثالهایی که تاکنون ذکر‬
‫شده اند‪ ،‬رده ذخیره سازی بطور صریح مشخص نشده بود‪ ،‬همگی از‬
‫رده پیش فرض در نظر گرفته شده اند‪.‬‬
‫بطور کلی دو رده ذخیره سازی برای متغیرها وجود دارد‪:‬‬
‫‪ ‬رده ذخیره سازی اتوماتیک‬
‫‪ ‬رده ذخیره سازی ایستا‬
‫‪ 9-6-1‬رده ذخیره سازی اتوماتیک‬
‫‪‬‬
‫‪‬‬
‫متغیرهای متعلق به رده ذخیره سازی اتوماتیک‪ ،‬هنگام ورود به بلوکی که این متغیرها در‬
‫آن اعالن شده اند‪ ،‬ایجاد شده و در طول اجرای این بلوک در حافظه وجود دارند؛ به محض‬
‫خاتمه بلوک‪ ،‬این متغیرها نیز از بین رفته و حافظه آنها پس گرفته میشود‪ .‬متغیرهای‬
‫محلی (شامل پارامترهای توابع)‪ ،‬معموال از این رده میباشند‪ .‬بعنوان مثال‪ ،‬متغیرهای‬
‫محلی یک تابع‪ ،‬به محض فراخوانی تابع ایجاد میشوند و در حین اجرای تابع در حافظه‬
‫حضور دارند‪ .‬با پایان یافتن اجرای تابع‪ ،‬این متغیرها نیز از بین میروند‪ .‬این مسئله باعث‬
‫میشود که صرفه جویی قابل توجهی در حافظه داشته باشیم‪ .‬چرا که هرگاه به متغیری‬
‫نیاز داریم ایجاد شده و با پایان یافتن کار نیز حافظه آن آزاد میشود‪.‬‬
‫برای تعریف یک متغیر اتوماتیک‪ ،‬باید از کلمه کلیدی ‪ ،auto‬قبل از مشخصه نوع متغیر‪،‬‬
‫استفاده نماییم‪ .‬بعنوان مثال به نمونه زیر دقت نمایید ‪:‬‬
‫{ )‪void test(int a, int b‬‬
‫;‪auto int i‬‬
‫;‪float k‬‬
‫…‬
‫دستورات تابع ‪//‬‬
‫}‬
‫‪ 9-6-1‬رده ذخیره سازی اتوماتیک‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫نوع دیگری از متغیرهای اتوماتیک نیز وجود دارد‪ .‬همانطور که میدانید کلیه متغیرهای‬
‫برنامه در حافظه اصلی کامپیوتر )‪ (RAM‬ذخیره میگردند‪ .‬اما پردازنده اصلی )‪ (CPU‬برای‬
‫پردازش دادهها باید ابتدا آنها را از حافظه اصلی به یک حافظه داخلی بنام ثبات یا‬
‫‪ Register‬منتقل نماید‪.‬‬
‫گرچه سرعت دسترسی به حافظه اصلی بسیار باال است‪ ،‬اما بهرحال مدتی زمان صرف‬
‫این انتقال میشود‪ .‬پس از آن پردازنده میتواند با سرعت بسیار باال عملیات را برروی‬
‫ثباتهای داخلی خود انجام داده و حاصل را مجددا به حافظه اصلی منتقل نماید‪.‬‬
‫حال اگر متغیری داشته باشیم که عملیات زیادی بر روی آن انجام شود‪ ،‬مانند متغیرهای‬
‫شمارنده حلقه‪ ،‬میتوانیم از کامپایلر بخواهیم که آن را بجای حافظه اصلی‪ ،‬بطور‬
‫مستقیم در داخل یکی از ثباتهای پردازنده ذخیره نماید‪ .‬با این کار دیگر نیازی به عملیات‬
‫انتقال نیست و سرعت بنحو قابل مالحظهای افزایش مییابد‪.‬‬
‫برای اینکار کافی است که از کلمه کلیدی ‪ register‬استفاده نماییم‪ .‬بعنوان مثال در تابع‬
‫قبلی میتوانستیم متغیر ‪ i‬را بصورت زیر تعریف نماییم‪:‬‬
‫;‪register int i‬‬
‫با این وجود دقت کنید که تعداد ثباتهای پردازنده محدود است و کامپایلر فقط درصورتی‬
‫این درخواست را قبول مینماید که ثبات خالی وجود داشته باشد؛ در غیراینصورت متغیر‬
‫را در حافظه اصلی ذخیره خواهد کرد(اما هیچ خطایی اعالم نخواهد کرد)‪.‬‬
‫همانطور که گفته شد فقط از متغیرهای بسیار پرکاربرد‪ ،‬بخصوص متغیرهایی که در داخل‬
‫یک حلقه استفاده میشوند‪ ،‬بعنوان متغیر ثبات استفاده نمایید‪.‬‬
‫‪ 9-6-2‬رده ذخیره سازی ایستا‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫متغیرهای متعلق به رده ذخیره سازی ایستا‪ ،‬از ابتدای آغاز برنامه ایجاد میشوند و تا پایان برنامه‬
‫نیز در حافظه حضور دارند‪.‬‬
‫متغیرهای سراسری به این دسته متعلق هستند‪ .‬با شروع اجرای برنامه به متغیرهای سراسری‬
‫حافظه تخصیص داده میشود‪ .‬پس از آن کلیه توابع قادر به دیدن و تغییر مقدار آنها هستند‪ ،‬اما‬
‫نمیتوانند حافظه تخصیص یافته به این متغیرها را بازپس بگیرند‪ .‬در پایان و پس از خاتمه تابع ‪،main‬‬
‫حافظه تخصیص یافته به این متغیرها بازپس گرفته میشود‪.‬‬
‫اما عالوه بر متغیرهای سراسری‪ ،‬متغیرهای محلی نیز میتوانند از رده ذخیره سازی ایستا تعریف‬
‫شوند‪.‬‬
‫اگر یک متغیر محلی‪ ،‬بصورت ایستا تعریف گردد؛ فقط یکبار و آن هم در شروع اجرای برنامه ایجاد‬
‫شده و مقدار اولیه خواهد گرفت(البته درصورتیکه به آن مقدار اولیه داده باشیم)‪ .‬پس از آن‪ ،‬با هر بار‬
‫اجرای تابع‪ ،‬قادر به دسترسی به این متغیر خواهیم بود(چرا که یک متغیر محلی است) ؛ اما با‬
‫خاتمه تابع این متغیر از بین نرفته و مقدار آن تا فراخوانی بعدی تابع حفظ خواهد شد‪ .‬متغیرهای‬
‫محلی ایستا هنگامی از بین میروند که برنامه اصلی خاتمه یابد‪.‬‬
‫از این متغیرها هنگامی استفاده میشود که بخواهیم مقدار یک متغیر محلی در فراخوانیهای متوالی‬
‫یک تابع حفظ شود(البته بدون اینکه سایر توابع به آن دسترسی داشته باشند‪ ،‬درغیراینصورت آن را‬
‫بصورت سراسری تعریف میکردیم)‪.‬‬
‫برای تعریف یک متغیر محلی از رده ذخیره سازی ایستا‪ ،‬از کلمه کلیدی ‪ static‬استفاده میشود‪.‬‬
‫البته استفاده از این کلمه الزامی است‪ ،‬چرا که متغیرهای محلی بطور پیش فرض از رده ذخیره‬
‫سازی اتوماتیک درنظر گرفته میشوند‪.‬‬
‫ رده ذخیره سازی ایستا‬9-6-2
#include <stdio.h>
number
2
4
31
5
void computeSum(int number) {
4
autoSum 1
3
0
5
2
int autoSum = 0;
static int staticSum = 0;
15
3
6
1
0
staticSum 10
autoSum += number;
staticSum += number ;
printf("autoSum = %d and staticSum = %d \n",autoSum,staticSum);
}
void main() {
int i;
for (i=1; i<=5; i++)
computeSum(i) ;
}
i
3
4
1
2
5
‫‪ 9-6-2‬رده ذخیره سازی ایستا‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫نوع دیگری از رده ذخیره سازی ایستا‪ ،‬رده ‪ extern‬است‪ .‬این رده هنگامی بکار می رود که برنامه در‬
‫چندین فایل مختلف قرار داشته باشد‪.‬‬
‫در برنامه های بزرگ که دارای توابع متعددی هستند‪ ،‬برای جلوگیری از بزرگ و پیچیده شدن بیش از‬
‫حد فایل برنامه‪ ،‬آن را بطور منطقی به چندین فایل تقسیم می کنند‪ .‬بدین صورت که کلیه توابع و‬
‫داده های مرتبط با یکدیگر را در یک فایل قرار می دهند‪ .‬سپس هر فایل بصورت مجزا کامپایل شده و‬
‫درنهایت تمامی آنها با یکدیگر پیوند )‪ (link‬خورده و تشکیل فایل اجرایی نهایی را می دهند‪ .‬این روش‬
‫باعث مدیریت بهتر پروژه های بزرگ می شود‪.‬‬
‫اما مشکل هنگامی است که بخواهیم یک متغیر سراسری را در توابع موجود در چند فایل مختلف‪،‬‬
‫مورد استفاده قرار دهیم‪ .‬مسلما این متغیر سراسری باید در یکی از فایلها تعریف شود‪ .‬اما در مورد‬
‫سایر فایلها چه باید بکنیم؟ متاسفانه هریک از دو راه زیر منجر به شکست می شود‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫اگر متغیر را در سایر فایلها بدون تعریف مجدد استفاده نماییم‪ ،‬کامپایلر اعالم خطا کرده و متغیر را نخواهد‬
‫شناخت‪.‬‬
‫اگر متغیر را در سایر فایلها نیز تعریف مجدد نماییم‪ ،‬کامپایلر اعالم خطا نخواهد کرد‪ ،‬اما پیوند زننده )‪ (linker‬در‬
‫هنگام ترکیب فایلها با هم متوجه تعریف چند متغیر با نام یکسان شده و اعالم خطا خواهد کرد‪.‬‬
‫تنها راه حل آن است که این متغیر را در یک فایل تعریف کرده و سپس در سایر فایلها آن را بعنوان یک‬
‫متغیر خارجی )‪ (extern‬اعالن (و نه تعریف) نماییم‪.‬‬
‫مشخصه ‪ extern‬به کامپایلر اعالن می کند که این متغیر در جای دیگری (معموال یک فایل دیگر)‬
‫تعریف شده است و بنابراین می تواند بدون اعالن خطا از آن استفاده کند؛ اما این مشخصه باعث‬
‫تعریف مجدد متغیر نمی گردد‪ .‬برای تعریف متغیر به شکل خارجی بصورت زیر عمل می کنیم‪:‬‬
‫;‪extern int a‬‬
‫‪ 9-6-2‬رده ذخیره سازی ایستا‬
‫‪File2.C‬‬
‫‪File1.C‬‬
‫;‪extern int k‬‬
‫;‪int k = 0‬‬
‫{ )(‪void F2‬‬
‫…‬
‫دسترسی به متغیر سراسری مشترک ‪k -- ; //‬‬
‫…‬
‫}‬
‫{ )(‪void F1‬‬
‫‪….‬‬
‫دسترسی به متغیر سراسری ‪k ++; // k‬‬
‫…‬
‫}‬
‫{ )(‪void F3‬‬
‫…‬
‫}‬
‫{ )(‪void main‬‬
‫‪….‬‬
‫}‬
‫ رده ذخیره سازی ایستا‬9-6-2
#include <stdio.h>
int x = 1; // global variable x
void a() {
int x = 10 ; // local automatic variable x
printf("x in function a is %d \n",x);
x ++;
printf("x in function a is %d \n",x);
}
void b() {
static int x = 20 ; // local static variable x
printf("x in function b is %d \n",x);
x ++;
printf("x in function b is %d \n",x);
}
void c() {
printf("x in function c is %d \n",x);
x ++;
printf("x in function c is %d \n",x);
}
void main() {
printf("x in function main is %d \n",x);
a() ;
b() ;
c() ;
printf("x in function main is %d \n",x);
a() ;
b() ;
c() ;
printf("x in function main is %d \n",x);
}
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
in
in
in
in
in
in
in
in
in
in
in
in
in
in
in
function main is 1
function a is 10
function a is 11
function b is 20
function b is 21
function c is 1
function c is 2
function main is 2
function a is 10
function a is 11
function b is 21
function b is 22
function c is 2
function c is 3
function main is 3
‫‪ 9-7‬مقادیر پیش فرض برای پارامترها‬
‫‪‬‬
‫‪‬‬
‫در بسیاری از موارد‪ ،‬توابعی داریم که دارای تعداد زیادی پارامتر هستند که در هربار‬
‫فراخوانی باید آرگومانهای متناظر با هریک را به تابع ارسال کرد‪ .‬چنانچه تعداد آرگومانهای‬
‫ارسالی‪ ،‬با تعداد پارامترها یکسان نباشد‪ ،‬یک خطای کامپایل ایجاد می گردد‪.‬‬
‫اما در بعضی موارد‪ ،‬مقادیر بعضی از این پارامترها در اکثر موارد مشخص است و فقط در‬
‫شرایط خاص تغییر می کند‪.‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫بعنوان مثال فرض کنید تابعی نوشته اید که یک پنجره را مکان موردنظر ترسیم میکند‪.‬‬
‫پارامترهای متداول برای چنین تابعی عبارتند از‪:‬‬
‫مختصات شروع‪ ،‬رنگ زمینه‪ ،‬رنگ متن‪ ،‬نوع حاشیه (یک خطی‪ ،‬دوخطی و ‪)...‬‬
‫اما فرض کنیم پنجره های متداول در برنامه ما دارای رنگ زمینه آبی و رنگ متن سفید با حاشیه‬
‫دوخطی هستند‪ .‬در اینصورت در اکثر موارد بجز اطالعات مربوط به مختصات پنجره‪ ،‬بقیه اطالعات‬
‫بصورت تکراری ارسال می گردند‪.‬‬
‫در چنین مواردی می توان از پارامترهای پیش فرض استفاده نمود‪.‬‬
‫چنانچه یک پارامتر از تابع دارای مقدار پیش فرض باشد‪ ،‬آنگاه تابع فراخواننده می تواند‬
‫هیچ آرگومانی متناظر با این پارامتر ارسال ننماید‪ .‬در اینصورت تابع فراخوانده شده از‬
‫مقدار پیش فرض برای آن پارامتر استفاده می نماید‪.‬‬
‫برای تعیین مقدار پیش فرض برای پارامتر‪ ،‬کافی است که در هنگام تعریف پارامتر با‬
‫استفاده از عملگر نسبت دهی )=( مقدار پیش فرض را به پارامتر نسبت دهیم‪.‬‬
‫ مقادیر پیش فرض برای پارامترها‬9-7
#include <stdio.h>
int sum(int a, int b=0, int c=0) {
return(a+b+c);
}
a
b 10
0
c 15
0
void main() {
printf("sum(5,10,20) = %d \n", sum(5,10,20) );
printf("sum(5,10) = %d \n", sum(5,10) );
printf("sum(5) = %d \n", sum(5) );
}
sum(5,10,20) = 35
sum(5,10) = 15
sum(5) = 5
5
‫‪ 9-7‬مقادیر پیش فرض برای پارامترها‬
‫‪‬‬
‫البته در نحوه استفاده از پارامترهای پیش فرض دو محدودیت وجود دارد‪:‬‬
‫‪ ‬تعریف پارامترهای پیش فرض حتما باید از سمت راست ترین پارامتر آغاز‬
‫شده و به سمت چپ ادامه یابد‪.‬‬
‫‪‬‬
‫بعنوان نمونه در مثال قبل چنانچه پارامتر ‪ c‬مقدار پیش فرض نداشته باشد‪،‬‬
‫پارامتر ‪ b‬نیز قادر به اعالم مقدار پیش فرض نیست‪ .‬عالوه براین پارامتر ‪ a‬نیز‬
‫فقط درصورتی می تواند مقدار پیش فرض داشته باشد که پارامترهای ‪ b‬و ‪c‬‬
‫هر دو دارای مقدار پیش فرض باشند‪ .‬یعنی اعالن زیر خطا می باشد‪:‬‬
‫‪int sum(int a=0, int b, int c=0) // compile error‬‬
‫‪‬‬
‫در هنگام فراخوانی یک تابع که دارای پارامترهای پیش فرض است‪،‬‬
‫آرگومانهای ارسالی از چپ به راست به پارامترها اختصاص می یابند‪.‬‬
‫یعنی در صورتیکه هنگام فراخوانی تابع ‪ sum‬دو آرگومان به آن ارسال‬
‫شود‪ ،‬اولی به ‪ a‬و دومی به ‪ b‬تخصیص خواهد یافت‪ .‬تحت هیچ‬
‫شرایطی نمی توان بدون اینکه مقداری برای ‪ b‬ارسال شود‪ ،‬آرگومانی را‬
‫برای ‪ c‬ارسال نمود‪.‬‬
‫‪ 9-8‬سربارگذاری توابع‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫در برخی موارد‪ ،‬تابعی داریم که یک وظیفه خاص را برای چندین نوع داده مختلف انجام‬
‫می دهد‪.‬‬
‫بعنوان مثال تابع ‪ max‬را درنظر بگیرید که دو داده را بعنوان ورودی دریافت و حداکثر آنها را‬
‫باز می گرداند‪ .‬چنانچه این تابع را برای دو ورودی از نوع عدد صحیح بنویسیم‪ ،‬آنگاه برای‬
‫ورودی های اعشاری درست عمل نخواهد کرد‪ .‬بنابراین ممکن است برنامهنویس ترجیح‬
‫دهد دو نسخه از این تابع داشته باشد‪ :‬یکی برای اعداد صحیح و یکی برای اعداد‬
‫اعشاری‪.‬‬
‫اما از آنجا که نمی توان دو شناسه همنام تعریف کرد‪ ،‬بنابراین ممکن است مجبور شویم‬
‫از دو نام مجزا استفاده نماییم‪ ،‬مانند ‪ intMax‬برای اعداد صحیح و ‪ doubleMax‬برای اعداد‬
‫اعشاری‪.‬‬
‫خوشبختانه با استفاده از سربار گذاری توابع‪ ،‬می توان این مشکل را حل کرد‪ .‬با استفاده‬
‫از سربار گذاری‪ ،‬می توان توابعی تعریف کرد که دارای نام یکسان باشند‪ ،‬ولی لیست‬
‫پارامترهای ورودی آنها متفاوت باشد‪.‬‬
‫در اینصورت در هنگام فراخوانی تابع‪ ،‬زبان ‪ C‬با توجه به نوع آرگومانهای ارسالی‪ ،‬تابع‬
‫مناسب را انتخاب کرده و فراخوانی می نماید‪ .‬تفاوت در لیست پارامترهای ورودی توابع‬
‫سربارگذاری شده‪ ،‬می تواند شامل یک یا هر دو مورد زیر باشد‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫تفاوت در تعداد پارامترها‬
‫تفاوت در نوع داده یک یا چند پارامتر‬
‫نکته آخر این که سربار گذاری نمی تواند بر اساس نوع داده خروجی انجام شود‪.‬‬
‫ سربارگذاری توابع‬9-8
int max(int a, int b) {
if (a > b) return(a);
else return(b);
}
double max(double a, double b) {
if (a > b) return(a);
else return(b);
}
int max(int a, int b, int c) {
if (a > b)
if (a > c) return(a);
else return(c);
else
if (b > c) return(b);
else return(c);
}
void main() {
printf("max(8, 10) = %d \n", max(8, 10) );
printf("max(4.23, 3.712) = %f \n", max(4.23, 3.712) );
printf("max(15, 3, 20) = %d \n", max(15, 3, 20) );
}
max(8, 10) = 10
max(4.23, 3.712) = 4.23
max(15, 3, 20) = 20
‫‪ 9-9‬الگوهای تابعی‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫الگوهای تابعی‪ ،‬در حقیقت شکل پیشرفته تری از سربارگذاری هستند‪.‬‬
‫اگر به تابع ‪ max‬در قسمت قبل دقت کنید‪ ،‬این تابع یک بار برای نوع داده ‪ int‬و یک بار نیز‬
‫برای نوع داده ‪ double‬نوشته شده است‪ ،‬اما الگوریتم هر دو تابع یکسان است‪ .‬در چنین‬
‫مواردی می توان بجای نوشتن چندین تابع سربارگذاری شده با الگوریتم یکسان‪ ،‬تنها یک‬
‫الگوی تابعی نوشت و ساخت توابع با نوع داده های مختلف را بعهده کامپایلر نهاد‪.‬‬
‫برای روشن شدن موضوع‪ ،‬بحث را با یک مثال ادامه می دهیم‪.‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫تابع ‪ swap‬را در نظر بگیرید که یک دو متغیر را دریافت و مقادیر آنها را جابجا می کند‪ .‬این تابع‬
‫دارای کاربردهای متعددی بوده و ممکن است با نوع داده های مختلفی مورد استفاده قرار گیرد‪.‬‬
‫یک روش سربارگذاری تابع ‪ swap‬برای انواع داده های مورد نیاز است‪ .‬اما روش بهتر استفاده از‬
‫یک الگو است‪.‬‬
‫تعریف الگو با استفاده از کلمه کلیدی ‪ template‬صورت می پذیرد و پس از آن لیست‬
‫پارامترهای الگو در داخل عالمت >< قرار می گیرند‪.‬‬
‫پارامترهای الگو‪ ،‬مشابه پارامترهای تابع هستند‪ ،‬با این تفاوت که بجای اینکه حاوی‬
‫مقدار باشند‪ ،‬حاوی نوع داده هستند‪ .‬در هنگام استفاده از یک الگو‪ ،‬نوع داده مورد نظر‬
‫از طریق این پارامترها به الگو ارسال می شود‪.‬‬
‫برای تعریف هر پارامتر الگو‪ ،‬از کلمه کلیدی ‪ class‬بعالوه یک شناسه که نماینده نوع داده‬
‫است‪ ،‬استفاده می شود‪ .‬در اینجا ‪ class‬به معنای هر نوع داده دلخواه است‪.‬‬
‫پس از کلمه کلیدی ‪ template‬و لیست پارامترها‪ ،‬تعریف تابع مربوط به الگو شروع می‬
‫شود که همانند سایر توابع است؛ با این تفاوت که می توان از پارامترهای الگو‪ ،‬همانند‬
‫نوع داده های عادی در تعریف متغیرهای تابع استفاده نمود‪.‬‬
‫ الگوهای تابعی‬9-9
template <class T>
void swap(T &a, T &b) {
T temp;
temp = a;
a = b;
b = temp ;
}
void main() {
int a=5 , b=10;
double c = 1.1 , d = 2.2 ;
char e = 'a' , f = 'b' ;
swap(a,b);
swap(c,d);
swap(e,f) ;
printf("a = %d and b = %d \n", a, b);
printf("c = %f and d = %f \n", c, d);
printf("e = %c and f = %c \n", e, f);
}
swap(double
swap(char
&a,
&a,
char
double
&b)
void swap(int
&a,
int
&b)
{ { &b) {
double
char
temp;
temp;
int
temp;
temp = a;
a = b;
b = temp ;
}
‫‪ 9-10‬توابع درون برنامه ای‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫فراخوانی یک تابع دارای سربار زمانی و حافظه ای می باشد‪ .‬با فراخوانی هر‬
‫تابع باید برای متغیرهای محلی و پارامترهای آن حافظه تخصیص داده شود‪،‬‬
‫آرگومانهای ارسالی در پارامترهای تابع کپی شوند و ‪ ...‬که همگی این موارد‬
‫باعث صرف زمان و حافظه می شوند‪.‬‬
‫به همین دلیل برای انجام عملیات کوتاه‪ ،‬استفاده از تابع ایده چندان خوبی‬
‫نیست؛ چراکه گرچه خوانایی برنامه باال می رود‪ ،‬اما برای انجام یک کار کوچک‪،‬‬
‫هزینه زیادی به سیستم تحمیل می گردد‪.‬‬
‫برای رفع این مشکل‪ ،‬زبان ‪ C‬از توابع درون برنامه ای یا ‪ inline‬استفاده می کند‪.‬‬
‫اگر قبل از شروع تعریف تابع (یعنی قبل از نوع مقدار بازگشتی) از کلمه کلیدی‬
‫‪ inline‬استفاده شود‪ ،‬به کامپایلر توصیه می شود که هر فراخوانی تابع مورد‬
‫نظر را با یک کپی صریح از کد تابع جایگزین نماید‪ ،‬تا دیگر نیازی به صرف زمان‬
‫برای فراخوانی تابع نباشد‪.‬‬
‫البته استفاده از ‪ ،inline‬گرچه باعث باال رفتن سرعت اجرای برنامه می شود‪،‬‬
‫اما حجم آن را باال می برد؛ چرا که بجای هر فراخوانی تابع‪ ،‬یک کپی از آن را‬
‫قرار می دهد‪.‬‬
‫در نتیجه استفاده از این مشخصه‪ ،‬فقط در توابع کوچک قابل قبول است‪ .‬در‬
‫حقیقت ‪ C‬از این مشخصه برای توابع بزرگ صرفنظر می کند‪.‬‬
‫ توابع درون برنامه ای‬9-10
inline int square(int n) {
return (n*n) ;
}
void main() {
int a, b ;
‫کامپایلر‬
void main() {
int a, b ;
a = (5) * (5) ;
b = (2*a+3) * (2*a+3) ;
a = square(5) ;
b = square(2*a+3) ;
}
}
‫‪ 9-11‬توابع بازگشتی‬
‫‪‬‬
‫‪‬‬
‫یکی از مهمترین مفاهیم علم کامپیوتر‪ ،‬توابع بازگشتی هستند که کاربرد بسیار زیادی در حل‬
‫مسائل دارند‪.‬‬
‫توابع بازگشتی‪ ،‬توابعی هستند که خود را مجددا فراخوانی می کنند‪ .‬این توابع به دو دسته تقسیم‬
‫می شوند‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫توابع بازگشتی مستقیم ‪ :‬تابعی مانند ‪ ،A‬خودش را فراخوانی نماید‪.‬‬
‫توابع بازگشتی غیر مستقیم ‪ :‬تابعی مانند ‪ ،A‬تابع دیگری مانند ‪ B‬را فراخوانی نماید و سپس ‪ B‬مجددا تابع ‪A‬‬
‫را فراخوانی نماید‪.‬‬
‫اما چرا یک تابع باید خودش را فراخوانی نماید؟‬
‫در بعضی مسائل (که عموما مسائل پیچیده ای نیز هستند)‪ ،‬می توان یک نمونه از مسئله را با‬
‫استفاده از نمونه ساده تر یا کوچکتری از همان مسئله حل کرد‪.‬‬
‫بنابراین‪ ،‬تابع می تواند برای حل مسئله بزرگتر‪ ،‬نسخه جدیدی از خودش را برای حل مسئله کوچکتر‬
‫فراخوانی نماید و سپس با استفاده از نتایج بدست آمده‪ ،‬مسئله بزرگ را حل نماید‪.‬‬
‫مسلم است که تابعی که برای حل مسئله کوچک فراخوانی شده است نیز از همین مکانیزم‬
‫استفاده کرده و مجددا خودش را برای حل یک مسئله کوچکتر فراخوانی خواهد کرد‪.‬‬
‫اما این فراخوانیها تا چه زمانی ادامه پیدا می کند؟‬
‫هر مسئله بازگشتی دارای یک حالت پایه است که حل آن بدیهی بوده و نیازی به حل یک مسئله‬
‫کوچکتر ندارد‪ .‬بنابراین تابع بازگشتی حتما باید دارای یک شرط برای بررسی حالت پایه باشد‪ ،‬که به‬
‫آن شرط توقف گفته می شود‪.‬‬
‫هنگامی که تابع به حالت پایه می رسد‪ ،‬فراخوانی بازگشتی را متوقف کرده و حل بدیهی مسئله را‬
‫برای فراخوانی قبلی تابع باز می گرداند‪ .‬سپس این بازگرداندن مقادیر رو به عقب تکرار می شود تا‬
‫هنگامی که به نسخه اولیه تابع برسد و در نهایت جواب نهایی به تابع اصلی بازگردانده شود‪.‬‬
‫‪ 9-11‬توابع بازگشتی‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫اکنون برای روشن شدن موضوع به بررسی یک مثال کالسیک در زمینه توابع‬
‫بازگشتی‪ ،‬یعنی محاسبه فاکتوریال می پردازیم‪.‬‬
‫قبال نحوه محاسبه فاکتوریال یک عدد را بصورت زیر بیان کردیم ‪:‬‬
‫‪n! = 1 × 2 × … × n‬‬
‫تابع مربوط به فاکتوریال نیز با استفاده از یک حلقه تکرار که اعداد ‪ 1‬تا ‪ n‬را در یکدیگر‬
‫ضرب می کرد‪ ،‬نوشته شد‪.‬‬
‫اما اگر به تعریف فاکتوریال دقت کنیم‪ ،‬درمی یابیم که فاکتوریال عددی مانند ‪ 5‬را می‬
‫توان از ضرب عدد ‪ 5‬در فاکتوریال ‪ 4‬بدست آورد‪ .‬بعبارت بهتر‬
‫!‪5! = 5 × 4‬‬
‫استدالل مشابهی برای !‪ 4‬برقرار است‪ .‬بطور کلی می توان گفت‪:‬‬
‫‪n0‬‬
‫‪‬‬
‫!)‪n  (n  1‬‬
‫‪n! ‬‬
‫‪n0‬‬
‫‪1‬‬
‫همانطور که دیده می شود‪ ،‬حالت پایه در این مسئله‪ ،‬حالت ‪ n=0‬است؛ چرا که در‬
‫این حالت حل مسئله بدیهی بوده و جواب برابر ‪ 1‬است‪.‬‬
‫‪ 9-11‬توابع بازگشتی‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫تابع بازگشتی فاکتوریال بصورت زیر نوشته می شود‪:‬‬
‫{ )‪long int factorial(int n‬‬
‫; )‪if (n ==0) return(1‬‬
‫;))‪else return(n * factorial(n-1‬‬
‫}‬
‫همانطور که دیده می شود‪ ،‬توابع باز گشتی بسیار ساده و کوچک می باشند‪،‬‬
‫اما درک نحوه کار آنها کمی مشکل است‪.‬‬
‫قبل از توضیح نحوه کار این تابع‪ ،‬توجه کنید که هنگامی که یک تابع خودش را‬
‫فراخوانی می کند‪ ،‬یک نسخه جدید از آن تابع بوجود می آید‪ .‬بدین معنا که یک‬
‫نسخه جدید از کلیه پارامترها و متغیرهای محلی تابع ایجاد می شود و عملیات‬
‫برروی این متغیرهای جدید انجام می شود(بدون اینکه تغییری در متغیرهای‬
‫فراخوانی قبلی ایجاد شود)‪.‬‬
‫البته برای صرفه جویی در حافظه‪ ،‬نسخه جدیدی از دستورات برنامه ایجاد نمی‬
‫شود‪.‬‬
‫ توابع بازگشتی‬9-11
long int factorial (4) {
if (4 ==0) return(1) ;
else return(4 * factorial(3));
}
6
long int factorial (3) {
if (3 ==0) return(1) ;
else return(3 * factorial(2));
}
2
long int factorial (2) {
if (2 ==0) return(1) ;
else return(2 * factorial(1));
}
factorial(4) = ?
24
1
long int factorial (0) {
if (0 ==0) return(1) ;
else return(0 * factorial(-1));
}
1
long int factorial (1) {
if (1 ==0) return(1) ;
else return(1 * factorial(0));
}
‫ توابع بازگشتی‬9-11
factorial(4)
n=4
return (24)
factorial(3)
n=3
return (6)
factorial(2)
n=2
return (2)
factorial(1)
n=1
return (1)
factorial(0)
n=0
return (1)
‫ توابع بازگشتی‬9-11
.‫ را محاسبه نماید‬k ‫ به‬n ‫) تابعی بنویسید که ترکیب‬7 ‫مثال‬
k  0 or n  k
1
n
  
    n  1  n  1
  
 otherwise
 k  
k

1
k
 


int combination(int n, int k) {
if (k==0 || n==k) return(1);
else return( combination(n-1,k-1) + combination(n-1,k) ) ;
}

‫‪ 9-11‬توابع بازگشتی‬
‫‪‬‬
‫‪‬‬
‫مثال ‪ )8‬تابعی بنویسید که ‪n‬امین عدد فیبوناچی را باز گرداند‪.‬‬
‫دنباله فیبو ناچی بصورت زیر تعریف می شود‪:‬‬
‫… ‪1 1 2 3 5 8 13 21‬‬
‫{ )‪int fibonacci(int n‬‬
‫;)‪if (n==1 || n==2) return(1‬‬
‫;) )‪else return( fibonacci(n-1) + fibonacci(n-2‬‬
‫}‬
‫‪ 9-11‬توابع بازگشتی‬
‫‪‬‬
‫مثال ‪ )9‬تابعی بنویسید که دو عدد را دریافت و بزرگترین مقسوم علیه‬
‫مشترک آن دو را بازگرداند‪.‬‬
‫‪y0‬‬
‫‪x‬‬
‫‪gcd ( x, y)  ‬‬
‫‪gcd ( y , x % y) y  0‬‬
‫{ )‪int gcd(int x, int y‬‬
‫;)‪if (y == 0) return(x‬‬
‫;) )‪else return( gcd(y, x%y‬‬
‫}‬
‫‪ 9-11-1‬مقایسه توابع بازگشتی و تکراری‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫همانطور که قبال دیده شد‪ ،‬الگوریتم فاکتوریال را می توان به دوصورت تکراری (با حلقه‬
‫تکرار) و بازگشتی نوشت‪ .‬آیا این مسئله برای سایر الگوریتمهای بازگشتی نیز برقرار‬
‫است؟‬
‫جواب مثبت است‪ .‬هر الگوریتم بازگشتی را می توان بصورت غیر بازگشتی یا تکراری نیز‬
‫نوشت‪ ،‬گرچه ممکن است راه حل آن پیچیده تر و وضوح آن نیز کمتر باشد‪.‬‬
‫اما کدام روش بهتر است؟‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫با توجه به این نکات منفی‪ ،‬چرا از توابع بازگشتی استفاده کنیم؟‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫در الگوریتمهای بازگشتی‪ ،‬با هربار فراخوانی تابع یک نسخه جدید از متغیرها ایجاد می شود‪ .‬به‬
‫همین دلیل معموال الگوریتمهای بازگشتی حافظه بیشتری را نسبت به الگوریتمهای تکراری‬
‫مصرف می نمایند‪.‬‬
‫عالوه براین الگوریتمهای بازگشتی معموال از نظر زمان اجرا نیز کندتر هستند‪ .‬دلیل این مسئله‬
‫سربار ناشی از فراخوانی مکرر تابع است‪.‬‬
‫تنها هنگامی از توابع بازگشتی استفاده نمایید که طبیعت مساله بگونه ای باشد که توسط‬
‫روشهای بازگشتی راحتتر حل شود و الگوریتم حاصل ساده تر و واضحتر باشد‪.‬‬
‫در بسیاری از مسائل‪ ،‬روش بازگشتی بسیار ساده و آسان بوده و درک و اشکالزدایی آن بسیار‬
‫ساده است‪ ،‬درحالیکه راه حل غیربازگشتی به راحتی به ذهن نمی رسد و یا بسیار پیچیده‬
‫است‪.‬‬
‫اما در مواردی که کارایی اهمیت زیادی دارد‪ ،‬هرگز از بازگشت استفاده ننمایید‪.‬‬
‫مبانی کامپیوتر و برنامه سازی‬
‫فصل‌دهم‌‪ :‬آرایه‌ها‬
‫مدرس‌‪ :‬رضا‌رمضانی‬
‫‪ 10‬آرایه ها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫در مبحث الگوریتمها با یک ساختمان داده مهم‪ ،‬یعنی‬
‫آرایهها آشنا شدیم‪.‬‬
‫البته در ‪ C‬ساختمان داده های دیگر ی همچون ساختارها‬
‫نیز وجود دارند‪ ،‬اما آرایهها همچنین مهمترین ساختمان‬
‫داده موجود در این زبان هستند‪.‬‬
‫آرایه در ‪ C‬عبارتست از مجموعه ای از داده های همنوع‬
‫که تحت یک نام مشترک و در خانه های متوالی حافظه‬
‫ذخیره می گردند‪.‬‬
‫برای دسترسی به عناصر آرایه‪ ،‬باید از نام آرایه بعالوه‬
‫اندیس استفاده کرد‪.‬‬
‫در قسمتهای بعدی‪ ،‬نحوه تعریف و استفاده از آرایهها را‬
‫تشریح خواهیم کرد‪.‬‬
‫‪ 10-1‬آرایه های یک بعدی‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫پیش از آنکه بتوان از یک آرایه یک بعدی استفاده کرد‪ ،‬باید آن را اعالن‬
‫کرد‪.‬‬
‫اعالن آرایهها بصورت زیر انجام می گردد‪:‬‬
‫; ]>‪<type> <var-name>[<size‬‬
‫بعنوان مثال‪:‬‬
‫;]‪int A[10‬‬
‫خط باال یک آرایه ‪ 10‬تایی از اعداد صحیح بنام ‪ A‬ایجاد می نماید‪.‬‬
‫هر کدام از عناصر این آرایه می توانند بعنوان یک متغیر مستقل مورد‬
‫استفاده قرار گیرد‪.‬‬
‫برای دسترسی به عناصر این آرایه باید از اندیس استفاده نمود‪ .‬در زبان‬
‫‪ C‬اندیسها در داخل کروشه ][ قرار می گیرند‪.‬‬
‫نکته بسیار مهمی که باید بدان توجه کرد آنستکه در ‪ C‬اندیس یک عدد‬
‫صحیح است که از ‪ 0‬آغاز می گردد‪.‬‬
‫‪ 10-1‬آرایه های یک بعدی‬
‫; ]‪int A[10‬‬
‫‪8‬‬
‫]‪A[9‬‬
‫]‪A[0] A[1] A[2] A[3‬‬
‫; ‪A[2] = 8‬‬
‫‪ 10-1‬آرایه های یک بعدی‬
‫‪‬‬
‫مثال ‪ )1‬برنامه ای بنویسید که شماره دانشجویی و معدل تعدادی دانشجو را دریافت‪ ،‬و سپس‬
‫چنانچه معدل دانشجو از میانگین کالس ‪:‬‬
‫‪ ‬بیش از یک نمره بیشتر باشد‪ ،‬چاپ کند ‪ :‬عالی‬
‫‪ ‬حداکثر یک نمره بیشتر یا کمتر باشد‪ ،‬چاپ کند ‪ :‬خوب‬
‫‪ ‬بیش از یک نمره کمتر باشد‪ ،‬چاپ کند ‪ :‬ضعیف‬
‫>‪#include <stdio.h‬‬
‫{ )(‪void main‬‬
‫; ]‪float average[100‬‬
‫آرایه نگهداری معدل دانشجویان‪//‬‬
‫; ]‪long int id[100‬‬
‫آرایه نگهداری شماره دانشجویان‪//‬‬
‫میانگین کل دانشجویان‪float totalAverage; //‬‬
‫; ‪int i, n‬‬
‫;)" ‪printf("enter number of students :‬‬
‫;)‪scanf("%d", &n‬‬
‫دریافت تعداد دانشجویان‪//‬‬
‫ آرایه های یک بعدی‬10-1
//‫حلقه دریافت مشخصات دانشجویان و محاسبه میانگین معدلها‬
for (i = 0; i < n ; i ++) {
printf("enter id and average : ");
scanf("%ld %f", &id[i], &average[i]);
totalAverage += average[i];
}
totalAverage /= n;
//‫حلقه مقایسه معدل هر دانشجو با میانگین کالس و چاپ پیام مناسب‬
for (i = 0; i < n ; i ++) {
if (average[i] >= totalAverage + 1)
printf("%ld : excellent !\n", id[i]);
else if (average[i] >= totalAverage – 1)
printf("%ld : good !\n", id[i]);
else printf("%ld : weak !\n", id[i]);
}
}
‫‪ 10-1‬آرایه های یک بعدی‬
‫‪‬‬
‫چند نکته مهم راجع به آرایه در ‪ C‬وجود دارد که حتما باید به آنها دقت کنید‪:‬‬
‫‪‬‬
‫اندازه آرایهها در ‪ C‬ثابت بوده و حتما باید توسط یک مقدار ثابت صحیح تعیین گردد‪ .‬بعنوان مثال‬
‫اعالن زیر خطای نحوی محسوب می گردد‪:‬‬
‫; ‪int n‬‬
‫; ‪n=100‬‬
‫;]‪int A[n‬‬
‫‪‬‬
‫‪‬‬
‫اما می توان با استفاده از متغیر های ثابت (ثابتهای دارای نام)‪ ،‬اندازه آرایه را تعیین کرد‪ ،‬که در‬
‫قسمتهای بعدی به آن اشاره خواهد شد‪.‬‬
‫اندیس آرایهها در ‪ C‬عدد صحیح بوده و همیشه از ‪ 0‬شروع می شود‪ .‬لذا به تفاوت "عنصر چهار‬
‫آرایه" یعنی ]‪ A[4‬و "چهارمین عنصر آرایه" یعنی ]‪ A[3‬دقت کنید‪ .‬این مسئله معموال باعث بروز‬
‫خطاهای منطقی می گردد‪.‬‬
‫در ‪ C‬مرز آرایهها بررسی نمی گردد‪ .‬بدین معنا که چنانچه اندیسی خارج از محدوده مجاز یک‬
‫آرایه استفاده شود‪ ،‬باعث ایجاد خطا توسط کامپایلر نمی گردد‪ ،‬اما مسلما برنامه را دچار یک‬
‫خطای منطقی خواهد کرد‪ .‬بعنوان مثال‪:‬‬
‫; ]‪int A[10‬‬
‫‪A[12] = 20 ; //this is not a syntax error but a logical error‬‬
‫لذا بررسی مرزهای آرایه بعهده خود برنامهنویس است و باید از درستی برنامه خود و خارج‬
‫نشدن از محدوده مجاز مطمئن گردد‪.‬‬
‫‪ 10-1‬آرایه های یک بعدی‬
‫‪‬‬
‫مقداردهی اولیه به آرایه های یک بعدی بصورت زیر انجام می پذیرد‪:‬‬
‫;}‪int A[3] = {5, 2, 8‬‬
‫که در اینجا ]‪ A[0‬برابر ‪ A[1] ، 5‬برابر ‪ 2‬و ]‪ A[2‬برابر ‪ 8‬خواهد شد‪.‬‬
‫عالوه براین می توان فقط به تعدادی از عناصر آرایه مقدار داد‪ ،‬دراینصورت مقدار‬
‫عناصر باقیمانده آرایه اتوماتیک ‪ 0‬خواهد شد‪.‬‬
‫; }‪int B[10] = {5, 8‬‬
‫در اینجا عناصر ]‪ B[2‬به بعد مقدار ‪ 0‬خواهند گرفت‪ .‬بنابراین می توان برای ‪ 0‬کردن‬
‫کلیه عناصر یک آرایه به شکل زیر عمل کرد ‪:‬‬
‫;}‪int C[10] = {0‬‬
‫چنانچه به آرایه مقدار دهی اولیه کرده باشیم‪ ،‬می توان تعداد عناصر آرایه را نیز‬
‫ذکر نکرد‪ ،‬دراینصورت اندازه آرایه بطور اتوماتیک برابر تعداد مقادیر مشخص شده‬
‫خواهد شد‪.‬‬
‫;}‪int C[] = {10, 15, 20‬‬
‫در مثال فوق آرایه ‪ C‬با ‪ 3‬عضو درنظر گرفته می شود‪.‬‬
‫‪ 10-2‬متغیرهای ثابت‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫همانطور که در قسمت قبل گفته شد‪ ،‬گرچه اندازه یک آرایه باید ثابت صحیح‬
‫باشد؛ اما می توان از متغیرهای ثابت نیز استفاده کرد‪.‬‬
‫یک متغیر ثابت‪ ،‬متغیری است که فقط می تواند در هنگام اعالن مقدار اولیه‬
‫بگیرد و این مقدار دیگر قابل تغییر نیست‪ .‬برای اعالن متغیرهای ثابت‪ ،‬از کلمه‬
‫کلیدی ‪ const‬قبل از نوع متغیر استفاده می گردد‪ .‬بعنوان مثال‪:‬‬
‫;‪const int k = 10‬‬
‫اکنون هرگونه تالش برای تغییر مقدار ‪ ،k‬باعث ایجاد یک خطای نحوی توسط‬
‫کامپایلر خواهد شد‪ .‬به این نوع متغیرها‪ ،‬ثابتهای نام دار نیز گفته می شود‪.‬‬
‫این متغیرها در تعریف مقادیر ثابتی که مقدار آنها در طول برنامه تغییر نمیکند‪،‬‬
‫بکار میروند‪ .‬بعنوان مثال ‪:‬‬
‫;‪const float pi = 3.14‬‬
‫این کار نه تنها خوانایی برنامه را باال میبرد (بدلیل استفاده از کلمه ‪ pi‬که برای‬
‫همه شناخته شده است)‪ ،‬بلکه باعث میشود تغییر پذیری برنامه نیز باال برود‪.‬‬
‫بدین معنا که در صورتیکه برنامهنویس تصمیم گرفت مقدار ثابت را عوض کند‪،‬‬
‫نیازی به تغییر کل برنامه نیست و فقط کافی است مقدار اولیه متغیر را عوض‬
‫نماید‪.‬‬
‫‪ 10-2‬متغیرهای ثابت‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫از این مسئله میتوان در تعریف آرایهها نیز استفاده کرد‪.‬‬
‫بدین صورت که بجای آنکه اندازه آرایه را با یک ثابت صحیح‬
‫مشخص نماییم‪ ،‬آن را با یک متغیر ثابت تعریف میکنیم‪.‬‬
‫با اینکار‪ ،‬درصورتیکه نیازی به تغییر اندازه آرایه (یا آرایه ها)‬
‫گردد‪ ،‬فقط کافی است مقدار اولیه متغیر ثابت خود را تغییر‬
‫دهیم‪.‬‬
‫مثال ‪ )2‬برنامه ای بنویسید که سال ورود تعدادی دانشجو را‬
‫دریافت و سپس تعداد ورودی های سالهای ‪ 75‬تا ‪ 84‬را‬
‫محاسبه و چاپ نماید‪.‬‬
‫ متغیرهای ثابت‬10-2
#include <stdio.h>
void main() {
const int startYear = 75;
const int yearNo = 10;
int count[yearNo] = {0};
int i, n, year;
printf("enter student no :");
scanf("%d",&n);
for (i=0; i<n ; i++) {
printf("enter entrance year :");
scanf("%d",&year);
count [year – startYear] ++;
}
for (i=0 ; i<yearNo ; i++)
printf("year = %d count = %d \n",startYear + i , count[i]);
}
‫‪ 10-3‬آرایههای چندبعدی‬
‫‪‬‬
‫‪‬‬
‫همانطور که در بخش الگوریتمها گفته شد‪ ،‬آرایهها می توانند‬
‫دارای ابعاد بیشتری نیز باشند‪.‬‬
‫در زبان ‪ C‬نیز می توان یک آرایه چند بعدی را بصورت زیر اعالن‬
‫کرد‪:‬‬
‫; ]>‪<type> <var-name>[<size 1>][<size 2>] … [<size n‬‬
‫‪‬‬
‫بعنوان مثال‪ ،‬اعالن زیر یک آرایه دوبعدی را معرفی می نماید‪:‬‬
‫; ]‪int A[5][8‬‬
‫‪‬‬
‫برای دسترسی به هر عنصر از این آرایه باید از دو عالمت ][‬
‫استفاده کرد‪ .‬توجه کنید که اندیس سطرها و ستونها هر دو از‬
‫‪ 0‬آغاز می گردند‪.‬‬
‫‪ 10-3‬آرایههای چندبعدی‬
‫; ]‪int A[5][8‬‬
‫ستون‬
‫‪7‬‬
‫‪6‬‬
‫‪5‬‬
‫‪4‬‬
‫‪3‬‬
‫‪2‬‬
‫‪1‬‬
‫‪0‬‬
‫‪0‬‬
‫]‪A[1][6‬‬
‫‪1‬‬
‫‪24‬‬
‫‪2‬‬
‫‪3‬‬
‫‪4‬‬
‫سطر‬
‫]‪A[2][3‬‬
‫; ‪A[3][1] = 24‬‬
‫‪ 10-3‬آرایههای چندبعدی‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫البته در مورد آرایه های با ابعاد باالتر نیز به شکل مشابهی عمل می‬
‫گردد‪.‬‬
‫بعنوان مثال به نحوه استفاده از یک آرایه سه بعدی در مثال زیر دقت‬
‫کنید‪:‬‬
‫; ]‪int B[5][8][6‬‬
‫;‪B[2][4][0] = 12‬‬
‫مثال ‪ )3‬یکی از اساتید قصد دارد آماری از نمرات دانشجویان خود در‬
‫کوییزهای بین ترم تهیه نماید‪ .‬وی ‪ 5‬کوییز در طول ترم برگذار نموده‬
‫است و اکنون نیاز به اطالعات زیر دارد‪:‬‬
‫‪ ‬میانگین نمرات هر دانشجو برای کل کوییزها‬
‫‪ ‬میانگین نمرات کل دانشجویان برای هر کوییز‬
‫برنامه ای بنویسید که نمرات دانشجویان را برای ‪ 5‬کوییز دریافت و‬
‫اطالعات خواسته شده را چاپ نماید‪.‬‬
‫‪ 10-3‬آرایههای چندبعدی‬
‫کوییز کوییز کوییز‬
‫پنجم چهارم سوم‬
‫کوییز‬
‫دوم‬
‫کوییز‬
‫اول‬
‫دانشجوی اول‬
‫دانشجوی دوم‬
‫دانشجوی سوم‬
‫‪...‬‬
‫ آرایههای چندبعدی‬10-3
#include <stdio.h>
const int maxStudent = 100;
const int quizNo = 5;
//‫حداکثر تعداد دانشجویان‬
//‫تعداد کوئیزها‬
void main() {
float grades[maxStudent][quizNo] ;
//‫آرایه نگهداری نمرات دانشجویان‬
float quizAverage,
//‫میانگین نمرات هر کوئیز‬
studentAverage;
//‫میانگین نمرات هر دانشجو‬
int i, j, n;
printf("enter number of students: ");
scanf("%d",&n);
//‫دریافت تعداد دانشجویان‬
//‫حلقه دریافت اطالعات دانشجویان‬
for (i=0 ; i<n ; i++) {
printf("student no %d:\n",i+1);
for (j=0 ; j<quizNo ; j++) {
printf("enter grade of quiz %d: ", j+1);
scanf("%f", &grades[i][j]);
}
}
‫ آرایههای چندبعدی‬10-3
//‫حلقههای متداخل برای محاسبه میانگین نمرات هر دانشجو‬
printf("Student's averages:\n");
for (i=0 ; i<n ; i++) {
studentAverage = 0.0;
for (j=0; j<quizNo; j++)
studentAverage += grades[i][j];
studentAverage /= quizNo ;
printf("student no %d: average=%f \n",i+1, studentAverage);
}
//‫حلقههای متداخل برای محاسبه میانگین نمرات هر کوئیز‬
printf("Average of each quiz:\n");
for (j=0 ; j<quizNo ; j++) {
quizAverage = 0.0;
for (i=0; i<n; i++)
quizAverage += grades[i][j];
quizAverage /= n ;
printf("quiz no %d: average=%f \n",j+1, quizAverage);
}
}
‫‪ 10-3‬آرایههای چندبعدی‬
‫‪‬‬
‫و نکته آخر اینکه مقداردهی اولیه به آرایه های چندبعدی‬
‫امکان پذیر است و بصورت زیر انجام می پذیرد‪:‬‬
‫;} }‪int A[3][4] = { {12, 5, 3, 8} , {-3, 7, -9, 2}, {4, 22, 18, 6‬‬
‫‪‬‬
‫‪‬‬
‫یعنی یک عالمت }{ برای کل مقداردهی قرار می گیرد‪،‬‬
‫سپس هر ردیف از آرایه در داخل یک }{ مجزا قرار می‬
‫گیرد‪.‬‬
‫برای ابعاد باالتر نیز به روش مشابهی عمل می گردد‪.‬‬
‫بعنوان مثال برای آرایه های سه بعدی داریم‪:‬‬
‫‪int A[2][3][4] = { { {12, 5, 3, 8} , {-3, 7, -9, 2}, {4, 22, 18, 6} } ,‬‬
‫;} } }‪{ {8, 1, -3, 4} , {-2, 8, 11, 21} , {7, 3, -15, -8‬‬
‫‪ 10-4‬ارسال آرایههای یک بعدی به توابع‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫آرایهها را نیز همچون سایر نوع دادهها میتوان به یک تابع ارسال کرد‪.‬‬
‫برای اینکار ابتدا باید تابع را بگونهای تعریف کنیم که یک پارامتر از نوع‬
‫آرایه را دریافت کند‪.‬‬
‫فرض کنید تابعی بنام ‪ sumArray‬داریم که یک آرایه یک بعدی از اعداد‬
‫صحیح را بعنوان ورودی دریافت مینماید و مجموع عناصر آن را باز‬
‫میگرداند‪.‬‬
‫تعریف این تابع بصورت زیر است‪:‬‬
‫{ )‪int sumArray(int A[], int size‬‬
‫;‪int i , sum = 0‬‬
‫)‪for (i=0; i<size; i++‬‬
‫;]‪sum += A[i‬‬
‫; )‪return(sum‬‬
‫}‬
‫‪ 10-4‬ارسال آرایههای یک بعدی به توابع‬
‫‪‬‬
‫همانطور که میبینید‪ ،‬اندازه آرایه مشخص نشده است و این یک نکته مثبت است؛ چرا‬
‫که تابع‪ sumArray‬میتواند هر آرایه صحیحی را با هر اندازهای دریافت نماید‪.‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫درواقع حتی اگر اندازه آرایه را نیز مشخص نمایید‪ ،‬کامپایلر از آن صرفنظر خواهد کرد‪.‬‬
‫دومین پارامتر‪ ،‬اندازه واقعی آرایه ‪ A‬را مشخص مینماید‪ .‬معموال توابع بگونهای نوشته‬
‫میشوند که هنگام ارسال یک آرایه به یک تابع‪ ،‬اندازه آن نیز بعنوان یک پارامتر ارسال‬
‫گردد‪.‬‬
‫در هنگام فراخوانی تابع ‪ ،sumArray‬برای ارسال آرایه موردنظر کافی است که تنها نام‬
‫آرایه را بدون کروشه استفاده نماییم‪.‬‬
‫{ )(‪void main‬‬
‫;}‪int data1[3] = {5, 10, 15‬‬
‫; }‪int data2[5] = {1, 6, 4, 12, 5‬‬
‫;‪int sum1, sum2‬‬
‫;)‪sum1 = sumArray(data1, 3‬‬
‫;)‪sum2 = sumArray(data2, 5‬‬
‫;)‪printf("sum1 = %d\n",sum1‬‬
‫;)‪printf("sum2 = %d\n",sum2‬‬
‫}‬
‫‪ 10-4‬ارسال آرایههای یک بعدی به توابع‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫نکته بسیار مهم‪ ،‬نحوه ارسال آرایهها به توابع است‪.‬‬
‫زبان ‪ C‬آرایهها را توسط ارجاع به تابع ارسال مینماید‪ ،‬بدین معنا که در هنگام ارسال‬
‫یک آرایه به تابع‪ ،‬بجای یک کپی از آرایه‪ ،‬خود آرایه ارسال میشود‪.‬‬
‫در حقیقت در فصلهای بعدی خواهید دید که برای ارسال یک آرایه‪ ،‬آدرس اولین عنصر آن‬
‫ارسال میگردد‪ .‬لذا تابع میتواند از طریق این آدرس‪ ،‬به کلیه دادههای آرایه اصلی‬
‫دسترسی پیدا کند‪.‬‬
‫اما چرا ‪ C‬در مورد آرایهها به روش متفاوتی عمل مینماید؟‬
‫‪‬‬
‫‪‬‬
‫اما آیا میتوان یک آرایه را توسط مقدار به یک تابع ارسال کرد؟‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫دلیل این مسئله آن است که معموال یک آرایه حافظه بسیار زیادی را اشغال میکند‪ ،‬لذا تهیه‬
‫یک کپی کردن از آن‪ ،‬نه تنها باعث اشغال حافظه میشود بلکه زمان زیادی را نیز صرف خواهد‬
‫کرد‪.‬‬
‫متاسفانه خیر‪.‬‬
‫اما اگر نگران تغییر سهوی آرایه ارسالی به یک تابع هستید میتوانید آن را بگونهای به‬
‫تابع ارسال نمایید که تغییر آن در تابع ممکن نباشد‪.‬‬
‫زبان ‪ C‬یک نحوه دیگر ارسال دادهها به توابع بنام ارسال توسط ارجاع ثابت میباشد‪.‬‬
‫چنانچه در هنگام تعریف یک پارامتر از یک تابع‪ ،‬از کلمه کلیدی ‪ const‬استفاده شود‪،‬‬
‫کامپایلر اجازه تغییر مقادیر آن پارامتر را در حین اجرای تابع نخواهد داد‪ .‬با این ارسال‬
‫آرایهها بصورت ارجاع ثابت‪ ،‬میتوانیم مانع از انجام تغییرات ناخواسته در آرایه شویم‪.‬‬
‫ ارسال آرایههای یک بعدی به توابع‬10-4
.‫ اشتراک دو مجموعه را محاسبه و چاپ نماید‬،‫) برنامهای بنویسید که با استفاده از یک تابع‬4 ‫مثال‬
void intersection(const int A[], int na, const int B[], int nb, int C[], int &nc) {
int i,k,j,sw;
k = 0;
for (i=0; i<na; i++) {
sw = 1;
for (j=0; j<nb && sw; j++)
if (A[i] == B[j]) {
C[k] = A[i] ;
k ++;
sw = 0;
}
}
nc = k;
}
void printSet(int set[], int size) {
int i;
printf("{ ") ;
for (i=0; i<size; i++)
printf("%d ",set[i]) ;
printf("}\n");
}

‫ ارسال آرایههای یک بعدی به توابع‬10-4
void
int
int
int
main() {
set1[5] = {5, 8, 3, 12, 20};
set2[3] = {12, 16, 8} ;
result[3] , resultSize ;
intersection(set1, 5, set2, 3, result, resultSize);
printf("set 1 = ");
printSet(set1,5) ;
printf("set 2 = ");
printSet(set2,3) ;
printf("intersection = ");
printSet(result,resultSize) ;
}
set1 = { 5 8 3 12 20 }
set2 = { 12 16 8 }
intersection = { 8 12 }
‫‪ 10-5‬ارسال آرایههای چندبعدی به توابع‬
‫‪‬‬
‫‪‬‬
‫در این قسمت‪ ،‬ابتدا به نحوه ارسال آرایههای دو بعدی به توابع میپردازیم و‬
‫سپس آرایههای با ابعاد باالتر را بررسی خواهیم کرد‪.‬‬
‫شاید تصور کنید که برای تعریف یک آرایه دوبعدی بعنوان پارامتری از یک تابع‪،‬‬
‫تنها قرار دادن دو عالمت ][ کافی است و نیازی به ذکر ابعاد آن نیست‪.‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫اما متاسفانه اینگونه نیست‪ ،‬بلکه برنامهنویس باید تعداد ستونهای آرایه دوبعدی را‬
‫صریحا مشخص نماید‪ ،‬اما نیازی به تعیین تعداد ردیفهای آن نیست‪.‬‬
‫بعنوان مثال فرض کنید تابعی مانند ‪ test‬داریم که بعنوان ورودی یک آرایه دو‬
‫بعدی و تعدادی پارامتر دیگر دریافت میکند‪ .‬تعریف تابع بصورت زیر اشتباه‬
‫است‪:‬‬
‫{ )… ‪void test(int A[][],‬‬
‫تعریف درست‪ ،‬تعریفی مانند زیر است‪:‬‬
‫{ )… ‪void test(int A[][10] ,‬‬
‫در هنگام فراخوانی تابع ‪ ،test‬میتوان هر آرایه دوبعدی ‪ 10‬ستونی را به آن‬
‫ارسال کرد‪ .‬آرایه ارسالی به تابع میتواند ‪ 5×10‬و یا ‪ 20×10‬باشد‪ ،‬اما‬
‫نمیتواند مثال ‪ 5×20‬باشد‪.‬‬
‫‪ 10-5‬ارسال آرایههای چندبعدی به توابع‬
‫‪‬‬
‫مثال ‪ )5‬تابعی بنویسید که میزان فروش تعدادی شرکت در ‪ 12‬ماه سال را بعنوان ورودی دریافت‪ ،‬و‬
‫میانگین فروش شرکتی را که بیشترین میانگین فروش را داشته است‪ ،‬بازگرداند‪.‬‬
‫{ )‪float maxSales(const long int sales[][12], int companyNo‬‬
‫;‪int i,j‬‬
‫;‪float average , max‬‬
‫;‪max = 0.0‬‬
‫{ )‪for (i=0 ;i<companyNo; i++‬‬
‫;‪average = 0‬‬
‫)‪for (j=0; j<12; j++‬‬
‫; ]‪average += sales[i][j‬‬
‫;‪average /= 12‬‬
‫)‪if (average > max‬‬
‫; ‪max = average‬‬
‫}‬
‫;)‪return(max‬‬
‫}‬
‫ ارسال آرایههای چندبعدی به توابع‬10-5
.‫) برنامهای بنویسید که حاصلضرب دو ماتریس را با استفاده از یک تابع محاسبه نماید‬6 ‫مثال‬
#include <stdio.h>
const int maxCol = 10;
void multiply(const int A[][maxCol], const int B[][maxCol], int m,int p, int n, int C[][maxCol] ) {
int i,j,k,sum;
for (i=0; i<m; i++)
for (j=0; j<n; j++) {
sum = 0;
for (k=0; k<p; k++)
sum += A[i][k] * B[k][j] ;
C[i][j] = sum;
}
}
void printMatrix(int matrix[][maxCol], int row,int col) {
int i,j;
for (i=0; i<row; i++) {
for (j=0; j<col ;j++)
printf("%d ",matrix[i][j]);
printf("\n");
}
}

‫ ارسال آرایههای چندبعدی به توابع‬10-5
void main() {
int matrix1[2][maxCol] = { {7 ,3 , 2} , {-2, 6, 1} };
int matrix2[3][maxCol] = { {2 , 7 , -4, -1} , { 3 ,-3, 5, -8} , {6, -7, 2, 3} };
int result[2][maxCol] ;
multiply(matrix1, matrix2, 2, 3, 4, result);
printf("matrix1 is :\n");
printMatrix(matrix1,2,3) ;
printf("\nmatrix2 is :\n");
printMatrix(matrix2,3,4) ;
printf("\nmultiply of matrix1 and matrix2 is :\n");
printMatrix(result,2,4) ;
}
matrix1 is :
7 3 2
-2 6 1
matrix2
2 7 -4
3 -3 5
6 -7 2
is :
-1
-8
3
multiply of matrix1 and matrix 2 is :
35 26 -9 -25
20 -39 40 -43
‫‪ 10-5‬ارسال آرایههای چندبعدی به توابع‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫اما ارسال آرایه های با ابعاد باالتر به توابع نیز مشابه آرایه های دوبعدی‬
‫است‪.‬‬
‫به این صورت که در هنگام تعریف یک پارامتر از تابع بعنوان یک آرایه‬
‫چندبعدی‪ ،‬مشخص کردن بعد اول لزومی ندارد‪ ،‬اما اندازه کلیه ابعاد بعدی‬
‫باید حتما مشخص گردد‪.‬‬
‫بعنوان مثال به تابع زیر دقت کنید ‪:‬‬
‫{ ) … ‪void test(int A[][5][10],‬‬
‫این تابع بعنوان ورودی یک آرایه سه بعدی دریافت می نماید که حتما باید‬
‫بعد دوم آن ‪ 5‬و بعد سوم آن ‪ 10‬باشند‪ ،‬اما اندازه بعد اول هر مقداری می‬
‫تواند باشد‪.‬‬
‫‪ 10-6‬برخی عملیات مهم برروی آرایه ها‬
‫‪‬‬
‫‪‬‬
‫همانطور که قبال گفته شد‪ ،‬آرایهها از ساختمان داده های‬
‫بسیار مهم برنامهنویسی بوده و تقریبا می توان گفت هیچ‬
‫برنامه ای وجود ندارد که به نحوی از آرایهها استفاده نکند‪.‬‬
‫به همین دلیل برای انجام بعضی از عملیات مهم بر روی آرایه‬
‫ها‪ ،‬الگوریتمهای کارا و موثری توسط محققین ابداع شده‬
‫است‪ ،‬که چند نمونه از مهمترین آنها را بررسی می نماییم‪.‬‬
‫‪ 10-6-1‬الگوریتمهای مرتب سازی‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫شاید مهمترین عملی که برروی یک آرایه یک بعدی انجام‬
‫می شود‪ ،‬مرتب کردن آن بصورت صعودی یا نزولی است‪.‬‬
‫بدلیل اهمیت این کار‪ ،‬الگوریتمهای متعددی برای آن ابداع‬
‫شده است که برخی از آنها بسیار پیچیده هستند‪.‬‬
‫در اینجا ‪ 3‬الگوریتم ساده مورد بررسی قرار گرفتهاند که‬
‫برای آرایه های کوچک بسیار کارا هستند‪ .‬اما برای مرتب‬
‫سازی آرایه های بزرگ‪ ،‬باید از روشهای پیچیده تری‬
‫استفاده شود که در کتابهای پیشرفته تر مورد بررسی‬
‫قرار گرفته اند‪.‬‬
‫در الگوریتمهای زیر فرض شده است که قصد داریم آرایه را‬
‫بصورت صعودی مرتب نماییم‪ ،‬گرچه با یک تغییر کوچک‬
‫می توان آن را به نزولی تبدیل نمود‪.‬‬
‫‪ 10-6-1-1‬الگوریتم مرتب سازی انتخابی‬
‫‪...‬‬
‫‪8‬‬
‫‪8‬‬
‫‪16‬‬
‫‪12‬‬
‫‪22‬‬
‫‪22‬‬
‫‪22‬‬
‫‪16‬‬
‫‪12‬‬
‫‪35‬‬
‫‪35‬‬
‫‪35‬‬
‫‪27‬‬
‫‪27‬‬
‫‪27‬‬
‫‪16‬‬
‫‪12‬‬
‫‪8‬‬
‫‪41‬‬
‫‪41‬‬
‫‪41‬‬
‫‪19‬‬
‫‪19‬‬
‫‪19‬‬
‫مرحله دوم‬
‫مرحله اول‬
‫مرحله سوم‬
‫ الگوریتم مرتب سازی انتخابی‬10-6-1-1
void selectionSort(int A[], int n) {
int i,j;
for (i=0; i<n; i++)
for (j=i+1; j<n; j++)
if (A[i] > A[j]) swap(A[i],A[j]) ;
}
‫‪ 10-6-1-2‬الگوریتم مرتب سازی حبابی‬
‫‪...‬‬
‫‪12‬‬
‫‪16‬‬
‫‪16‬‬
‫‪16‬‬
‫‪12‬‬
‫‪22‬‬
‫‪22‬‬
‫‪22‬‬
‫‪12‬‬
‫‪8‬‬
‫‪27‬‬
‫‪35‬‬
‫‪27‬‬
‫‪8‬‬
‫‪27‬‬
‫‪19‬‬
‫‪35‬‬
‫‪8‬‬
‫‪35‬‬
‫‪19‬‬
‫‪41‬‬
‫‪41‬‬
‫‪41‬‬
‫‪19‬‬
‫مرحله دوم‬
‫مرحله اول‬
‫مرحله سوم‬
‫ الگوریتم مرتب سازی حبابی‬10-6-1-2
void bubbleSort(int A[], int n) {
int i, contSw;
do {
contSw = 0;
n --;
for (i=0; i<n; i++)
if (A[i] > A[i+1]) {
swap(A[i], A[i+1]) ;
contSw = 1;
}
} while (contSw) ;
}
‫‪ 10-6-1-3‬الگوریتم مرتب سازی درجی‬
‫‪‬‬
‫‪‬‬
‫در این روش مرتب سازی از عمل درج استفاده می شود‪.‬‬
‫این روش را می توان بصورت زیر مرحله بندی کرد‪:‬‬
‫‪ ‬مرحله ‪ -1‬فرض می کنیم آرایه فقط دارای عنصر اول است و سایر عناصر‬
‫را درنظر نمی گیریم‪ .‬در اینصورت یک آرایه یک عنصری مرتب داریم‬
‫‪ ‬مرحله ‪ – 2‬عنصر دوم را در آرایه مرتب مرحله قبل درج می نماییم‪ .‬اکنون‬
‫یک آرایه دو عنصری مرتب داریم‪.‬‬
‫‪ ‬مرحله ‪ -3‬عنصر سوم را در آرایه مرتب مرحله قبل درج می نماییم‪ .‬اکنون‬
‫یک آرایه سه عنصری مرتب داریم‪.‬‬
‫‪... ‬‬
‫‪ ‬مرحله ‪ –k‬عنصر ‪k‬ام را در آرایه مرتب ‪ k-1‬عنصری مرحله قبل درج می‬
‫نماییم تا یک آرایه ‪ k‬عنصری مرتب بدست آوریم‪.‬‬
‫‪... ‬‬
‫‪ ‬مرحله ‪ –n‬عنصر ‪ n‬را در آرایه مرتب مرحله قبل درج کرده تا آرایه مرتب‬
‫نهایی حاصل شود‪.‬‬
‫‪ 10-6-1-3‬الگوریتم مرتب سازی درجی‬
‫‪8‬‬
‫‪8‬‬
‫‪12‬‬
‫‪12‬‬
‫‪12‬‬
‫‪16‬‬
‫‪16‬‬
‫‪12‬‬
‫‪12‬‬
‫‪16‬‬
‫‪16‬‬
‫‪16‬‬
‫‪22‬‬
‫‪22‬‬
‫‪16‬‬
‫‪16‬‬
‫‪22‬‬
‫‪22‬‬
‫‪22‬‬
‫‪12‬‬
‫‪12‬‬
‫‪22‬‬
‫‪22‬‬
‫‪27‬‬
‫‪35‬‬
‫‪35‬‬
‫‪35‬‬
‫‪35‬‬
‫‪27‬‬
‫‪27‬‬
‫‪35‬‬
‫‪27‬‬
‫‪27‬‬
‫‪27‬‬
‫‪27‬‬
‫‪35‬‬
‫‪35‬‬
‫‪8‬‬
‫‪8‬‬
‫‪8‬‬
‫‪8‬‬
‫‪8‬‬
‫‪41‬‬
‫‪41‬‬
‫‪41‬‬
‫‪41‬‬
‫‪41‬‬
‫‪41‬‬
‫‪41‬‬
‫‪19‬‬
‫‪19‬‬
‫‪19‬‬
‫‪19‬‬
‫‪19‬‬
‫‪19‬‬
‫‪19‬‬
‫مرحله هفتم مرحله ششم‬
‫مرحله پنجم‬
‫مرحله چهارم‬
‫مرحله سوم‬
‫مرحله دوم‬
‫مرحله اول‬
‫ الگوریتم مرتب سازی درجی‬10-6-1-3
void insertionSort(int A[], int n) {
int i, j, cur;
for (i=1; i<n; i++) {
cur = A[i] ;
for (j=i-1; j>=0 && cur<A[j]; j--)
A[j+1] = A[j] ;
A[j+1] = cur ;
}
}
‫‪ 10-6-1-3‬الگوریتم مرتب سازی درجی‬
‫‪‬‬
‫در مورد سه الگوریتم مرتب سازی ذکرشده چند نکته قابل‬
‫ذکر است‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫هر ‪ 3‬الگوریتم برای مرتب سازی یک آرایه از اعداد صحیح )‪(int‬‬
‫نوشته شده اند‪ ،‬اما روش بهتر آن است که آنها را بصورت یک‬
‫الگو بنویسیم که قادر به مرتب سازی هر نوع آرایهای باشند‪.‬‬
‫الگوها و نحوه پیاده سازی آنها در فصل گذشته مورد بحث قرار‬
‫گرفتند‪.‬‬
‫هر ‪ 3‬الگوریتم برای مرتب سازی بصورت صعودی نوشته شده اند‪،‬‬
‫اما میتوان آنها را با یک تغییر کوچک به مرتب سازی نزولی تبدیل‬
‫کرد‪.‬‬
‫حتی میتوان تابع را بگونهای نوشت که یک پارامتر دیگر برای‬
‫تعیین نوع مرتب سازی نیز بعنوان ورودی دریافت و براساس مقدار‬
‫آن‪ ،‬مرتب سازی را بصورت صعودی یا نزولی انجام دهد‪.‬‬
‫‪ 10-6-2‬الگوریتمهای جستجو‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫یکی دیگر از الگوریتمهای بسیار مهم برای آرایه ها‪،‬‬
‫الگوریتمهای جستجو هستند‪.‬‬
‫موارد زیادی پیش میآیند که قصد داریم به دنبال یک داده در‬
‫یک آرایه جستجو کرده و مکان آن را پیدا کنیم‪.‬‬
‫در این قسمت دو روش متداول جستجو را مورد بررسی قرار‬
‫میدهیم‪ .‬البته معموال برای عمل جستجو از ساختمان‬
‫دادههای پیچیده تری همچون درختهای جستجوی دودویی‬
‫استفاده میگردد‪ ،‬که از بحث ما خارج است‪.‬‬
‫‪ 10-6-2-1‬الگوریتم جستجوی خطی‬
‫‪‬‬
‫جستجوی خطی‪ ،‬ساده ترین نوع جستجو است که در آرایههای نامرتب‬
‫استفاده میشود‪ .‬در این روش داده مورد نظر به ترتیب با تک تک عناصر آرایه‬
‫مقایسه میشود تا مکان آن پیدا شود‪.‬‬
‫{ )‪int linearSearch(int A[], int n, int x‬‬
‫;‪int i‬‬
‫)‪for (i=0; i<n; i++‬‬
‫;)‪if (x == A[i]) return(i‬‬
‫; )‪return(-1‬‬
‫‪‬‬
‫‪‬‬
‫}‬
‫اگر آرایه دارای ‪ n‬عنصر باشد‪ ،‬در بدترین حالت نیاز به ‪ n‬مقایسه برای پیدا کردن‬
‫داده مورد نظر داریم و این در صورتی است که داده در آخرین مکان آرایه قرار‬
‫داشته باشد‪.‬‬
‫اما از آنجا که احتمال قرار گرفتن داده در هریک از مکانهای آرایه یکسان است‪،‬‬
‫بطور متوسط نیاز به ‪ n/2‬مقایسه خواهیم داشت‪.‬‬
‫‪ 10-6-2-2‬الگوریتم جستجوی دودویی‬
‫‪‬‬
‫‪‬‬
‫چنانچه آرایه مورد جستجو مرتب شده باشد‪ ،‬روش بسیار کاراتری برای جستجو وجود‬
‫دارد‪ .‬این روش که به جستجوی دودویی موسوم است‪ ،‬با هربار مقایسه‪ ،‬نیمی از عناصر‬
‫آرایه را از بازه جستجو حذف مینماید‪ .‬در نتیجه جستجو در یک آرایه بزرگ با سرعت‬
‫بسیار زیادی صورت میپذیرد‪.‬‬
‫برای تشریح الگوریتم‪ ،‬فرض کنید آرایه موردنظر بصورت صعودی مرتب شده است‪ .‬ابتدا‬
‫عنصر وسط آرایه را پیدا کرده و داده مورد جستجو را با آن مقایسه میکنیم‪ .‬سه حالت‬
‫ممکن است رخ دهد‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫اگر داده مورد جستجو با عنصر وسط آرایه مساوی باشد‪ ،‬داده پیدا شده و مکان آن را باز‬
‫میگردانیم‪.‬‬
‫اگر داده مورد جستجو از عنصر وسط آرایه کوچکتر باشد‪ ،‬بنابراین باید در نیمه اول آرایه به دنبال‬
‫آن جستجو نماییم‪.‬‬
‫اگر داده مورد جستجو از عنصر وسط آرایه بزرگتر باشد‪ ،‬بنابراین باید در نیمه دوم آرایه به دنبال‬
‫آن جستجو نماییم‪.‬‬
‫چنانچه حالت اول رخ دهد‪ ،‬جستجو پایان یافته و مکان داده بازگردانده میشود‪ .‬اما اگر‬
‫حالت دوم یا سوم رخ دهد‪ ،‬عملیات جستجو به روش فوق مجددا برای نیمه اول یا نیمه‬
‫دوم آرایه تکرار میشود‪ .‬بدین ترتیب بازه مورد جستجو به نیمی از آرایه کاهش مییابد‪.‬‬
‫عملیات تا زمانی ادامه مییابد که یا داده مورد نظر پیدا شود و یا بازه مورد جستجو آنقدر‬
‫کوچک شود که دادهای باقی نماند (یعنی طول بازه مورد جستجو به صفر برسد)‪ ،‬که در‬
‫اینصورت داده در آرایه وجود ندارد‪.‬‬
‫‪ 10-6-2-2‬الگوریتم جستجوی دودویی‬
‫…‬
‫‪0‬‬
‫<‬
‫‪0‬‬
‫‪23‬‬
‫=‬
‫داده پیدا شد‬
‫…‬
‫‪0‬‬
‫‪24‬‬
‫‪x‬‬
‫<‬
‫‪25‬‬
‫‪48‬‬
‫>‬
‫‪48‬‬
‫=‬
‫داده پیدا شد‬
‫…‬
‫‪49‬‬
‫‪50‬‬
‫<‬
‫‪50‬‬
‫‪73‬‬
‫=‬
‫داده پیدا شد‬
‫…‬
‫‪74‬‬
‫‪75‬‬
‫>‬
‫‪99‬‬
‫‪99‬‬
‫‪x‬‬
‫>‬
‫‪99‬‬
‫‪x‬‬
‫ الگوریتم جستجوی دودویی‬10-6-2-2
int binarySearch(int A[], int n, int x) {
int low, high, mid;
low = 0;
high = n-1;
while (low <= high) {
mid = (low + high) / 2;
if (x == A[mid]) return(mid);
else if (x < A[mid]) high = mid-1;
else low = mid + 1;
}
return(-1);
}
‫‪ 10-6-2-2‬الگوریتم جستجوی دودویی‬
‫حد پایین‬
‫حد باال‬
‫‪8‬‬
‫‪0‬‬
‫‪11‬‬
‫‪1‬‬
‫‪23‬‬
‫‪2‬‬
‫‪35‬‬
‫‪3‬‬
‫‪42‬‬
‫‪4‬‬
‫‪53‬‬
‫‪5‬‬
‫‪58‬‬
‫‪6‬‬
‫‪62‬‬
‫‪7‬‬
‫‪71‬‬
‫‪8‬‬
‫‪78‬‬
‫‪9‬‬
‫‪23‬‬
‫ الگوریتم جستجوی دودویی‬10-6-2-2
int recBinarySearch(int A[], int low, int high, int x) {
int mid;
if (low > high) return(-1);
mid = (low + high) / 2;
if (x == A[mid]) return(mid);
else if (x < A[mid]) return(recBinarySearch(A, low, mid-1, x));
else return(recBinarySearch(A, mid+1, high, x)) ;
}
100 ‫ در آرایه‬52 ‫نحوه فراخوانی اولیه این تابع برای جستجوی داده‬
:‫ بصورت است‬data ‫عنصری‬
recBinarySearch(data, 0, 99, 52)

‫‪ 10-6-2-2‬الگوریتم جستجوی دودویی‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫برای محاسبه زمان مورد نیاز یک جستجو‪ ،‬باید به این نکته‬
‫توجه کرد که با هر مقایسه‪ ،‬بازه جستجو نصف می شود‪.‬‬
‫بدترین حالت زمانی است که داده اصال پیدا نشود و یا در‬
‫آخرین مقایسه (زمانی که تنها یک عنصر باقی مانده است)‬
‫پیدا شود‪.‬‬
‫سوال اینجاست که چند بار می توان اندازه آرایه را نصف کرد تا‬
‫سرانجام به یک عنصر رسید؟‬
‫‪n‬‬
‫‪2‬‬
‫‪log‬‬
‫‪‬‬
‫بعنوان مثال در یک آرایه با ‪ 1.000.000‬عنصر‪ ،‬تنها به ‪20‬‬
‫مقایسه نیاز است‪ .‬به همین دلیل جستجوی دودویی یکی از‬
‫بهترین روشهای جستجو محسوب می گردد‪.‬‬
‫‪ 10-6-3‬الگوریتم ادغام‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫یکی دیگر از الگوریتمهای مفید‪ ،‬ادغام دو آرایه مرتب است‪ .‬ادغام دو آرایه‬
‫مرتب به معنای ایجاد یک آرایه مرتب دیگر است که حاوی تمام عناصر دو‬
‫آرایه اولیه باشد‪.‬‬
‫یک روش ضعیف برای این کار آن است که ابتدا عناصر هر دو آرایه را در آرایه‬
‫جواب کپی کنیم و سپس آرایه جواب را مرتب نماییم‪ .‬اما مرتب سازی آرایه‬
‫جواب زمان زیادی را صرف خواهد کرد‪.‬‬
‫خوشبختانه الگوریتم بهتری وجود دارد که در آن نیازی به مرتب سازی‬
‫نهایی آرایه جواب نیست‪.‬‬
‫طرح کلی این الگوریتم به شرح زیر است‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫یک شمارنده برای هریک از دو آرایه در نظر بگیرید و آنها را برابر صفر قرار دهید‪.‬‬
‫در هر مرحله‪ ،‬دو عنصری را که شمارندهها برروی آنها قرار دارند‪ ،‬با یکدیگر‬
‫مقایسه نمایید؛ عنصر کوچکتر را به آرایه جواب منتقل کرده و شمارنده مربوط به‬
‫آن را یک واحد افزایش دهید‪ .‬این مرحله را تا پایان یافتن یکی از دو آرایه تکرار‬
‫کنید‪.‬‬
‫در پایان‪ ،‬تمام عناصر باقیمانده از آرایه ناتمام را به ترتیب به آرایه جواب منتقل‬
‫نمایید‪.‬‬
‫‪ 10-6-3‬الگوریتم ادغام‬
‫‪8‬‬
‫‪8‬‬
‫‪17‬‬
‫‪14‬‬
‫‪14‬‬
‫‪32‬‬
‫‪17‬‬
‫‪43‬‬
‫‪49‬‬
‫‪32‬‬
‫‪52‬‬
‫‪56‬‬
‫‪43‬‬
‫‪64‬‬
‫‪49‬‬
‫‪73‬‬
‫‪52‬‬
‫‪56‬‬
‫‪64‬‬
‫‪73‬‬
‫ الگوریتم ادغام‬10-6-3
void merge(int A[], int na, int B[], int nb, int C[], int &nc) {
int i=0, j=0, k=0;
while (i<na && j<nb) {
if (A[i] < B[j]) {
C[k] = A[i] ;
i++ ;
}
else if (A[i] > B[j]) {
C[k] = B[j] ;
j++ ;
}
else {
C[k] = A[i] ;
i++ ;
j ++ ;
}
k++ ;
}
for (; i<na; i++, k++) C[k] = A[i];
for (; j<nb; j++, k++) C[k] = B[j];
nc = k;
}
‫مبانی کامپیوتر و برنامه سازی‬
‫فصل‌یازدهم‌‪ :‬رشته‌ها‬
‫مدرس‌‪ :‬رضا‌رمضانی‬
‫‪ 11‬رشته ها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫رشتهها یکی از مهمترین انواع دادهها در زبانهای برنامه سازی هستند‪.‬‬
‫یک رشته به یک توالی از صفر یا چند کاراکتر گفته می شود‪.‬‬
‫بعنوان مثال ‪ Ali‬یک رشته کاراکتری است‪.‬‬
‫از رشتهها در موارد بسیاری همچون ذخیره اسم و آدرس استفاده می شود‪.‬‬
‫در زبان ‪C‬نوع داده مشخصی برای رشته وجود ندارد‪ ،‬بلکه یک رشته بصورت‬
‫آرایه ای از کاراکترها تعریف می گردد‪ .‬بعنوان مثال‪:‬‬
‫;]‪char name[10‬‬
‫متغیر ‪ name‬می تواند یک رشته با حداکثر ‪ 10‬کاراکتر را در خود نگاه دارد‪.‬‬
‫اما فرض کنید قصد داریم رشته ای مانند ‪ Ali‬را در این متغیر ذخیره نماییم که‬
‫کمتر از ‪ 10‬کاراکتر دارد‪ .‬دراینصورت زبان ‪ C‬چگونه دریابد که در هنگام انجام‬
‫عملیات مختلف برروی این رشته‪ ،‬مثال در هنگام چاپ آن‪ ،‬فقط باید ‪ 3‬حرف اول‬
‫رشته را چاپ نماید؟‬
‫‪ 11‬رشته ها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫برای حل این مشکل‪ ،‬طراحان زبان ‪ C‬از یک کاراکتر خاص بنام ‪null‬‬
‫استفاده کردند‪.‬‬
‫کلیه رشتهها در زبان ‪ C‬باید به کاراکتر ‪ null‬ختم گردند‪ .‬در حقیقت در‬
‫زبان ‪ C‬یک رشته هنگامی که به ‪ null‬برسد‪ ،‬خاتمه می یابد و نه‬
‫زمانیکه به انتهای آرایه برسد‪.‬‬
‫کاراکتر ‪ ،null‬دارای کد اسکی ‪0‬می باشد و با '‪ '\0‬نشان داده می شود‪.‬‬
‫بعنوان مثال به نمونه زیر دقت کنید‪:‬‬
‫;} '‪char name[10] = {'A', 'l', 'i', '\0‬‬
‫دقت کنید که در حقیقت ‪ 4‬عضو از آرایه پر شده است و عضو آخر به‬
‫‪ null‬تخصص داده شده است‪.‬‬
‫مقادیر عناصر بعدی آرایه مهم نیست‪ ،‬چرا که در هنگام انجام عملیات‬
‫برروی رشته ‪ name‬از آنها صرفنظر می گردد‪.‬‬
‫بنابراین دقت کنید که در هنگام تعریف یک رشته‪ ،‬یک عنصر اضافه برای‬
‫کاراکتر ‪ null‬در نظر بگیرید‪.‬‬
‫‪ 11‬رشته ها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫همانطور که در فصول قبل گفتیم‪ ،‬در زبان ‪C‬امکان تعریف ثابت رشته ای نیز‬
‫وجود دارد‪ .‬یک ثابت رشته ای‪ ،‬دنباله ای از کاراکترها است که در داخل "‬
‫قرار می گیرد‪.‬‬
‫بعنوان مثال "‪ "Ali‬نشاندهنده یک ثابت رشته ای است‪.‬‬
‫توجه کنید که هنگامی که از " برای یک ثابت رشته ای استفاده می کنید‪،‬‬
‫کامپایلر یک عالمت ‪ null‬در انتهای رشته اضافه می کند‪.‬‬
‫از ثوابت رشته ای برای مقداردهی اولیه به متغیرهای رشته ای نیز می‬
‫توان استفاده کرد‪ .‬بعنوان مثال‪:‬‬
‫; "‪char name[10] = "Ahmad‬‬
‫; "‪char address[50] = "No. 20 Azadi Street‬‬
‫‪d \0‬‬
‫‪m a‬‬
‫‪h‬‬
‫‪A‬‬
‫‪name‬‬
‫‪ 11-1‬خواندن و نوشتن رشته ها‬
‫‪‬‬
‫‪‬‬
‫برای خواندن و نوشتن رشتهها می توان از توابع ‪ scanf‬و ‪ printf‬استفاده‬
‫کرد‪ .‬تنها نکته این است که در داخل رشته کنترلی باید از مشخصه تبدیل‬
‫‪ %s‬استفاده نمایید‪.‬‬
‫بعنوان مثال به نمونه زیر دقت کنید‪:‬‬
‫>‪#include <stdio.h‬‬
‫{ )(‪void main‬‬
‫;]‪char name[20‬‬
‫;)" ? ‪printf("what is your name‬‬
‫;)‪scanf("%s" ,name‬‬
‫;)‪printf("Hello %s !", name‬‬
‫}‬
‫‪what is your name ? Mohamad‬‬
‫‪Ali‬‬
‫‪reza‬‬
‫‪Hello Mohamad‬‬
‫! ‪Ali‬‬
‫!‬
‫‪ 11-1‬خواندن و نوشتن رشته ها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫برای رفع این مشکل می توان از تابع دیگری بنام ‪ gets‬استفاده کرد‪.‬‬
‫این تابع یک متغیر رشته ای را بعنوان پارامتر ورودی دریافت و پس از خواندن یک رشته از‬
‫صفحه کلید‪ ،‬آن را در پارامتر ورودی قرار داده و باز می گرداند‪.‬‬
‫نکته مهم این است که تابع ‪ gets‬تا زمانیکه کلید ‪ Enter‬فشرده نشده است‪ ،‬به خواندن‬
‫دادهها از صفحه کلید ادامه می دهد‪ .‬بنابراین رشته می تواند دارای فضای خالی‬
‫)‪(space‬نیز باشد‪.‬‬
‫به بازنویسی مثال قبل با تابع ‪ gets‬دقت کنید‪:‬‬
‫>‪#include <stdio.h‬‬
‫{ )(‪void main‬‬
‫;]‪char name[20‬‬
‫;)" ? ‪printf("what is your name‬‬
‫;)‪gets(name‬‬
‫;)‪printf("Hello %s !", name‬‬
‫}‬
‫الزم به ذکر است که برای چاپ رشتهها نیز تابعی بنام ‪ puts‬وجود دارد‪ .‬بعنوان مثال بجای‬
‫آخرین تابع ‪ printf‬در مثال باال می توانستید به شکل زیر عمل کنید‪:‬‬
‫;)" ‪puts("Hello‬‬
‫; )‪puts(name‬‬
‫‪ 11-2‬توابع کتابخانه ای رشته ای‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫زبان ‪ C‬دارای یک کتابخانه غنی از توابع کار با رشتهها است‪.‬‬
‫پیش تعریف این توابع در فایل سرآمد ‪ string.h‬آمده است‪.‬‬
‫در این قسمت چندین تابع از کتابخانه ‪ C‬به همراه الگوریتم آنها بررسی‬
‫می گردند‪ .‬هدف از بررسی الگوریتم این توابع‪ ،‬آشنایی بیشتر شما با‬
‫نحوه کار با رشتهها می باشد‪.‬‬
‫دقت کنید که ممکن است تعریف دقیق تابع کتابخانه ای در ‪ C‬با آنچه که‬
‫ما در اینجا آورده ایم‪ ،‬کمی متفاوت باشد‪.‬‬
‫نکته بسیار مهمی که در هنگام بررسی این توابع باید بدان توجه داشته‬
‫باشید‪ ،‬این است که هیچیک از آنها حدود آرایه (اندازه آرایه) را بررسی‬
‫نمی کنند‪.‬‬
‫بنابراین این وظیفه خود برنامهنویس است که آرایه هایی با اندازه‬
‫مناسب را به توابع ارسال کند‪.‬‬
(string length) strlen ‫تابع‬
int strlen(const char string[]) {
int i;
for (i=0; string[i]; i++) ;
return(i);
}
string
A
h
i=0
i=1
m a
i=2
i=3
d \0
i=4 i=5
(string length) strlen ‫تابع‬
:‫ را نشان می دهد‬strlen ‫برنامه زیر نحوه استفاده از‬
void main() {
char text[100];
int len;
printf("enter a text : ");
gets(text);
len = strlen(text);
printf("length of your text is %d",len);
}
enter a text : Hello
length of your text is 5

(string copy) strcpy ‫تابع‬
void strcpy(char dest[], const char source[]) {
int i;
for (i=0; source[i]; i++)
dest[i] = source[i] ;
dest[i] = '\0' ;
}
source
dest
A
h
i=0
i=1
A
h
i=0
i=1
m a
i=2
i=3
m a
i=2
i=3
d \0
i=4 i=5
d \0
i=4 i=5
(string copy) strcpy ‫تابع‬
:‫ را نشان می دهد‬strcpy ‫برنامه زیر نحوه استفاده از‬
void main() {
char string1[20], string2[20] ;
printf("Please enter string1 : ");
gets(string1) ;
strcpy(string2, string1) ;
printf("copy string1 into string2\n");
printf("now string1 = %s and string2 = %s\n", string1, string2);
strcpy(string1,"new") ;
printf("copy new into string1\n");
printf("now string1 = %s", string1);
}
Please enter string1 : Hello
copy string1 into string2
now string1 = Hello and string2 = Hello
copy new into string1
now string1 = new

(string concat) strcat ‫تابع‬
void strcat(char str1[], const char str2[]) {
int i, j ;
for (i=0; str1[i]; i++) ;
for (j=0; str2[j]; j++)
str1[i+j] = str2[j] ;
str1[i+j] = '\0' ;
}
str1
str2
i+j
i+j
i+j
i+j
i+j
e
z
a
\0
a
l
i
\0r
i=0
i=1
i=2
i=3
r
e
z
a
\0
j=3
j=4
j=0
j=1
j=2
(string concat) strcat ‫تابع‬
:‫ را نشان می دهد‬strcat ‫برنامه زیر نحوه استفاده از‬
void main() {
char string1[20], string2[20] ;
printf("please enter string1 : ");
gets(string1);
printf("please enter string2 : ");
gets(string2);
strcat(string1,string2);
printf("concatenate of string1 and string2 is : %s", string1) ;
}
please enter string1 : Hello
please enter string2 : everybody!
concatenate of string1 and string2 is : Hello everybody!

‫تابع ‪(string compare) strcmp‬‬
‫‪‬‬
‫این تابع دو رشته را دریافت و پس از مقایسه آنها یکی از ‪ 3‬مقدار زیر را باز می‬
‫گرداند‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫در صورتیکه مساوی باشند ‪0 :‬‬
‫در صورتیکه رشته اول بزرگتر باشد ‪+1 :‬‬
‫درصورتیکه رشته اول کوچکتر باشد ‪-1 :‬‬
‫{ )][‪int strcmp(const char str1[], const char str2‬‬
‫;‪int i‬‬
‫;‪i = -1‬‬
‫{ ‪do‬‬
‫;‪i ++‬‬
‫;)‪if (str1[i] > str2[i]) return(1‬‬
‫; )‪if (str1[i] < str2[i]) return(-1‬‬
‫; )]‪} while (str1[i‬‬
‫;)‪return(0‬‬
‫}‬
(string compare) strcmp ‫تابع‬
:‫ را نشان می دهد‬strcmp ‫برنامه زیر نحوه استفاده از‬
void main() {
char string1[20], string2[20] ;
int result;
printf("please enter string1 : ");
gets(string1);
printf("please enter string2 : ");
gets(string2);
result = strcmp(string1,string2);
if (result == 0)
printf("%s equals %s\n", string1, string2) ;
else if (result == 1)
printf("%s is grater than %s\n", string1, string2) ;
else printf("%s is less than %s\n", string1, string2) ;
}
please
please enter
enter string1
string1 :: ali
ali
please enter
enter string2
string2 :: ali
ahmad
alireza
please
ali equals
is
is grater
less ali
than
than
alireza
ahmad
ali

(string in string) strstr ‫تابع‬
int strstr(const char str1[], const char str2[]) {
int i, j ;
for (i=0; str1[i]; i++)
if (str1[i] == str2[0]) {
for (j=1; str2[j] && str1[i+j] == str2[j] ; j++) ;
if (!str2[j]) return(i);
}
return(-1) ;
}
i+j
str1
t h i
s
i+j
i
s
i+j i+j i+j i+j i+j i+j
a
s a m p l
i=0 i=1 i=2 i=3 i=4i=5 i=6 i=7 i=8 i=9 i=10
str2
s a m p l
e \0
J=1 J=2J=3J=4 J=5 J=6
e
t
e x t \0
(string in string) strstr ‫تابع‬
:‫ را نشان می دهد‬strstr ‫برنامه زیر نحوه استفاده از‬
void main() {
char text[100], word[20];
int i, n, result ;
printf("enter a text : ");
gets(text) ;
printf("how many words do you have : ");
scanf("%d",&n) ;
for (i=0; i<n; i++) {
printf("enter a word to search : ") ;
scanf("%s",word);
result = strstr(text, word) ;
if (result == -1)
printf("(%s) not found\n",word);
else printf("(%s) is founded in position %d\n", word, result);
}
}
enter a text : this is a sample text!
how many words do you have : 3
enter a word to search : sample
(sample) is founded in position 10
enter a word to search : is
(is) is founded in position 2
enter a word to search : test
(test) not found

(string reverse) strrev ‫تابع‬
void strrev(char string[]) {
int i, j, l;
char temp;
for (l=0; string[l]; l++) ;
for (i=0, j=l-1; i<j; i++, j--) {
temp = string[i] ;
string[i] = string[j] ;
string[j] = temp ;
}
}
string
c
l=0
i=0
o
i=1
l=1
u
n
i=2 i=3
l=2
l=3
J=3
t
e
r
j=4 l=5
j=5 j=6
l=4
l=6
\0
l=7
(ascii to integer) atoi ‫تابع‬
int atoi(const char string[]) {
int i=0 , sign=1, number=0;
if (string[0] == '-') {
sign = -1; i = 1;
}
else if (string[0] == '+')
i = 1;
for (; string[i]; i++)
if (string[i]>='0' && string[i]<='9')
number = number * 10 + (string[i] – 48) ; // or (string[i] – '0')
else return(0);
return(sign * number);
}
sign = 1
string
+
7
3
5
\0
7
10++)‘3’
)‘5’––48)
48)
0 * *10
)‘7’
number = 73
= 70
0
++7+3=
730
5=7=73
735
i=0
i=1
i=2
i=3
i=4
‫‪ 11-3‬چند تابع رشته ای مفید دیگر‬
‫‪‬‬
‫‪‬‬
‫همانطور که گفته شد‪ ،‬توابع مذکور همگی در کتابخانه ‪C‬‬
‫موجود هستند و نیازی به نوشتن آنها نیست‪.‬‬
‫اما بعضی اعمال مفید دیگر نیز بر روی رشتهها وجود دارند که‬
‫در توابع کتابخانه ای تعریف نشده اند‪ .‬در اینجا چند نمونه از این‬
‫توابع برای آشنایی بیشتر شما آمده است‪ .‬در نوشتن این‬
‫توابع‪ ،‬از توابع کتابخانه ای استفاده شده است‪.‬‬
strDelete ‫تابع‬
int strDelete(char str1[], const char str2[]) {
int i, position, length;
position = strstr(str1, str2);
if (position == -1) return(0);
length = strlen(str2) ;
for (i= position+length ; str1[i]; i++)
str1[i – length] = str1[i] ;
str1[i-length] = '\0' ;
return(1);
}
i
str1
t h i
s
i
s
a
e xp tl \0
s at m
e
position = 10
str2 s a m p l e \0
i
i
t
e x t \0
i
i
i
length = 6
strInsert ‫تابع‬
void strInsert(char str1[], const char str2[], int k) {
int i, length ;
length = strlen(str2);
for (i=strlen(str1); i >= k ; i --)
str1[i+length] = str1[i] ;
for (i=0 ;i<length; i++)
str1[k + i] = str2[i] ;
}
i i+4 i+4 i+4 i+4
i i+4
i i+4
k+i
i k+i
i k+i
i k+i
k+i
str1
t h i
i
s
s
a
nt e xw t t! \0
e x
k = 10
str2 n e w
\0
i=0 i=1 i=2 i=3 i=4
t
! \0
length = 4
‫‪ 11-4‬آرایهای از رشتهها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫در بعضی موارد الزم است که تعدادی رشته کاراکتری را‬
‫ذخیره نماییم‪.‬‬
‫بعنوان مثال فرض کنید قصد ذخیره سازی نام ‪ 5‬تن از‬
‫دانشجویان را داریم‪ .‬اگر برای هر نام ‪ 10‬کاراکتر در نظر‬
‫بگیریم‪ ،‬احتیاج به یک آرایه ‪ 5‬تایی از رشته های ‪10‬‬
‫کاراکتری خواهیم داشت‪.‬‬
‫برای تعریف آرایه ای از رشته ها‪ ،‬باید از یک آرایه دو بعدی‬
‫از کاراکترها استفاده کرد‪.‬‬
‫دستور زیر‪ ،‬آرایه مورد نظر برای نگاهداری نام دانشجویان‬
‫را ایجاد و به آن مقدار اولیه می دهد‪:‬‬
‫;} "‪char nameList[5][10] = {"Ali", "Reza", "Ahmad", "Babak", "Hamid‬‬
‫ آرایهای از رشته ها‬11-4
nameList[0]
nameList[1]
nameList[2]
nameList[3]
nameList[4]
A
R
l
e
i
z
\0
a
\0
A
h
m
a
d
\0
B
a
b
a
k
\0
H
a
m
i
d
\0
nameList‫آرایه‬
‫‪ 11-4‬آرایهای از رشته ها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫آرایه ‪ ،nameList‬یک آرایه دو بعدی از نوع کاراکتر است و همانند تمام آرایه های دو بعدی‬
‫دیگر می توان از طریق دو اندیس به اعضای آن دسترسی پیدا کرد‪.‬‬
‫بعنوان مثال عنصر ]‪ nameList[0][0‬در مثال فوق برابر '‪ 'A‬می باشد‪.‬‬
‫اما در یک آرایه از رشته ها‪ ،‬معموال برنامهنویس عملیات خود را بر روی رشته های آن‬
‫آرایه انجام می دهد و بطور مستقیم با تک تک کاراکترها کار نمی کند‪.‬‬
‫بعنوان مثال ممکن است بخواهد که رشته اول یعنی "‪ "Ali‬را چاپ نماید‪ .‬برای اینکار‬
‫کافی است از نام آرایه بعالوه یک اندیس استفاده نماییم‪.‬‬
‫بعنوان مثال ]‪ nameList[0‬یک متغیر رشته ای است که مقدار "‪ "Ali‬را در خود جای داده‬
‫است‪ .‬برای دسترسی به سایر رشتهها نیز کافی است از نام آرایه بعالوه اندیس ردیفی‬
‫که رشته در آن قرار دارد‪ ،‬استفاده نماییم‪.‬‬
‫حتی می توان با این روش‪ ،‬یک رشته را به یک تابع نیز ارسال کرد‪ .‬بعنوان مثال دستور‬
‫زیر مقداری را برای سومین رشته از کاربر دریافت می نماید ‪:‬‬
‫; )]‪gets(nameList[2‬‬
‫مثال ‪ )8‬برنامه ای بنویسید که ابتدا شماره دانشجویی‪ ،‬نام‪ ،‬معدل تعدادی دانشجو را‬
‫دریافت و ذخیره نماید‪ .‬سپس با دریافت شماره دانشجویی هر فرد به دنبال اطالعات وی‬
‫جستجو کرده و در صورت پیدا شدن‪ ،‬مشخصات وی را چاپ نماید‪ .‬برنامه با در یافت‬
‫شماره دانشجویی ‪ 0‬پایان می یابد‪.‬‬
‫ آرایهای از رشته ها‬11-4
#include <stdio.h>
const int maxStudent = 100;
//‫حداکثر تعداد دانشجویان‬
const int maxNameLen = 20;
//‫حداکثر طول نام هر دانشجو‬
void main() {
long int idList[maxStudent] ;
float averageList[maxStudent] ;
char nameList[maxStudent][maxNameLen] ;
long int searchId;
int i, n;
printf("enter student number : ");
scanf("%d",&n);
//‫دریافت اطالعات دانشجویان‬
for (i=0; i<n; i++) {
printf("student #%d :\n", i+1) ;
printf("enter id : ");
scanf("%ld", &idList[i]);
printf("enter name : ");
scanf("%s", nameList[i]);
printf("enter average : ");
scanf("%f", &averageList[i]);
}
‫ آرایهای از رشته ها‬11-4
//‫حلقه جستجو به دنبال اطالعات دانشجو‬
while(1) {
printf("enter student id (0 to exit) : ");
scanf("%ld", &searchId);
if (!searchId) break ;
for (i=0 ; i<n; i++)
if (idList[i] == searchId) break;
if (i<n) {
printf("Student specifications : \n");
printf("id=%ld name=%s average=%f\n", idList[i], nameList[i],
averageList[i]);
}
else printf("student not found !\n");
}
}
‫مبانی کامپیوتر و برنامه سازی‬
‫فصل‌دوازدهم‌‪ :‬ساختارها‬
‫مدرس‌‪ :‬رضا‌رمضانی‬
‫‪ 12‬ساختارها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫گاهی مجموعهای از دادههای غیر همنوع داریم که به نحوی با یکدیگر‬
‫دارای ارتباط منطقی هستند‪ ،‬به همین دلیل مایلیم آنها را تحت یک نام‬
‫مشترک ذخیره نماییم تا بتوانیم کل آنها را به صورت یکجا پردازش نماییم‬
‫(مثال آنها را به یک تابع ارسال نماییم)‪.‬‬
‫برای این کار باید از ساختمان داده قدرتمند دیگری به نام ساختار یا‬
‫‪ structure‬استفاده نماییم‪.‬‬
‫ساختارها به برنامهنویس اجازه میدهند نوع دادههای جدیدی را به‬
‫وجود آورد که همانند نوع دادههای اولیه خود زبان (مانند ‪ )int‬میتوانند‬
‫در تعریف متغیرها به کار روند‪.‬‬
‫خواهید دید که این قابلیت (امکان ایجاد نوع دادههای موردنظر) یکی از‬
‫ویژگیهای بسیار قوی زبان ‪ C‬است که نوع پیشرفته تر آن یعنی کالس‬
‫)‪ ،(class‬پایه برنامهنویسی شئ گرا در زبان ‪C++‬است‪.‬‬
‫‪ 12-1‬نحوه تعریف ساختار‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫همانطور که گفته شد‪ ،‬ساختارها مجموعهای از دادههای غیر همنوع‬
‫میباشند که به نحوی با یکدیگر در ارتباط هستند‪.‬‬
‫به این داده ها‪ ،‬عناصر ساختار (یا اعضای ساختار و یا فیلد) گفته‬
‫میشود‪.‬‬
‫هر عضو ساختار‪ ،‬در حقیقت یک متغیر است که در تعریف ساختار باید‬
‫نوع و نام آن تعیین شود‪.‬‬
‫برای تعریف یک ساختار از کلمه کلیدی ‪ struct‬استفاده میشود و شکل‬
‫کلی آن به صورت زیر است‪:‬‬
‫{ >‪struct <struct-name‬‬
‫; >‪<type> <element-name 1‬‬
‫; >‪<type> <element-name 2‬‬
‫نام ساختار‬
‫…‬
‫نوع عنصر‬
‫; >‪<type> <element-name n‬‬
‫;}‬
‫نام عنصر‬
‫‪ 12-1‬نحوه تعریف ساختار‬
‫‪‬‬
‫‪‬‬
‫فرض کنید قصد داریم ساختاری تعریف کنیم که اطالعات یک دانشجو را‬
‫در خود ذخیره نماید‪.‬‬
‫این اطالعات شامل شماره دانشجویی‪ ،‬نام‪ ،‬معدل کل‪ ،‬سن و جنسیت‬
‫دانشجو میباشد‪ .‬تعریف این ساختار در زیر آمده است‪:‬‬
‫{ ‪struct student‬‬
‫;‪long int id‬‬
‫شماره دانشجویی (حداکثر ‪ 8‬رقم) ‪//‬‬
‫; ]‪char name[20‬‬
‫نام دانشجو ‪//‬‬
‫معدل کل‪float average; //‬‬
‫; ‪int age‬‬
‫سن دانشجو ‪//‬‬
‫زن ‪:‬جنسیت دانشجو = '‪ 'F‬مرد = '‪char gender ; // 'M‬‬
‫;}‬
‫‪ 12-1‬نحوه تعریف ساختار‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫همانطور که قبال نیز گفته شد‪ ،‬با تعریف یک ساختار در حقیقت یک نوع‬
‫داده جدید را تعریف میکنید‪.‬‬
‫اکنون میتوانید متغیری را از نوع ‪ student‬تعریف نمایید‪ .‬نحوه انجام این‬
‫کار همانند تعریف متغیرهای عادی است‪ .‬به عنوان مثال‪:‬‬
‫;‪student s‬‬
‫اکنون متغیر ‪ s‬شامل کلیه عناصر تعریف شده در ساختار میباشد و‬
‫زبان ‪ C‬به طور خودکار حافظه الزم برای این ساختار را تخصیص‬
‫میدهد‪.‬‬
‫معموال کامپایلر این فضا را بصورت پشت سرهم تخصیص میدهد‪ .‬به‬
‫عنوان مثال متغیر ‪ s‬به صورت شکل زیر در حافظه ذخیره میگردد‪.‬‬
‫‪2 1‬‬
‫‪4‬‬
‫‪average age‬‬
‫‪20‬‬
‫‪4‬‬
‫‪name‬‬
‫‪id‬‬
‫‪ 12-1‬نحوه تعریف ساختار‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫اکنون سوال این است که چگونه میتوان به اجزای متغیر ‪ s‬دسترسی پیدا کرد؟‬
‫برای این کار از عملگر نقطه (یا ‪ )dot‬استفاده میشود‪ .‬به این صورت که ابتدا نام‬
‫متغیر ساختاری‪ ،‬به دنبال آن یک نقطه و سپس نام عنصر موردنظر ذکر میگردند‪.‬‬
‫به عنوان مثال برای مقداردهی به عناصر متغیر ‪ s‬میتوان به شکل زیر عمل کرد‪:‬‬
‫; ‪s.id = 84122030‬‬
‫;)"‪strcpy(s.name, "reza‬‬
‫; ‪s.average = 17.64‬‬
‫; ‪s.age = 18‬‬
‫; '‪s.gender = 'M‬‬
‫بنابراین میتوان از عناصر یک متغیر ساختاری‪ ،‬دقیقا همانند یک متغیر معمولی‬
‫استفاده کرد‪ .‬مثال میتوان برای دریافت نام و معدل دانشجوی ‪ s‬از دستورات زیر‬
‫استفاده کرد‪:‬‬
‫; )‪gets(s.name‬‬
‫;)‪scanf("%f", &s.average‬‬
‫و یا میتوان سن دانشجوی ‪ s‬را به صورت زیر چاپ کرد‪:‬‬
‫;)‪printf("age = %d", s.age‬‬
‫‪ 12-1‬نحوه تعریف ساختار‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫در پایان به چند نکته مهم در مورد ساختارها توجه کنید‪:‬‬
‫مقدار دهی اولیه به ساختارها امکان پذیر است و تقریبا مشابه مقداردهی اولیه به‬
‫آرایهها میباشد‪ .‬بعنوان نمونه به مثال زیر توجه کنید‪:‬‬
‫;} '‪student s = {83201012, "ali", 16.90, 19, 'M‬‬
‫می توان در هنگام تعریف یک ساختار‪ ،‬به طور همزمان یک یا چند متغیر از نوع آن ساختار‬
‫تعریف کرد‪ .‬به عنوان مثال به نمونه زیر دقت کنید‪:‬‬
‫{ ‪struct rectangle‬‬
‫‪int x,y ; // start coordinates‬‬
‫;‪int length, width‬‬
‫;‪} r1, r2‬‬
‫جالب آن است که اگر فقط نیاز به همین دو متغیر از نوع ‪ rectangle‬دارید و دیگر در‬
‫قسمتهای بعدی برنامه از این ساختار استفاده نمیکنید‪ ،‬میتوانید برای ساختار خود‬
‫نامی تعیین نکنید‪.‬‬
‫{ ‪struct‬‬
‫‪int x,y ; // start coordinates‬‬
‫;‪int length, width‬‬
‫;‪} r1, r2‬‬
‫‪ 12-1‬نحوه تعریف ساختار‬
‫‪‬‬
‫‪‬‬
‫استفاده از عملگر نسبت دهی یا = برای دو متغیر ساختاری مجاز است‪،‬‬
‫البته به شرطی که هر دو از یک نوع ساختار باشند‪ .‬در این حالت محتویات‬
‫هر عنصر از متغیر ساختاری مبدا‪ ،‬به عنصر متناظر در متغیر مقصد منتقل‬
‫میشود‪.‬‬
‫به عنوان مثال ساختار ‪ rectangle‬را در نظر بگیرید‪:‬‬
‫;‪rectangle r1, r2‬‬
‫;‪r1.x = r1.y = 0‬‬
‫;‪r1.length = 10‬‬
‫;‪r1.width = 20‬‬
‫; ‪r2 = r1‬‬
‫چنانچه تعریف ساختار را در خارج کلیه توابع انجام دهید‪ ،‬ساختار به صورت‬
‫سراسری تعریف شده است و در نتیجه در کلیه توابع فایل شناخته شده‬
‫خواهد بود‪.‬‬
‫البته میتوان تعریف ساختار را در داخل یک تابع خاص نیز انجام داد که در این‬
‫صورت ساختار مورد نظر فقط در داخل همان تابع شناخته شده خواهد بود‪.‬‬
‫البته تعریف ساختار در داخل یک تابع متداول نمیباشد‪.‬‬
‫‪ 12-2‬آرایهای از ساختارها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫فرض کنید نیاز به نگاهداری اطالعات تعدادی دانشجو دارید‪ ،‬در این‬
‫صورت میتوانید آرایهای از ساختار دانشجو تعریف نمایید‪.‬‬
‫در حقیقت آرایهای از ساختار بسیار متداول بوده و کاربرد زیادی دارد‪ .‬به‬
‫عنوان مثال‪ ،‬یک بار دیگر ساختار دانشجو را درنظر بگیرید‪:‬‬
‫{ ‪struct student‬‬
‫;‪long int id‬‬
‫; ]‪char name[20‬‬
‫;‪float average‬‬
‫; ‪int age‬‬
‫; ‪char gender‬‬
‫;}‬
‫دستور زیر‪ ،‬متغیر ‪ list‬را به عنوان یک آرایه ‪ 100‬عنصری از نوع ‪student‬‬
‫تعریف مینماید‪:‬‬
‫; ]‪student list[100‬‬
‫‪ 12-2‬آرایهای از ساختارها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫همانند سایر آرایه ها‪ ،‬برای دسترسی به عناصر یک آرایه از ساختارها‬
‫نیز باید از اندیس استفاده نمود‪.‬‬
‫به عنوان مثال ]‪ ،list[0‬نشان دهنده اطالعات مربوط به اولین دانشجو‬
‫میباشد‪.‬‬
‫حال برای دسترسی به نام مربوط به اولین دانشجو‪ ،‬میتوان از‬
‫‪ list[0].name‬استفاده نمود‪ .‬به عنوان نمونه دستورات زیر اطالعات مربوط‬
‫به دهمین دانشجو را مقداردهی میکند‪:‬‬
‫; ‪list[9].id = 83102012‬‬
‫;)"‪strcpy(list[9].name, "ahmad‬‬
‫;‪list[9].average = 17.34‬‬
‫;‪list[9].age = 21‬‬
‫; '‪list[9].gender = 'M‬‬
‫همان طور که گفته شد استفاده از آرایهای از ساختارها‪ ،‬عملی متداول‬
‫برای ذخیره و بازیابی اطالعات است‪.‬‬
‫‪ 12-3‬ساختارها و توابع‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫یک متغیر ساختاری را میتوان همانند سایر متغیرها به یک تابع ارسال کرد‪.‬‬
‫به عنوان مثال ساختار ‪ rectangle‬از قسمت قبل را در نظر بگیرید‪ ،‬قصد داریم‬
‫تابعی بنویسیم که یک مستطیل را دریافت و محیط آن را بازگرداند‪.‬‬
‫برنامه زیر نحوه تعریف و فراخوانی این تابع را نشان میدهد‪.‬‬
‫>‪#include <stdio.h‬‬
‫{ ‪struct rectangle‬‬
‫; ‪int x,y‬‬
‫;‪int length, width‬‬
‫;}‬
‫{ )‪int rectangleArea(rectangle r‬‬
‫; ) )‪return (2 * (r.width+r.length‬‬
‫}‬
‫‪ 12-3‬ساختارها و توابع‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫نکته مهمی که باید به آن توجه کرد‪ ،‬این است که ارسال ساختارها به‬
‫توابع در حالت عادی توسط مقدار )‪ (call by value‬صورت میپذیرد‪.‬‬
‫از آنجا که هر ساختار‪ ،‬خود از چندین متغیر دیگر تشکیل شده است‪،‬‬
‫این نحوه ارسال باعث ایجاد سربار میگردد؛ چرا که باید ساختار‬
‫ارسالی در پارامتر تابع کپی گردد و این عمل باعث هدر رفتن زمان و‬
‫حافظه میگردد‪.‬‬
‫به همین دلیل معموال ساختارها توسط ارجاع به توابع ارسال‬
‫میگردند‪ .‬حتی در مواردی که قصد تغییر متغیر ساختاری را در تابع‬
‫نداریم نیز آنها را به صورت ارجاع ثابت به تابع ارسال مینماییم‪.‬‬
‫بنابراین بهتر است تابع قبلی را به صورت زیر بازنویسی نماییم‪:‬‬
‫{ )‪int rectangleArea(const rectangle &r‬‬
‫; ) )‪return (2 * (r.width+r.length‬‬
‫}‬
‫‪ 12-3‬ساختارها و توابع‬
‫‪‬‬
‫‪‬‬
‫یک ساختار میتواند به عنوان خروجی یک تابع نیز تعریف گردد‪.‬‬
‫به عنوان مثال فرض کنید قصد داریم تابعی بنویسیم که مشخصات یک مستطیل را از کاربر دریافت و‬
‫آن را باز گرداند‪ .‬این تابع‪ ،‬به همراه نحوه فراخوانی آن در برنامه زیر نشان داده شده است‪.‬‬
‫>‪#include <stdio.h‬‬
‫{ ‪struct rectangle‬‬
‫; ‪int x,y‬‬
‫;‪int length, width‬‬
‫;}‬
‫{ )(‪rectangle getRectangle‬‬
‫;‪rectangle r‬‬
‫;)" ‪printf("enter coordinates of rectangle (x,y) :‬‬
‫;)‪scanf("%d %d", &r.x, &r.y‬‬
‫;)" ‪printf("enter length :‬‬
‫;)‪scanf("%d", &r.length‬‬
‫;)" ‪printf("enter width :‬‬
‫;)‪scanf("%d", &r.width‬‬
‫;)‪return (r‬‬
‫}‬
‫ ساختارها و توابع‬12-3
void main() {
rectangle rect1;
rect1 = getRectangle() ;
printf("rectangle 1 : x=%d y=%d length=%d width=%d\n",
rect1.x, rect1.y, rect1.length,rect1.width) ;
}
enter coordinates of rectangle (x,y) : 5 5
enter length : 20
enter width : 8
rectangle 1 : x=5 y=5 length=20 width=8
‫ به‬،‫ به این صورت که مستطیل بازگشتی‬،‫می توان این تابع را به شکل دیگری نیز نوشت‬
‫ باید تعریف تابع را به‬،‫ برای این کار‬.‫عنوان یک پارامتر ارجاعی (خروجی) به تابع ارسال شود‬
:‫صورت زیر تغییر داد‬
void getRectangle)rectangle &r( } …

‫‪ 12-4‬ساختارهای تودرتو‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫ساختارهایی که تاکنون طراحی کرده ایم‪ ،‬حاوی نوع دادههای سادهای مانند‬
‫عدد صحیح‪ ،‬عدد اعشاری و یا رشته بوده اند‪.‬‬
‫اما هر عضو ساختار میتواند یک آرایه و یا حتی یک ساختار دیگر نیز باشد‪.‬‬
‫هنگامی که یک ساختار‪ ،‬دارای عضوی از نوع یک ساختار دیگر باشد‪ ،‬به آن‬
‫ساختار تودرتو یا متداخل میگوییم‪ .‬به مثال زیر توجه کنید‪.‬‬
‫مثال ‪ )7-1‬ساختاری برای نگهداری اطالعات دانشجویان طراحی کنید که شامل‬
‫اطالعات زیر باشد‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫شماره دانشجویی‬
‫نام‬
‫شماره ترم جاری‬
‫تعداد دروس ترم جاری‬
‫مشخصات دروس ترم جاری (نام درس‪ ،‬تعداد واحد درس‪ ،‬نمره دانشجو)‬
‫معدل کل‬
‫لیست معدل ترمهای گذشته‬
‫تاریخ تولد به طور کامل (روز‪ ،‬ماه‪ ،‬سال)‬
‫ ساختارهای تودرتو‬12-4
const int maxCourse = 10;
const int maxTerm = 12;
// ‫حداکثر دروس دانشجو در یک ترم‬
// ‫حداکثر ترمهای تحصیلی دانشجو‬
struct date {
int day, month, year ;
};
struct course {
char name[30] ;
int unit;
float grade;
};
// ‫نام درس‬
// ‫تعداد واحد‬
// ‫نمره درس‬
struct student {
long int id ;
// ‫شماره دانشجویی‬
char name[20] ;
// ‫نام دانشجو‬
int termNo;
// ‫شماره ترم جاری‬
int courseNo ;
// ‫تعداد دروس ترم جاری‬
course courseList[maxCourse] ;
// ‫لیست دروس ترم جاری‬
float gpa ;
// ‫معدل کل‬
float averageList[maxTerm] ;
// ‫لیست معدل ترمهای گذشته‬
date birthDate ;
// ‫تاریخ تولد‬
};
student s ;
‫‪ 12-4‬ساختارهای تودرتو‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫اولین مورد‪ ،‬یک آرایه از اعداد اعشاری به نام ‪ averageList‬میباشد که‬
‫لیست معدل ترمهای گذشته دانشجو را نگاه میدارد‪.‬‬
‫به عنوان مثال چنانچه بخواهیم معدل ترم اول دانشجوی ‪ s‬را مقداردهی‬
‫کنیم‪ ،‬میتوانیم از دستور زیر استفاده نماییم‪:‬‬
‫; ‪s.averageList[0] = 18.2‬‬
‫مورد بعدی‪ ،‬متغیر ‪ birthDate‬است که تاریخ تولد دانشجو را نگاه میدارد‬
‫و خود از نوع ساختار ‪ date‬میباشد‪.‬‬
‫دستورات زیر تاریخ تولد دانشجوی ‪ s‬را برابر ‪ 1/12/1360‬قرار میدهند ‪:‬‬
‫; ‪s.birthDate.day = 1‬‬
‫; ‪s.birthDate.month = 12‬‬
‫; ‪s.birthDate.year = 1360‬‬
‫همان طور که میبینید برای دسترسی به عناصر ساختار متداخل ‪،date‬‬
‫از دو عالمت نقطه استفاده شده است‪.‬‬
‫‪ 12-4‬ساختارهای تودرتو‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫آخرین مورد‪ ،‬متغیر ‪ courseList‬است که لیست دروس ترم جاری دانشجو را نگاه‬
‫میدارد‪.‬‬
‫این متغیر خود آرایهای از یک ساختار دیگر به نام ‪ course‬است‪ .‬دستورات زیر‪،‬‬
‫اولین درس دانشجوی ‪ s‬را مقدار دهی مینمایند‪:‬‬
‫;)"‪strcpy(s.courseList[0].name, "Computer Programming‬‬
‫;‪s.courseList[0].unit = 4‬‬
‫; ‪s.courseList[0].grade = 18.5‬‬
‫مقداردهی به سایر دروس دانشجوی ‪ s‬نیز همانند دستورات فوق است‪ ،‬با این‬
‫تفاوت که باید از اندیس مناسب برای آرایه ‪ courseList‬استفاده شود‪.‬‬
‫البته ساختارها میتوانند پیچیده تر نیز باشند‪ .‬به عنوان مثال میتوان ساختاری‬
‫برای یک گروه آموزشی تعریف کرد که شامل نام گروه‪ ،‬نام مدیر گروه و لیست‬
‫دانشجویان‪ ،‬که آرایهای از ساختار دانشجو است‪ ،‬باشد‪ .‬به همین ترتیب‬
‫میتوان ساختار دانشکده و دانشگاه را نیز طراحی کرد‪ .‬البته تعریف‬
‫ساختارهای پیچیدهای که دارای چندین ساختار تودرتو باشند‪ ،‬باعث میشود‬
‫که استفاده از آنها نیز مشکل گردد‪.‬‬
‫‪ 12-5‬چند جمله ایها‬
‫‪‬‬
‫‪‬‬
‫به دلیل اهمیت ساختارها‪ ،‬در این قسمت یک مثال جامع برای نشان دادن نحوه‬
‫طراحی و کار با آنها ارائه میگردد‪.‬‬
‫این مثال‪ ،‬نحوه پیاده سازی چند جمله ایها را نشان می دهد‪ .‬حتما از دروس ریاضی‬
‫به یاد دارید که یک چند جمله ای به صورت زیر تعریف می گردد‪:‬‬
‫‪A( x)  an x n  an1 x n1  ... a2 x 2  a1 x  a0‬‬
‫‪‬‬
‫به عنوان مثال به چند جمله ای زیر توجه کنید‪:‬‬
‫‪A( x)  7 x 8  4 x 5  8 x 4  3 x  9‬‬
‫‪‬‬
‫و اما اعمال مختلفی همچون جمع دو چند جمله ای‪ ،‬ضرب دو چند جمله ای‪ ،‬ضرب‬
‫یک جمله در چند جمله ای و ‪ ...‬نیز برروی چند جمله ایها تعریف شده است‪ ،‬که‬
‫تعاریف آنها را در کتابهای ریاضی خوانده اید‪.‬‬
‫‪ 12-5‬چند جمله ایها‬
‫‪‬‬
‫اکنون فرض کنید از ما خواسته شده است یک مجموعه از ساختارها و‬
‫توابع برای کار با چند جمله ایها طراحی نماییم‪ .‬موارد خواسته شده‬
‫عبارتند از‪:‬‬
‫‪ ‬نوع داده چند جمله ای به عنوان یک ساختار‬
‫‪ ‬یک مجموعه از توابع برای‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫اضافه کردن یک جمله به چند جمله ای‬
‫حذف یک جمله از چند جمله ای‬
‫دریافت یک چند جمله ای از کاربر‬
‫چاپ یک چند جمله ای‬
‫جمع دو چند جمله ای‬
‫ضرب دو چند جمله ای‬
‫ضرب یک جمله در چند جمله ای‬
‫محاسبه ضریب یک توان خاص از چند جمله ای‬
‫محاسبه درجه یک چند جمله ای‬
‫تعیین مقدار چند جمله ای به ازای یک مقدار مشخص برای ‪x‬‬
‫‪ 12-5‬چند جمله ایها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫قبل از هر چیز‪ ،‬باید ساختاری برای نگهداری یک چند جمله ای طراحی نماییم‪.‬‬
‫همان طور که می بینید‪ ،‬هر چند جمله ای از تعدادی جمله مانند ‪5x3‬تشکیل شده است‪.‬‬
‫هر جمله شامل دو قسمت است‪ :‬ضریب و توان جمله (متغیر ‪ x‬مهم نیست‪ ،‬چرا که می توان‬
‫از هر حرف دیگری مانند ‪ y‬نیز استفاده کرد)‪.‬‬
‫بنابراین می توان هر چند جمله ای را به صورت آرایه ای از جملهها پیاده سازی کرد‪ ،‬که هر‬
‫جمله شامل یک ضریب و یک توان است‪.‬‬
‫این پیاده سازی در زیر نشان داده شده است‪.‬‬
‫حداکثر تعداد جمالت در یک چندجمله ای ‪//‬‬
‫;‪const int maxTerm = 100‬‬
‫ضریب جمله ‪//‬‬
‫توان جمله ‪//‬‬
‫آرایه ذخیره سازی جمالت ‪//‬‬
‫تعداد جمالت چند جمله ای ‪//‬‬
‫{ ‪struct term‬‬
‫; ‪float coef‬‬
‫; ‪int exp‬‬
‫;}‬
‫{ ‪struct polynomial‬‬
‫; ]‪term terms[maxTerm‬‬
‫; ‪int termNo‬‬
‫;}‬
‫‪ 12-5‬چند جمله ایها‬
‫‪‬‬
‫‪‬‬
‫اکنون می توان متغیری از نوع چند جمله ای را به صورت زیر تعریف کرد‪:‬‬
‫;‪polynomial A‬‬
‫در شکل زیر‪ ،‬نحوه ذخیره سازی یک چند جمله ای در متغیر ‪ A‬نشان داده شده‬
‫است‪.‬‬
‫‪A( x)  7 x 8  4 x 5  8 x 4  3 x  9‬‬
‫…‬
‫…‬
‫‪4 8 3 9‬‬
‫‪5 4 1 0‬‬
‫‪coef = 7‬‬
‫‪exp = 8‬‬
‫= ‪terms‬‬
‫‪termNo = 5‬‬
‫‪A‬‬
‫ چند جمله ایها‬12-5
‫ تابع‬،‫ اولین تابعی که بررسی می نماییم‬.‫اکنون به پیاده سازی توابع می پردازیم‬
‫ و جمله را‬،‫ است که یک چند جمله ای و یک جمله را به عنوان ورودی دریافت‬insertTerm
.‫در مکان مناسب چند جمله ای (بر حسب توان) درج می نماید‬
int insertTerm(polynomial &A, term newTerm) {
int i;
if (A.termNo == maxTerm) return(1);
if ( searchExponent(A, newTerm.exp) != -1) return(2) ;
for (i=A.termNo-1; i>=0; i--)
if (newTerm.exp > A.terms[i].exp)
A.terms[i+1] = A.terms[i] ;
else break ;
A.terms[i+1] = newTerm ;
A.termNo ++;
return(0) ;
}

‫‪ 12-5‬چند جمله ایها‬
‫‪‬‬
‫قبل از اینکه به سراغ تابع بعدی برویم‪ ،‬ابتدا تابع ‪ searchExponent‬را که جزو‬
‫توابع استفاده شده بود‪ ،‬پیاده سازی می نماییم‪.‬‬
‫{ )‪int searchExponent(const polynomial &A, int searchExp‬‬
‫;‪int i‬‬
‫)‪for (i=0; i<A.termNo; i++‬‬
‫; )‪if (A.terms[i].exp == searchExp) return(i‬‬
‫;)‪return(-1‬‬
‫}‬
‫‪‬‬
‫اگرچه به دلیل مرتب بودن چندجمله ای برحسب توان‪ ،‬جستجو می تواند به صورت‬
‫دودویی نیز انجام پذیرد‪ ،‬اما از آنجا که معموال تعداد جمالت بسیار کم است‪ ،‬از‬
‫جستجوی خطی استفاده شده است ‪.‬‬
‫‪ 12-5‬چند جمله ایها‬
‫‪‬‬
‫‪‬‬
‫تابع بعدی‪ ،‬تابع ‪ deleteTerm‬است که یک چند جمله ای و یک توان را دریافت‪ ،‬و در‬
‫صورت وجود توان موردنظر در چندجمله ای‪ ،‬جمله مربوط به آن را حذف می نماید‪.‬‬
‫از آنجا که جمله های چندجمله ای برحسب توان مرتب هستند‪ ،‬حذف یک جمله‬
‫نیاز به شیفت دادن جمله های پس از آن دارد‪ .‬این تابع درصورت موفقیت در حذف‬
‫مقدار ‪ ،1‬و در صورت عدم موفقیت ‪ 0‬باز می گرداند‪.‬‬
‫{ )‪int deleteTerm(polynomial &A, int deleteExp‬‬
‫;‪int i, position‬‬
‫;)‪position = searchExponent(A, deleteExp‬‬
‫; )‪if (position == -1) return(0‬‬
‫)‪for (i=position; i<A.termNo -1; i++‬‬
‫; ]‪A.terms[i] = A.terms[i+1‬‬
‫;‪A.termNo --‬‬
‫; )‪return(1‬‬
‫}‬
‫‪ 12-5‬چند جمله ایها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫اکنون به تابع ‪ getPolynomial‬میپردازیم که یک چند جمله ای را از کاربر دریافت کرده و‬
‫به عنوان خروجی باز میگرداند‪.‬‬
‫این تابع را می توان به دو صورت نوشت‪ .‬در حالت اول چندجمله ای دریافتی به صورت‬
‫خروجی تابع بازگردانده می شود‪ .‬یعنی تعریف تابع به صورت زیر است‪:‬‬
‫… { )(‪polynomial getPolynomial‬‬
‫و اما در حالت دوم‪ ،‬چند جمله ای به صورت یک پارامتر ارجاعی به تابع ارسال می شود‪،‬‬
‫و سپس در داخل تابع مقادیر آن از کاربر دریافت می گردد‪ .‬در این حالت تعریف تابع به‬
‫صورت زیر خواهد بود‪:‬‬
‫… { )‪void getPolynomial(polynomial &A‬‬
‫تفاوت این دو در نحوه فرا خوانی است‪ .‬فرض کنید قصد داریم چند جمله ای به نام ‪ p‬را از‬
‫کاربر دریافت کنیم‪ .‬دستورات زیر نحوه انجام این کار در هر دو حالت را نشان می دهند‪:‬‬
‫;‪polynomial p‬‬
‫حالت اول ‪p = getPolynomial() ; //‬‬
‫;)‪getPolynomial(p‬‬
‫حالت دوم ‪//‬‬
‫‪‬‬
‫البته حالت دوم از لحاظ فضا و زمان بهینه تر است‪ .‬چرا که در حالت اول‪ ،‬پس از بازگشت‬
‫از تابع‪ ،‬باید مقدار بازگشتی در داخل متغیر ‪ p‬کپی گردد‪ .‬در حالیکه در حالت دوم خود‬
‫متغیر ‪ p‬به تابع ارسال و در آنجا مقدار می گیرد‪.‬‬
‫ چند جمله ایها‬12-5
void getPolynomial(polynomial &A) {
int i, n;
term newTerm;
printf("enter number of terms : " );
scanf("%d", &n) ;
if (n > maxTerm) {
printf("sorry! we can save only %d terms.\n", maxTerm);
n = maxTerm ;
}
A.termNo = 0;
for (i=0; i<n; i++) {
printf("enter coefficient and exponent : ");
scanf("%f %d", &newTerm.coef, &newTerm.exp) ;
if ( insertTerm(A, newTerm) == 2 ) {
printf("repeated exponent! try again.\n");
i --;
}
}
}
‫‪ 12-5‬چند جمله ایها‬
‫‪‬‬
‫‪‬‬
‫تابع بعدی‪ printPolynomial ،‬است که یک چند جمله ای را دریافت و آن‬
‫را چاپ می کند‪.‬‬
‫در این تابع برای نشان دادن توان از عالمت ^ استفاده شده است‪.‬‬
‫عالمت ‪ +‬در مشخصه تبدیل ‪ %+7.2f‬باعث میشود که عالمت عدد‬
‫(مثبت یا منفی) نیز چاپ گردد‪.‬‬
‫{ )‪void printPolynomial(const polynomial &A‬‬
‫;‪int i‬‬
‫)‪for (i=0; i<A.termNo; i++‬‬
‫; )‪printf("%+7.2f x^%d",A.terms[i].coef,A.terms[i].exp‬‬
‫}‬
‫‪ 12-5‬چند جمله ایها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫اکنون نوبت به یکی از مهمترین توابع برنامه‪ ،‬یعنی تابع جمع دو چند جمله ای‬
‫رسیده است‪ .‬همان طور که می دانید در ریاضیات برای جمع دو چند جمله ای )‪A(x‬‬
‫و )‪ B(x‬به صورت زیر عمل می شود‪:‬‬
‫برای جمله های مشترک (جمله هایی که توانهای مشابه دارند) ضرایب دو جمله با‬
‫یکدیگر جمع شده و ضریب حاصل با همان توان مشترک به چند جمله ای حاصلجمع‬
‫اضافه می شود‪.‬‬
‫جمله های غیر مشترک‪ ،‬عینا به چند جمله ای حاصلجمع اضافه می شوند‪.‬‬
‫به مثال زیر توجه کنید‪:‬‬
‫‪A( x)  7 x 8  4 x 6  8 x 4  3 x  9‬‬
‫‪B( x)  8 x 5  2 x 4  2 x 2  7 x‬‬
‫‪C ( x)  A( x)  B( x)  7 x 8  4 x 6  8 x 5  10 x 4  2 x 2  10 x  9‬‬
‫‪ 12-5‬چند جمله ایها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫برای پیاده سازی تابع جمع‪ ،‬باید جمله های مشترک بین دو چند جمله ای که‬
‫دارای توان یکسان هستند را پیدا کنیم‪.‬‬
‫اما خوشبختانه از آنجا که چندجمله ایها برحسب توان مرتب هستند‪ ،‬نیازی به‬
‫جستجوی هر جمله از یک چندجمله ای در دیگری نیست‪ .‬راه ساده تر آن است‬
‫که مشابه عمل ادغام دو لیست مرتب عمل نماییم (به فصل آرایهها مراجعه‬
‫نمایید)‪.‬‬
‫طرح کلی روش به صورت زیر است‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫یک شمارنده برای آرایه ‪ terms‬هریک از دو چند جمله ای در نظر بگیرید و آنها را برابر‬
‫صفر قرار دهید‪.‬‬
‫در هر مرحله‪ ،‬توان دو جمله ای را که شمارندهها برروی آنها قرار دارند‪ ،‬با یکدیگر‬
‫مقایسه نمایید‪ .‬در صورت مساوی بودن توانها‪ ،‬ضرایب را با هم جمع و حاصل را به‬
‫چند جمله ای جواب اضافه کنید‪ ،‬سپس هر دو شمارنده را یک واحد افزایش دهید؛‬
‫در غیر اینصورت جمله ای را که توان بزرگتری دارد به چندجمله ای جواب اضافه کرده‬
‫و شمارنده مربوط به آن را یک واحد افزایش دهید‪ .‬این عملیات را تا پایان یافتن یکی‬
‫از دو چند جمله ای تکرار کنید‪.‬‬
‫در پایان‪ ،‬تمام عناصر باقیمانده از چندجمله ای ناتمام را به ترتیب به چندجمله ای‬
‫جواب منتقل نمایید‪.‬‬
‫‪ 12-5‬چند جمله ایها‬
‫…‬
‫…‬
‫‪C‬‬
‫‪7 4 8 10 2 10 9‬‬
‫‪8 6 5 4 2 1 0‬‬
‫‪B‬‬
‫… ‪7‬‬
‫… ‪1‬‬
‫‪8 2 2‬‬
‫‪5 4 2‬‬
‫‪A‬‬
‫… ‪7 4 8 3 9‬‬
‫… ‪8 6 4 1 0‬‬
‫ چند جمله ایها‬12-5
polynomial addPolynomial(const polynomial &A, const polynomial &B) {
polynomial C ; // ‫چندجمله ای حاصلجمع‬
int i, j, k;
float newCoef;
i = j = k = 0;
while (i < A.termNo && j < B.termNo) {
if (A.terms[i].exp > B.terms[j].exp) {
C.terms[k] = A.terms[i] ;
i++; k++;
}
else if (A.terms[i].exp < B.terms[j].exp) {
C.terms[k] = B.terms[j] ;
j ++; k ++;
}
else {
newCoef = A.terms[i].coef + B.terms[j].coef ;
if (newCoef != 0) {
C.terms[k].coef = newCoef ;
C.terms[k].exp = A.terms[i].exp;
k ++;
}
i ++; j ++;
}
} //end while
for (; i<A.termNo; i++, k++)
C.terms[k] = A.terms[i] ;
for (; j<B.termNo; j++, k++)
C.terms[k] = B.terms[j] ;
C.termNo = k;
return(C);
}
‫‪ 12-5‬چند جمله ایها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫نوشتن تابع ضرب کمی مشکلتر است‪.‬‬
‫برای ضرب دو چندجمله ای در یکدیگر‪ ،‬باید هریک از جمله های چند جمله ای اول‪،‬‬
‫در هر یک از جمله های چند جمله ای دوم ضرب شود‪ .‬برای ضرب دو جمله نیز‪ ،‬ابتدا‬
‫ضرایب آن دو را در هم ضرب و سپس توانهای آنها را با هم جمع می کنیم‪.‬‬
‫به مثال زیر دقت کنید‪:‬‬
‫‪A( x)  4 x 6  2 x 4  3 x‬‬
‫‪B( x)  8 x 5  2 x 2  7 x‬‬
‫‪C ( x)  A( x)  B( x)  32 x11  16 x 9  8 x 8  28 x 7  28 x 6  14 x 5  6 x 3  21x 2‬‬
‫ چند جمله ایها‬12-5
polynomial multiplyPolynomial(const polynomial &A, const polynomial &B) {
polynomial C;
//‫چندجملهای حاصلضرب‬
term multTerm ;
int i, j, position;
C.termNo = 0;
for (i=0; i<A.termNo; i++)
for (j=0; j<B.termNo; j++) {
//‫محاسبه حاصلضرب دو جمله‬
multTerm.coef = A.terms[i].coef * B.terms[j].coef ;
multTerm.exp = A.terms[i].exp + B.terms[j].exp ;
//‫جستجوی توان جمله جدید در چندجملهای حاصلضرب‬
position = searchExponent(C, multTerm.exp) ;
if (position == -1)
//‫جمله جدید در چندجملهای حاصلضرب وجود ندارد‬
insertTerm(C, multTerm);
else {
//‫جمله جدید در چندجملهای حاصلضرب وجود دارد‬
C.terms[position].coef += multTerm.coef ;
if (C.terms[position].coef == 0)
deleteTerm(C, C.terms[position].exp) ;
}
}
return(C);
}
‫ چند جمله ایها‬12-5
‫برای ضرب یک جمله در یک چند جمله ای نیز می توان به صورت باال‬
‫ با این تفاوت که تنها یک جمله باید در چند جمله ای ضرب‬،‫عمل کرد‬
.‫ بنابراین ترتیب جملهها به هم نخواهد خورد‬،‫شود‬
polynomial multiplyTerm(const polynomial &A, term T) {
polynomial B ;
int i;
for (i=0; i<A.termNo; i++) {
B.terms[i].coef = A.terms[i].coef * T.coef ;
B.terms[i].exp = A.terms[i].exp + T.exp ;
}
B.termNo = A.termNo ;
return (B);
}

‫‪ 12-5‬چند جمله ایها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫تابع بعدی ‪ getCoefficient‬نام دارد‪.‬‬
‫این تابع‪ ،‬یک چندجمله ای و یک توان را دریافت‪ ،‬و ضریب مربوط به آن‬
‫توان در چند جمله ای را باز می گرداند‪.‬‬
‫به یاد داشته باشید که چنانچه یک توان در چند جملهای وجود نداشته‬
‫باشد‪ ،‬ضریب مربوط به آن برابر صفر است‪.‬‬
‫{ )‪float getCoefficient(const polynomial &A, int exponent‬‬
‫;‪int position‬‬
‫; )‪position = searchExponent(A, exponent‬‬
‫;)‪if (position == -1) return(0‬‬
‫; )‪else return(A.terms[position].coef‬‬
‫}‬
‫‪ 12-5‬چند جمله ایها‬
‫‪‬‬
‫‪‬‬
‫تابع بعدی‪ polynomialDegree ،‬نام داشته و درجه چند جمله ای را باز می‬
‫گرداند‪.‬‬
‫از آنجا که چند جمله ای بر حسب توان مرتب است‪ ،‬این تابع بسیار ساده‬
‫بوده و تنها توان اولین جمله را باز می گرداند‪.‬‬
‫{ )‪int polynomialDegree(const polynomial &A‬‬
‫;)‪if (A.termNo == 0) return(0‬‬
‫;)‪else return(A.terms[0].exp‬‬
‫}‬
‫ چند جمله ایها‬12-5
‫ است که مقدار چند جمله ای را به ازای یک مقدار‬evaluatePolynomial ،‫و آخرین تابع‬
.‫ محاسبه می کند‬x ‫مشخص برای‬
float evaluatePolynomial(const polynomial &A, float x) {
float result = 0;
int i;
for (i=0; i<A.termNo; i++)
result += A.terms[i].coef * power(x, A.terms[i].exp) ;
return(result) ;
}
float power(float base, int exp) {
int i;
float result = 1;
for (i=0; i<exp; i++)
result *= base ;
return(result) ;
}

‫‪ 12-5‬چند جمله ایها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫حال که کلیه ساختارها و توابع مورد نیاز را تعریف کردیم‪ ،‬می‬
‫توانیم آنها را در داخل یک فایل جداگانه مانند ‪poly.h‬قرار داده و‬
‫سپس در برنامه هایی که نیاز به کار با چندجمله ایها دارند‪ ،‬از‬
‫این فایل استفاده نماییم‪.‬‬
‫برای استفاده از این فایل‪ ،‬ابتدا باید با استفاده از دستور‬
‫‪ #include‬آن را در برنامه موردنظر گنجانید‪.‬‬
‫در این قسمت فقط تعریف توابع و نحوه کار آنها تشریح گردید‪،‬‬
‫نوشتن برنامه ای که از این توابع استفاده نماید را به عنوان‬
‫تمرین به خود شما واگذار می نماییم‪.‬‬
‫‪ 7-6‬اتحادها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫اتحاد یا ‪ ،union‬محلی از حافظه است که توسط دو یا چند متغیر مختلف‬
‫به طور مشترک استفاده می شود‪.‬‬
‫کاربرد اتحادها در مواردی است که می خواهید از یک مکان حافظه‪،‬‬
‫تحت شرایط مختلف‪ ،‬با نوعهای متفاوتی استفاده نمایید‪.‬‬
‫به عنوان مثال فرض کنید در یک کارخانه برای یک قطعه تولیدی دانستن‬
‫نوع قطعه (یک کاراکتر)‪ ،‬یا وزن قطعه (عدد اعشاری) و یا شماره رنگ آن‬
‫(عدد صحیح) کافی است‪ ،‬و با داشتن یکی از این سه مورد می توان‬
‫قطعه را شناسایی کرد‪ .‬اکنون قصد داریم متغیری تعریف کنیم که بتواند‬
‫هریک از این سه داده را نگهداری کند‪.‬‬
‫مسلم است که تعریف این متغیر به صورت ساختار باعث اتالف حافظه‬
‫می گردد‪ ،‬چرا که فضای الزم برای هر سه داده ایجاد خواهد شد‪ .‬راه‬
‫حل درست‪ ،‬استفاده از یک اتحاد است‪.‬‬
‫‪ 7-6‬اتحادها‬
‫‪‬‬
‫‪‬‬
‫تعریف اتحادها تقریبا همانند ساختارها صورت می پذیرد‪ ،‬با این تفاوت‬
‫که به جای کلمه کلیدی ‪ ،struct‬از کلمه ‪ union‬استفاده می شود‪.‬‬
‫به عنوان مثال می توان برای قطعه تولیدی موردنظر‪ ،‬اتحاد زیر را تعریف‬
‫کرد ‪:‬‬
‫{ ‪union product‬‬
‫; ‪char kind‬‬
‫; ‪int color‬‬
‫; ‪float weight‬‬
‫;}‬
‫; ‪product p‬‬
‫‪ 7-6‬اتحادها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫برای دسترسی به عناصر متغیر ‪ ،p‬می توان از همان عالمت نقطه استفاده‬
‫کرد‪.‬‬
‫اما نکته مهم آن است که در هر لحظه فقط از یک عضو ‪ p‬می توان استفاده‬
‫نمود‪.‬‬
‫دلیل این امر آن است که هر سه متغیر ‪ color ،kind‬و ‪ weight‬از محل حافظه‬
‫مشترکی استفاده می نمایند‪ ،‬در نتیجه نوشتن بر روی یکی از آنها‪ ،‬مقادیر‬
‫سایرین را از بین خواهد برد‪.‬‬
‫در حقیقت هنگامی که متغیری از نوع یک اتحاد تعریف می کنید‪ ،‬کامپایلر‬
‫فضای حافظه ای به اندازه بزرگترین نوع موجود در اتحاد را به آن تخصیص می‬
‫دهد‪.‬‬
‫به عنوان مثال‪ ،‬نحوه ذخیره متغیر ‪ p‬در حافظه در شکل زیر نشان داده شده‬
‫است‪.‬‬
‫‪color‬‬
‫‪byte 3‬‬
‫‪byte 2‬‬
‫‪byte 1‬‬
‫‪byte 0‬‬
‫‪kind‬‬
‫‪weight‬‬
‫‪p‬‬
‫ اتحادها‬7-6
.‫ به برنامه زیر و خروجیهای آن توجه نمایید‬،‫برای آشنایی بیشتر با اتحادها‬
#include <stdio.h>
union product {
char kind ;
int color ;
float weight ;
};
void main() {
product p;
p.weight = 4200.500 ;
printf("p.weight = %f \n\n", p.weight);
p.color = 10;
printf("p.color = %d \n", p.color);
printf("now p.weight = %f \n\n", p.weight);
p.kind = 'M';
printf("p.kind = %c \n", p.kind);
printf("now p.color = %d and p.weight = %f \n\n", p.color, p.weight);
}

‫‪ 7-6‬اتحادها‬
‫‪p.weight = 4200.500000‬‬
‫‪p.color = 10‬‬
‫‪now p.weight = 4192.004883‬‬
‫‪p.kind = M‬‬
‫‪now p.color = 77 and p.weight = 4192.037598‬‬
‫‪‬‬
‫‪‬‬
‫همان گونه که می بینید‪ ،‬با تغییر هر عضو از متغیر ‪ ،p‬مقدار اعضای دیگر آن‬
‫نیز تغییر می کند که دلیل آن استفاده مشترک از یک مکان حافظه است‪.‬‬
‫بار دیگر یادآوری می شود که مسئولیت استفاده درست از یک متغیر اتحاد‬
‫به عهده برنامهنویس است‪ ،‬و خود وی باید از عضو مناسب متغیر استفاده‬
‫نماید‪.‬‬
‫مبانی کامپیوتر و برنامه سازی‬
‫فصل‌سیزدهم‌‪ :‬فایلها‬
‫مدرس‌‪ :‬رضا‌رمضانی‬
‫‪ 13‬فایلها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫همانگونه که میدانید در زبانهای برنامه سازی از جمله ‪ ،C‬کلیه متغیرها در‬
‫حافظه اصلی )‪ (RAM‬ذخیره میگردند‪.‬‬
‫اگرچه حافظه اصلی دارای سرعت بسیار باالیی است‪ ،‬اما با پایان یافتن اجرای‬
‫برنامه اطالعات آن از بین میروند‪.‬‬
‫با این وجود در بسیاری از برنامه ها‪ ،‬باید اطالعات برای اجرای بعدی برنامه‬
‫حفظ شوند‪ .‬به عنوان مثال در یک سیستم دانشجویی‪ ،‬باید اطالعات‬
‫دانشجویان و دروس آنها بر روی یک حافظه پایدار ذخیره گردد‪ ،‬تا با اتمام برنامه‬
‫اطالعات آنها از بین نرود‪.‬‬
‫راه حل این مشکالت استفاده از حافظه پایداری به نام حافظه جانبی است‪.‬‬
‫اگرچه حافظههای جانبی سرعت بسیار کمتری نسبت به حافظه اصلی دارند‪،‬‬
‫اما قادرند اطالعات خود را حتی پس از قطع جریان برق نیز حفظ نمایند‪.‬‬
‫انواع مختلفی از حافظههای جانبی وجود دارد‪ ،‬مانند‪ :‬دیسک سخت و نرم‪،‬‬
‫نوار‪ ،‬دیسکهای نوری )‪ (CD‬و اخیرا نیز ‪ Flash Disk‬ها‪.‬‬
‫واحد ذخیره سازی اطالعات بر روی حافظههای جانبی فایل است‪ .‬فایل‬
‫مجموعهای از دادهها است که به نحوی با یکدیگر در ارتباط هستند‪.‬‬
‫برای ذخیره اطالعات یک برنامه‪ ،‬کافی است یک فایل بر روی حافظه جانبی‬
‫ایجاد کرده و دادهها را در آن ذخیره نماییم‪ .‬در این فصل با نحوه انجام این کار‬
‫آشنا خواهیم شد‪.‬‬
‫‪ 13-1‬انواع فایلها‬
‫‪‬‬
‫فایلها به دو دسته اصلی تقسیم میگردند‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫فایلهای متنی )‪(text‬‬
‫فایلهای دودویی )‪(binary‬‬
‫درک تفاوت بین این دو بسیار مهم است‪ .‬برای روشن شدن موضوع‪ ،‬به یک‬
‫مثال ساده توجه کنید‪ .‬فرض کنید یک متغیر صحیح به صورت زیر تعریف کرده‬
‫ایم‪:‬‬
‫;‪int a = 9281‬‬
‫همانگونه که میدانید این متغیر در حافظه به صورت یک عدد دودویی ذخیره‬
‫میگردد‪.‬‬
‫اگر برنامه در محیط ‪DOS‬باشد‪ ،‬برای این متغیر دو بایت حافظه به صورت شکل‬
‫زیر در نظر گرفته شده و عدد ‪ 9281‬به صورت دودویی در آن ذخیره میگردد‪.‬‬
‫‪a 00100100 01000001‬‬
‫‪ 13-1‬انواع فایلها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫حال فرض کنیم یک فایل دودویی به نام ‪ ،test.dat‬و یک فایل متنی بنام‬
‫‪ test.txt‬ایجاد کرده و متغیر ‪ a‬را در آنها نوشته ایم‪.‬‬
‫در فایل دودویی ‪ ،test.dat‬متغیر ‪ a‬دقیقا به همان شکلی که در حافظه‬
‫قرار دارد‪ ،‬ذخیره میگردد‬
‫‪ ‬البته در هنگام ذخیره در فایل‪ ،‬ابتدا بایت ارزش پایینتر و سپس بایت با‬
‫ارزش باالتر ذخیره میگردد‪.‬‬
‫‪ ‬بنابراین این متغیر دو بایت فضا را در فایل اشغال خواهد کرد‪.‬‬
‫اما در فایل متنی ‪ ،test.txt‬کلیه اطالعات به صورت رشتهای از کاراکترها‬
‫ذخیره میگردند‪.‬‬
‫‪ ‬بنابراین متغیر صحیح ‪ ،a‬ابتدا به رشته "‪ "9281‬تبدیل شده و سپس این‬
‫چهار کاراکتر به ترتیب در فایل نوشته میشوند‪.‬‬
‫‪ ‬در حقیقت در این فایل متنی ‪ ،‬به ترتیب کدهای اسکی مربوط به‬
‫کاراکترهای '‪ '8' ،'2' ،'9‬و سرانجام '‪ '1‬نوشته خواهد شد‪.‬‬
‫‪ ‬درنتیجه این متغیر‪ ،‬چهار بایت فضا را در فایل اشغال خواهد کرد‪.‬‬
‫‪ 13-1‬انواع فایلها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫مزیت فایلهای متنی نسبت به فایلهای دودویی آن است که میتوان آنها را‬
‫توسط هر ویرایشگر متنی (مانند ‪ Edit‬و یا ‪ )Notepad‬باز کرد و محتوای آنها را‬
‫دید‪.‬‬
‫توجه کنید که یک ویرایشگر متنی‪ ،‬فایل را به صورت رشتهای از کاراکترها‬
‫میبیند‪ ،‬و آن را به صورت کاراکتر به کاراکتر میخواند‪.‬‬
‫چنانچه یک فایل دودویی توسط یک ویرایشگر متنی باز شود‪ ،‬اطالعات به شکل‬
‫نادرستی نمایش داده خواهند شد و کاربر فقط مجموعهای از کاراکترهای‬
‫نامفهوم را خواهد دید‪.‬‬
‫به عنوان مثال شکل زیر نحوه نمایش دو فایل ‪ test.dat‬و ‪ test.txt‬را توسط یک‬
‫ویرایشگر متنی نشان میدهد‪.‬‬
‫‪A$‬‬
‫‪test.dat‬‬
‫‪9281‬‬
‫‪test.txt‬‬
‫‪ 13-1‬انواع فایلها‬
‫‪‬‬
‫اگر به نحوه نمایش فایل ‪ test.dat‬دقت کنید‪ ،‬میبینید که این فایل حاوی دو‬
‫کاراکتر (بایت) میباشد‪.‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫کاراکتر ‪ ،A‬دارای کد اسکی ‪ 65‬است که در مبنای دو برابر ‪ 01000001‬میباشد‪.‬‬
‫کاراکتر ‪ $‬نیز دارای کد اسکی ‪ 36‬است که در مبنای دو برابر ‪ 00100100‬خواهد شد‪.‬‬
‫حال اگر این دو عدد را با نحوه ذخیره متغیر ‪ a‬در حافظه مقایسه کنید‪ ،‬متوجه خواهید‬
‫شد که این دو همان بایتهای تشکیل دهنده عدد ‪ 9281‬هستند‪.‬‬
‫به طور کلی‪ ،‬یک فایل دودویی توسط ویرایشگر متنی قابل خواندن نیست و‬
‫فقط توسط خود برنامه میتواند خوانده شده و تفسیر گردد‪.‬‬
‫از آنجا که فایلهای متنی توسط هر ویرایشگری قابل خواندن هستند‪ ،‬معموال از‬
‫آنها برای ذخیره گزارش و یا اطالعاتی که باید توسط کاربر قابل مشاهده و چاپ‬
‫باشد‪ ،‬استفاده میگردد‪.‬‬
‫به فایلهای متنی‪ ،‬فایلهای قالب بندی شده نیز گفته میشود‪ ،‬چرا که‬
‫برنامهنویس میتواند دادهها را در یک قالب دلخواه و با شکل مناسب برای کاربر‬
‫در فایل قرار دهد‪.‬‬
‫مثال در صورت لزوم میتوان دادهها را در داخل یک جدول با عنوانهای مناسب‬
‫قرار داد‪ ،‬تا کاربر راحت تر بتواند از آنها استفاده نماید‪.‬‬
‫‪ 13-1‬انواع فایلها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫اما در مقابل‪ ،‬فایلهای دودویی دنبالهای از بایتهای خام هستند که فقط‬
‫توسط برنامه میتوانند خوانده شده و تفسیر گردند‪.‬‬
‫یک فایل دودویی از خارج برنامه قابل استفاده نمیباشد‪ ،‬چرا که یک‬
‫فایل دودویی ‪ 200‬بایتی‪ ،‬میتواند ‪ 100‬عدد صحیح (دو بایتی) و یا ‪50‬‬
‫عدد اعشاری (‪ 4‬بایتی) باشد‪.‬‬
‫بنابراین تنها خود برنامهنویس میداند اطالعات را چگونه ذخیره کرده‬
‫است‪ ،‬و به چه نحوی باید آنها را خوانده و تفسیر نماید‪.‬‬
‫توجه کنید که در فایلهای دودویی مواردی همچون فاصله گذاری بین‬
‫اطالعات‪ ،‬رفتن به خط بعد‪ ،‬جدول کشی و ‪ ...‬معنا ندارد‪ ،‬چرا که این‬
‫فایلها از خارج برنامه قابل مشاهده و چاپ نیستند‪.‬‬
‫اما فایلهای دودویی دارای مزایای متعدی نسبت به فایلهای متنی‬
‫هستند که باعث شده است برنامهنویسان در اغلب موارد از این دسته‬
‫فایلها برای ذخیره و بازیابی اطالعات استفاده نمایند‪.‬‬
‫‪ 13-1‬انواع فایلها‬
‫‪‬‬
‫این مزایا عبارتند از‪:‬‬
‫‪ ‬سرعت باال ‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫این فایلها دادهها را به همان نحوی که در حافظه ذخیره میشوند‪ ،‬نگهداری‬
‫میکنند‪.‬‬
‫بنابراین در هنگام نوشتن یا خواندن دادهها به‪/‬از فایل‪ ،‬نیازی به تبدیل دادهها‬
‫به رشته کاراکتری و بالعکس نمیباشد‪ .‬درحالیکه در فایلهای متنی‪ ،‬انجام‬
‫این تبدیل ها‪ ،‬باعث پایین آمدن سرعت میگردد‪.‬‬
‫حجم کمتر‪:‬‬
‫‪‬‬
‫‪‬‬
‫فایلهای دودویی حجم کمتری را نسبت به فایلهای متنی اشغال میکنند‪،‬‬
‫چرا که معموال تبدیل اعداد صحیح و یا اعشاری به رشته‪ ،‬باعث میشود که‬
‫تعداد بایتهای بیشتری اشغال گردد‪.‬‬
‫به عنوان مثال عدد اعشاری ‪1456.321‬که در فایل دودویی به صورت یک داده‬
‫چهار بایتی ذخیره میگردد‪ ،‬دریک فایل متنی احتیاج به یک رشته هشت‬
‫بایتی دارد (برای ذخیره رشته "‪.)"1456.321‬‬
‫‪ 13-1‬انواع فایلها‬
‫امکان دسترسی تصادفی‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫فرض کنید یک فایل متنی حاوی تعدادی عدد صحیح داریم‪ .‬حال قصد داریم‬
‫دهمین عدد این فایل را بخوانیم‪ .‬برای این کار باید از ابتدای فایل شروع‬
‫کرده و نه عدد اول را خوانده و کنار بگذاریم تا به عدد دهم برسیم‪.‬‬
‫چرا که تعداد بایتهای تخصیص داده شده به هر عدد مشخص نیست و‬
‫بستگی به تعداد ارقام آن دارد (مثال ‪ 12‬نیاز به دو بایت و ‪ 6714‬نیاز به چهار‬
‫بایت دارند)‪.‬‬
‫به این روش دسترسی به داده ها‪ ،‬دسترسی ترتیبی گفته میشود‪.‬‬
‫اما در یک فایل دودویی مشابه‪ ،‬میتوان بالفاصله به ابتدای دهمین عدد‬
‫پرش کرده و آن را خواند؛ چرا که هر عدد صحیح دقیقا دو بایت را اشغال‬
‫میکند‪ ،‬بنابراین چنانچه ‪ 18‬بایت از ابتدای فایل جلو برویم‪ ،‬به عدد دهم‬
‫خواهیم رسید‪.‬‬
‫به این روش دسترسی به داده ها‪ ،‬دسترسی تصادفی یا مستقیم‬
‫گفته میشود‪ .‬در قسمتهای بعدی‪ ،‬روش دسترسی تصادفی را بررسی‬
‫خواهیم کرد‪.‬‬
‫اطالعات از بیرون برنامه قابل خواندن و بروزرسانی نیستند‪.‬‬
‫‪‬‬
‫‪‬‬
‫این خاصیت در مواردی که دادهها مهم بوده و نمیخواهیم سایر افراد از‬
‫بیرون برنامه به اطالعات دسترسی داشته باشند‪ ،‬مفید است‪.‬‬
‫‪ 13-2‬نحوه دستیابی به فایلها‬
‫‪‬‬
‫برای دستیابی به یک فایل‪ ،‬باید مراحل زیر انجام گردد‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫تعریف یک متغیر برای فایل‬
‫باز کردن فایل‬
‫خواندن و نوشتن در فایل‬
‫بستن فایل‬
‫کلیه دادهها و توابع الزم برای عملیات فوق در فایل سرآمد‬
‫‪ stdio.h‬تعریف شده اند‪ ،‬بنابراین برای کار با فایلها باید این‬
‫فایل را در برنامه گنجاند‪.‬‬
‫در ادامه‪ ،‬هریک از موارد فوق مورد بررسی قرار گرفته اند‪.‬‬
‫‪ 13-2-1‬تعریف فایل‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫برای تعریف یک فایل‪ ،‬باید از نوع دادهای بنام ‪ FILE‬استفاده نمایید‪.‬‬
‫در حقیقت نوع داده ‪ ،FILE‬یک ساختار است که در فایل ‪ stdio.h‬تعریف‬
‫شده است‪ .‬این ساختار حاوی اطالعاتی همچون ‪:‬‬
‫‪ ‬نام خارجی فایل بر روی دیسک‪ :‬نام واقعی فایل در سیستم عامل‬
‫‪ ‬نوع فایل‪ :‬ورودی‪ ،‬خروجی‪ ،‬دودویی‪ ،‬متنی‬
‫‪ ‬آدرس محل بافر اطالعات‪ :‬مکانی از حافظه است که در هنگام عملیات‬
‫ورودی و خروجی‪ ،‬به عنوان واسط بین برنامه و فایل استفاده میشود‪.‬‬
‫‪ ‬اشاره گر به مکان فعلی فایل‪ :‬شماره بایتی از فایل را نشان میدهد که‬
‫در اولین عمل خواندن یا نوشتن بعدی‪ ،‬استفاده خواهد شد‪ .‬از این‬
‫اشاره گر در قسمتهای بعدی برای دسترسی تصادفی استفاده خواهیم‬
‫کرد‪.‬‬
‫‪ ‬وضعیت خطاها مانند رسیدن به آخر فایل‪ ،‬خطا در خواندن یا نوشتن و ‪...‬‬
‫توجه کنید که در حقیقت نیازی به دانستن عناصر ساختار ‪ FILE‬و نام‬
‫آنها نیست‪ ،‬چرا که کلیه عملیات توسط توابع زبان ‪ C‬انجام خواهد شد و‬
‫موارد گفته شده تنها برای آشنایی بیشتر شما با این ساختار بود‪.‬‬
‫‪ 13-2-1‬تعریف فایل‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫برای استفاده از یک فایل در برنامه‪ ،‬ابتدا باید متغیری از نوع اشاره گر به‬
‫ساختار ‪ FILE‬تعریف نمایید‪ .‬به عنوان مثال به تعریف زیر توجه کنید‪:‬‬
‫; ‪FILE *inputFile‬‬
‫این تعریف‪ ،‬متغیر ‪ inputFile‬را از نوع اشاره گر به ‪ FILE‬تعریف مینماید‪.‬‬
‫در مورد اشاره گرها و نحوه تعریف آنها در فصل آینده بیشتر توضیح خواهیم‬
‫داد‪ .‬در حال حاضر فقط کافی است بدانیم از عملگر * برای تعریف یک اشاره‬
‫گر استفاده میشود‪.‬‬
‫‪ 13-2-2‬باز کردن فایل‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫برای استفاده از یک فایل‪ ،‬ابتدا باید آن را باز کنید و یا به عبارت بهتر‪ ،‬آن را به‬
‫برنامه خود مربوط کنید‪.‬‬
‫برای این کار از تابعی بنام ‪ fopen‬استفاده میشود‪ .‬اعالن این تابع به صورت‬
‫زیر است‪:‬‬
‫; )][‪FILE* fopen(char fileName[], char mode‬‬
‫پارامتر ‪ ،fileName‬رشتهای است که نام خارجی فایل در سیستم عامل را‬
‫مشخص میکند‪.‬‬
‫این رشته میتواند عالوه بر نام فایل‪ ،‬حاوی مسیر فایل نیز باشد‪.‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫به عنوان مثال چنانچه این رشته به صورت "‪"letter.txt‬مشخص شود‪ ،‬در فهرست‬
‫جاری به دنبال این فایل جستجو میشود‪.‬‬
‫اما رشتهای مانند "‪ ،"C:\\myLetters\\letter.txt‬آدرس فیزیکی فایل را به طور کامل‬
‫مشخص مینماید‪.‬‬
‫پارامتر دوم یا ‪ ،mode‬رشتهای است که نحوه باز شدن فایل را نشان میدهد‪.‬‬
‫این رشته از دو قسمت تشکیل میگردد‪.‬‬
‫قسمت اول‪ ،‬عملیات قابل اجرا بر روی فایل را نشان میدهد و یکی از موارد‬
‫مشخص شده در جدول صفجه بعد است‬
‫‪ 13-2-2‬باز کردن فایل‬
‫توضیحات‬
‫رشته‬
‫عمل قابل اجرا‬
‫‪r‬‬
‫خواندن‬
‫فایل باید موجود باشد‪ ،‬در غیر اینصورت ‪ NULL‬باز‬
‫گردانده میشود‪.‬‬
‫‪w‬‬
‫نوشتن‬
‫یک فایل جدید ایجاد میشود‪ ،‬اگر فایل از قبل موجود‬
‫باشد‪ ،‬اطالعات آن از بین خواهد رفت‪.‬‬
‫‪a‬‬
‫افزودن به انتها‬
‫‪r+‬‬
‫خواندن و نوشتن‬
‫فایل باید موجود باشد‪ ،‬در غیر اینصورت ‪ NULL‬باز‬
‫گردانده میشود‪.‬‬
‫‪w+‬‬
‫نوشتن و خواندن‬
‫یک فایل جدید ایجاد میشود‪ ،‬اگر فایل از قبل موجود‬
‫باشد‪ ،‬اطالعات آن از بین خواهد رفت‪.‬‬
‫‪a+‬‬
‫افزودن و خواندن‬
‫اگر فایل موجود باشد‪ ،‬اطالعات به انتهای آن اضافه‬
‫میگردد‪ ،‬درغیراینصورت ایجاد میگردد‪.‬‬
‫اگر فایل موجود باشد‪ ،‬اطالعات به انتهای آن اضافه‬
‫میگردد‪ ،‬درغیراینصورت ایجاد میگردد‪.‬‬
‫‪ 13-2-2‬باز کردن فایل‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫و اما قسمت دوم رشته ‪ ،mode‬نوع فایل را نشان میدهد و یکی از موارد‬
‫مشخص شده در شکل زیر است‪.‬‬
‫رشته‬
‫نوع فایل‬
‫‪t‬‬
‫متنی‬
‫‪b‬‬
‫دودویی‬
‫توضیحات‬
‫فایل بصورت متنی باز شود‪.‬‬
‫فایل بصورت دودویی باز شود‪.‬‬
‫این دو قسمت به یکدیگر متصل شده و تشکیل رشته نحوه باز شدن فایل را‬
‫میدهند‪.‬‬
‫به عنوان نمونه‪ ،‬رشته "‪ "wb‬به معنای نوشتن بر روی یک فایل دودویی‬
‫است‪.‬‬
‫الزم به ذکر است که اگر قسمت دوم ذکر نشود‪ ،‬به طور پیش فرض از نوع‬
‫متنی )‪ (t‬در نظر گرفته میشود‪ .‬به عنوان مثال "‪ "r+‬به معنای خواندن و‬
‫نوشتن بر روی یک فایل متنی است‪.‬‬
‫‪ 13-2-2‬باز کردن فایل‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫تابع ‪ ،fopen‬ابتدا حافظه الزم برای یک ساختار از نوع ‪ FILE‬را تخصیص داده و‬
‫سپس با توجه به آرگومانهای ارسالی (نام واقعی فایل و نحوه باز شدن آن)‪،‬‬
‫عناصر آن را مقدار دهی اولیه میکند‪ ،‬و اشاره گری به آن را باز میگرداند‪.‬‬
‫اکنون میتوان این اشاره گر بازگشتی را به یک متغیر از نوع اشاره گر به ‪FILE‬‬
‫نسبت داده و از آن برای عملیات خواندن و نوشتن در فایل استفاده کرد‪.‬‬
‫به مثال زیر دقت کنید‪:‬‬
‫;‪FILE *inputFile‬‬
‫;)"‪inputFile = fopen("letter.txt", "rt‬‬
‫در این مثال‪ ،‬تابع ‪ fopen‬فایلی بنام ‪ letter.txt‬را به صورت متنی و برای خواندن‬
‫باز کرده و یک اشاره گر به ‪ FILE‬باز میگرداند‪ ،‬که آن را در متغیری به نام‬
‫‪ inputFile‬قرار داده ایم‪.‬‬
‫به عنوان یک مثال دیگر‪ ،‬به نمونه زیر دقت کنید‪:‬‬
‫;‪FILE *itemFile‬‬
‫;)"‪itemFile = fopen("C:\\data\\items.dat","w+b‬‬
‫این دستور‪ ،‬فایلی از آدرس ‪C:\data\items.dat‬را برای خواندن و نوشتن‪ ،‬به‬
‫صورت دودویی باز کرده و اشاره گر ‪ FILE‬آن را در متغیر ‪ dataFile‬قرار میدهد‪.‬‬
‫‪ 13-2-2‬باز کردن فایل‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫توجه کنید که چنانچه تابع ‪ fopen‬به هر دلیلی نتواند فایل مورد نظر را باز‬
‫کند (مثال به دلیل موجود نبودن فایل یا فهرست مورد نظر‪ ،‬یا پر بودن‬
‫دیسک و ‪ ،)...‬مقدار ‪( NULL‬که برابر با ‪ 0‬است) را باز میگرداند‪.‬‬
‫بنابراین بهتر است پس از باز کردن فایل‪ ،‬ابتدا درستی باز شدن آن‬
‫بررسی گردد‪ .‬به مثال زیر توجه کنید‪:‬‬
‫;‪FILE *inputFile‬‬
‫;)"‪inputFile = fopen("letter.txt", "rt‬‬
‫{ )‪if (inputFile == NULL‬‬
‫;)"‪printf("can't open file!\n‬‬
‫; )‪exit(1‬‬
‫}‬
‫البته معموال برنامهنویسان قسمت شرط ‪ if‬را بصورت زیر مینویسند‪:‬‬
‫… { )‪if (!inputFile‬‬
‫‪ 13-2-3‬بستن فایل‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫پس از آنکه عملیات ما بر روی فایل خاتمه پیدا کرد‪ ،‬باید آن را توسط تابع‬
‫‪ fclose‬ببندیم‪.‬‬
‫اعالن این تابع به شکل زیر است‪:‬‬
‫)‪int fclose(FILE *f‬‬
‫این تابع‪ ،‬یک اشاره گر به فایل را به عنوان ورودی دریافت و آن را میبندد‪.‬‬
‫در صورت موفقیت در بستن فایل مقدار ‪ ،0‬و در صورت عدم موفقیت مقدار‬
‫‪ EOF‬بازگردانده میشود‪ EOF .‬یک ثابت نمادی است که برابر مقدار ‪-1‬‬
‫تعریف شده است‪.‬‬
‫به عنوان مثال دستور زیر‪ ،‬فایلی که توسط متغیر ‪inputFile‬به آن اشاره‬
‫میشود را میبندد‪:‬‬
‫; )‪fclose(inputFile‬‬
‫‪ 13-2-3‬بستن فایل‬
‫‪‬‬
‫‪‬‬
‫با بستن یک فایل‪ ،‬ارتباط آن با برنامه قطع میشود‪ .‬چندین دلیل برای‬
‫بستن فایل وجود دارد‪:‬‬
‫‪ ‬تعداد فایلهایی که میتوانند به طور همزمان باز باشند‪ ،‬محدود است و‬
‫در نتیجه هرگاه به فایلی نیاز نداریم‪ ،‬بهتر است آن را ببندیم‪.‬‬
‫‪ ‬هنگام عملیات نوشتن در فایل‪ ،‬دادهها ابتدا در یک بافر در حافظه نوشته‬
‫میشوند (به قسمت تعریف فایل مراجعه کنید)‪ ،‬سپس هنگامی که‬
‫بافر پر شد‪ ،‬به طور یکجا به فایل ارسال میشوند‪.‬با بستن فایل‪ ،‬چنانچه‬
‫هنوز دادهای در بافر باقی مانده باشد‪ ،‬به فایل ارسال میشود‪.‬‬
‫‪ ‬تابع ‪ fclose‬فضای تخصیص یافته به متغیر اشاره گر فایل را آزاد میکند‬
‫که باعث صرفه جویی در حافظه میشود‪ .‬عالوه براین میتوان از این‬
‫متغیر مجددا برای باز کردن یک فایل دیگر استفاده کرد‪.‬‬
‫البته چنانچه اجرای یک برنامه به طور عادی خاتمه پیدا کند‪ ،‬خود‬
‫کامپایلر تمام فایلهای باز آن را میبندد؛ ولی بهتر است این کار توسط‬
‫خود برنامهنویس انجام شود‪.‬‬
‫‪ 13-2-3‬بستن فایل‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫برای بستن فایل تابع دیگری نیز وجود دارد‪ .‬تابع ‪ fcloseall‬تمام فایلهای‬
‫باز برنامه را میبندد‪.‬‬
‫این تابع هیچ آرگومانی دریافت نی کند و اعالن آن به صورت زیر است‪:‬‬
‫;)(‪int fcloseall‬‬
‫این تابع در صورت موفقیت‪ ،‬تعداد فایلهای بسته شده و در صورت عدم‬
‫موفقیت‪ ،‬مقدار ‪ EOF‬را باز میگرداند‪.‬‬
‫آخرین نکتهای که به آن اشاره میکنیم‪ ،‬در مورد بافر است‪ .‬همانطور که‬
‫گفته شد‪ ،‬با بسته شدن یک فایل‪ ،‬محتوای بافر آن خالی شده و به‬
‫فایل اصلی ارسال میگردد‪ .‬اما چنانچه پیش از بستن فایل بخواهیم‬
‫بافر را خالی کنیم‪ ،‬میتوانیم از تابع ‪ fflush‬استفاده نماییم‪.‬‬
‫اعالن این تابع به صورت زیر است‪:‬‬
‫)‪int fflush(FILE *f‬‬
‫این تابع‪ ،‬بافر فایلی را که توسط پارامتر ‪ f‬به آن ارسال کرده ایم‪ ،‬خالی‬
‫مینماید‪ .‬در صورت موفقیت مقدار ‪ ،0‬و درصورت عدم موفقیت مقدار‬
‫‪( EOF‬یا ‪ )-1‬باز گردانده میشود‪.‬‬
‫‪ 13-2-4‬ورودی و خروجی در فایلها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫از آنجا که نحوه خواندن و نوشتن در فایلهای متنی با فایلهای دودویی متفاوت است‪ ،‬هریک از آنها را‬
‫در یک بخش جداگانه بررسی مینماییم‪.‬‬
‫قبل از اینکه این مبحث را آغاز نماییم‪ ،‬باید اشارهای به یک متغیر بسیار مهم‪ ،‬یعنی اشاره گر به‬
‫مکان فعلی فایل داشته باشیم‪.‬‬
‫همان طور که در قسمتهای قبلی گفته شد‪ ،‬ساختار ‪ FILE‬دارای عضوی است که به مکان فعلی‬
‫فایل اشاره میکند‪ .‬این عضو‪ ،‬مکانی از فایل را نشان میدهد که عمل خواندن یا نوشتن بعدی از‪/‬به‬
‫آنجا انجام خواهد شد‪.‬‬
‫هنگامی که یک فایل را باز میکنیم‪ ،‬این اشاره گر به اولین بایت فایل اشاره میکند‪ ،‬مگر اینکه فایل‬
‫را برای اضافه کردن به انتها (با گزینه ‪ )a‬باز کرده باشیم‪ ،‬که در این صورت به انتهای فایل اشاره‬
‫خواهد کرد‪.‬‬
‫با هر عمل خواندن یا نوشتن‪ ،‬این اشاره گر به تعداد بایتهای خوانده یا نوشته شده‪ ،‬جلو میرود‪.‬‬
‫حال فرض کنید فایلی را برای خواندن باز کرده ایم‪ .‬با اولین عمل خواندن یک کاراکتر‪ ،‬اولین بایت آن‬
‫خوانده خواهد شد‪ .‬اما از آنجا که اشاره گر مکان فعلی فایل نیز به اندازه یک بایت جلو میرود‪ ،‬در‬
‫دومین عمل خواندن کاراکتر‪ ،‬دومین بایت آن فایل خوانده خواهد شد (و نه اولین بایت) و عملیات به‬
‫همین شکل ادامه مییابد‪.‬‬
‫الزم به ذکر است که کلیه این عملیات توسط خود توابع زبان ‪ C‬انجام خواهد شد‪ ،‬و نیازی نیست که‬
‫برنامهنویس نگران محل اشاره گر مکان فعلی فایل باشد‪ .‬البته میتوانیم مکان این اشاره گر را‬
‫توسط توابعی که معرفی خواهیم کرد‪ ،‬تغییر دهیم‪.‬‬
‫روال نوشتن در فایل نیز مشابه خواندن است؛ به این معنا که با هر بار عمل نوشتن یک داده‪ ،‬اشاره‬
‫گر مکان فعلی به تعداد بایتهای آن داده جلو خواهد رفت‪ .‬به این ترتیب هر داده جدید‪ ،‬به طور خودکار‬
‫در مکان پس از داده قبلی نوشته خواهد شد (و نه بر روی آن)‪.‬‬
‫‪ 13-2-4-1‬ورودی و خروجی در فایلهای متنی‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫همانطور که قبال نیز گفته شد‪ ،‬فایلهای متنی توسط هر ویرایشگری‬
‫قابل خواندن هستند‪ .‬لذا برنامهنویس باید بتواند دادهها را در قالب‬
‫دلخواه خود در فایل قرار دهد تا توسط کاربران قابل استفاده باشد‪.‬‬
‫به همین دلیل به فایلهای متنی‪ ،‬فایلهای قالب بندی شده نیز‬
‫میگویند‪.‬‬
‫اگر به یاد داشته باشید‪ ،‬عملیات ورودی و خروجی در زبان ‪ C‬از طریق دو‬
‫تابع ‪ scanf‬و ‪ printf‬انجام میگرفت‪ .‬برای عملیات ورودی و خروجی در‬
‫فایلهای متنی نیز دو تابع مشابه وجود دارد‪ fscanf :‬و ‪.fprintf‬‬
‫تابع ‪fprintf‬که برای نوشتن دادههای خروجی در یک فایل استفاده‬
‫میشود‪ ،‬دارای شکل کلی زیر است‪:‬‬
‫; )>‪fprintf(<file-pointer>,<control-string>, <variable-list‬‬
‫ ورودی و خروجی در فایلهای متنی‬13-2-4-1
‫ یا‬365( ‫) برنامهای بنویسید که میزان فروش روزانه یک شرکت را برای تمام روزهای سال‬13-1 ‫مثال‬
‫ میزان فروش روزانه یک عدد‬.‫ قرار دهد‬sales.txt ‫ و آنها را دریک فایل متنی به نام‬،‫ روز) دریافت‬366
.‫صحیح است‬
#include <stdio.h>
void main() {
FILE *outFile ;
int i, day, sales;
outFile = fopen("sales.txt", "wt");
if (!outFile) {
printf("can't open file!");
return ;
}
printf("enter number of days : ");
scanf("%d", &day);
for (i=0; i<day; i++) {
printf("enter daily sales (day %d) : ", i+1);
scanf("%d", &sales) ;
fprintf(outFile, "%d\n", sales);
}
fclose(outFile);
}

‫‪ 13-2-4-1‬ورودی و خروجی در فایلهای متنی‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫برای خواندن اطالعات از یک فایل متنی‪ ،‬از تابع ‪ fscanf‬استفاده میگردد‪.‬‬
‫شکل کلی این تابع بصورت زیر است‪:‬‬
‫; )>‪fscanf(<file-pointer>,<control-string>, <variable-address-list‬‬
‫تعریف این تابع نیز تقریبا همانند تابع ‪ scanf‬است‪ ،‬با این تفاوت که به عنوان‬
‫اولین آرگومان‪ ،‬یک اشاره گر به فایل را دریافت میکند که مشخص کننده‬
‫فایلی است که دادهها باید از آن خوانده شوند‪.‬‬
‫دو آرگومان بعدی دقیقا همانند تابع ‪scanf‬بوده و نحوه استفاده از آنها نیز‬
‫کامال مشابه است‪ .‬به عنوان نمونه‪ ،‬به یک مثال توجه کنید‪.‬‬
‫مثال ‪ )13-2‬برنامهای بنویسید که فایل متنی ‪ ،sales.txt‬مربوط به مثال قبلی‬
‫را خوانده و میانگین فروش روزانه شرکت را بر روی نمایشگر چاپ نماید‪.‬‬
‫‪ 13-2-4-1‬ورودی و خروجی در فایلهای متنی‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫نکته مهم در حل این مسئله آن است که تعداد اعداد فایل ‪ sales.txt‬از قبل مشخص نیست (ممکن‬
‫است ‪ 365‬یا ‪ 366‬باشد)‪ ،‬بنابراین برنامه باید عمل خواندن را تا زمانی که به انتهای فایل برسد‪،‬‬
‫ادامه دهد‪.‬‬
‫حتما از مطالب مطرح شده در اول این قسمت به یاد دارید که با هر بار عمل خواندن از فایل‪ ،‬اشاره‬
‫گر مکان جاری فایل به اندازه تعداد بایتهای خوانده شده جلو میرود‪ .‬بنابراین باید عمل خواندن را تا‬
‫زمانی که این اشاره گر به انتهای فایل برسد‪ ،‬ادامه دهیم‪.‬‬
‫اما چگونه میتوان فهمید که این اشاره گر به انتهای فایل رسیده است؟ خوشبختانه برای این کار‬
‫تابعی به نام ‪ feof‬وجود دارد که اعالن آن به صورت زیر است‪:‬‬
‫;)‪int feof(FILE *f‬‬
‫این تابع یک اشاره گر به فایل را دریافت کرده‪ ،‬و در صورتی که اشاره گر مکان فعلی آن به انتهای‬
‫فایل رسیده باشد‪ ،‬مقدار درست (غیر صفر) و درغیر این صورت مقدار نادرست (صفر) را باز‬
‫میگرداند‪.‬‬
‫اما در استفاده از این تابع باید دقت کرد‪ .‬نکته مهم در اینجا است که این تابع هنگامی مقدار درست‬
‫باز میگرداند که در آخرین عمل خواندن با شکست مواجه شده و به پایان فایل رسیده باشد‪.‬‬
‫برای روشن شدن موضوع‪ ،‬فرض کنید فایلی دارای ‪ 10‬عدد صحیح است‪ .‬چنانچه پس از ‪ 10‬بار عمل‬
‫خواندن از فایل‪ ،‬تابع ‪ feof‬را فراخوانی کنید‪ ،‬مقدار صفر را باز خواهد گرداند که نشان میدهد هنوز‬
‫انتهای فایل را ندیده است‪ .‬اما چنانچه یک عمل خواندن دیگر (یازدهمین عمل خواندن) انجام دهید‪،‬‬
‫گرچه این عمل ناموفق خواهد بود چرا که دادهای برای خواندن وجود ندارد‪ ،‬اما تابع متوجه میگردد‬
‫که به انتهای فایل رسیده است‪ .‬این بار فراخوانی تابع ‪ feof‬مقدار غیرصفر باز خواهد گرداند‪.‬‬
‫ ورودی و خروجی در فایلهای متنی‬13-2-4-1
#include <stdio.h>
void main() {
FILE *inFile ;
int sales, counter=0 ;
float average = 0.0;
inFile = fopen("sales.txt", "rt");
if (!inFile) {
printf("can't open file!");
return ;
}
fscanf(inFile, "%d",&sales) ;
while ( ! feof(inFile) ) {
average += sales ;
counter ++;
fscanf(inFile, "%d",&sales) ;
}
fclose(inFile);
average /= counter ;
printf("average = %f",average);
}
‫‪ 13-2-4-2‬ورودی و خروجی در فایلهای دودویی‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫همانطور که گفته شد‪ ،‬در فایلهای دودویی اطالعات به صورت بایتهای‬
‫خام ذخیره میگردند و به همین دلیل توسط ویرایشگرها قابل خوانده‬
‫شدن نیستند‪.‬‬
‫در این نوع فایلها‪ ،‬دادهها به صورت پشت سرهم ذخیره میگردند و از‬
‫آنجا که اندازه (تعداد بایتهای) هر داده مشخص است‪ ،‬احتیاجی به‬
‫استفاده از کاراکترهای جدا کننده مانند فضای خالی نیست‪.‬‬
‫به طور کلی برای خواندن و نوشتن در این فایلها‪ ،‬احتیاج به‬
‫ورودی‪/‬خروجی قالب بندی شده نداریم‪ ،‬بلکه به توابعی نیاز داریم که‬
‫بتوانند دادهها را به صورت دنبالهای از بایتهای خام به فایل ارسال کنند و‬
‫یا از آن بخوانند‪.‬‬
‫به همین دلیل به این فایلها‪ ،‬فایلهای قالب بندی نشده نیز گفته‬
‫میشود‪.‬‬
‫برای خواندن و نوشتن در فایلهای دودویی‪ ،‬دو تابع داریم ‪:‬‬
‫‪fread ‬‬
‫‪fwrite ‬‬
‫‪ 13-2-4-2‬ورودی و خروجی در فایلهای دودویی‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫اعالن تابع ‪fwrite‬که برای نوشتن در فایلهای دودویی به کار میرود‪ ،‬به شکل‬
‫زیر است‪:‬‬
‫;)‪size_t fwrite(void *data, size_t size, size_t n, FILE *f‬‬
‫ابتدا توجه کنید که نوع داده ‪ ،size_t‬نام دیگری برای عدد صحیح بدون عالمت‬
‫)‪ (unsigned int‬میباشد‪ .‬این نوع داده توسط دستور ‪ typedef‬در فایل ‪stdio.h‬‬
‫تعریف شده است‪.‬‬
‫پارامتر ‪ ،data‬آدرس دادهای است که قصد نوشتن آن در فایل را داریم‪ .‬همانطور‬
‫که پیشتر نیز گفته شد‪ ،‬برای به دست آوردن آدرس یک متغیر‪ ،‬از عالمت &‬
‫پیش از نام متغیر استفاده مینماییم‪.‬‬
‫پارامتر ‪ ،size‬اندازه دادهای که باید نوشته شود را بر حسب بایت مشخص می‬
‫نماید‪.‬‬
‫پارامتر ‪ n‬نیز تعداد دادههایی که باید نوشته شوند را مشخص میکند‪ .‬در‬
‫قسمتهای بعدی خواهید دید که این تابع می تواند تعدادی داده را در قالب یک‬
‫آرایه به طور یکجا در فایل بنویسد‪.‬‬
‫آخرین پارامتر نیز اشاره گر به فایلی است که قصد نوشتن دادهها در آن را‬
‫داریم‪ .‬این تابع به عنوان خروجی‪ ،‬تعداد دادههای (نه تعداد بایتهای) نوشته‬
‫شده را باز میگرداند‪.‬‬
‫ ورودی و خروجی در فایلهای دودویی‬13-2-4-2
‫ روز) دریافت‬366 ‫ یا‬365( ‫) برنامهای بنویسید که میزان فروش روزانه یک شرکت را برای تمام روزهای سال‬13-3 ‫مثال‬
.‫ میزان فروش روزانه یک عدد صحیح است‬.‫ قرار دهد‬sales.dat ‫و آنها را دریک فایل دودویی بنام‬
#include <stdio.h>
void main() {
FILE *outFile ;
int i, day, sales ;
outFile = fopen("sales.dat", "wb");
if (!outFile) {
printf("can't open file!");
return ;
}
printf("enter number of days : ");
scanf("%d", &day);
for (i=0; i<day; i++) {
printf("enter daily sales (day %d): ", i+1);
scanf("%d", &sales) ;
fwrite(&sales, sizeof(int), 1, outFile) ;
}
fclose(outFile);
}

‫‪ 13-2-4-2‬ورودی و خروجی در فایلهای دودویی‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫برای خواندن دادهها از یک فایل دودویی‪ ،‬از تابع ‪ fread‬استفاده می شود‪.‬‬
‫اعالن این تابع بصورت زیر است‪:‬‬
‫;)‪size_t fread(void *data, size_t size, size_t n, FILE *f‬‬
‫پارامترهای این تابع همانند تابع ‪ fwrite‬میباشند‪ ،‬با این تفاوت که اولین‬
‫پارامتر‪ ،‬آدرس داده ای است که باید از فایل خوانده شود‪.‬‬
‫در ضمن مقدار خروجی این تابع‪ ،‬تعداد دادههای (و نه بایتهای) خوانده شده‬
‫میباشد‪ .‬به مثال زیر توجه کنید‪.‬‬
‫مثال ‪ )13-4‬برنامهای بنویسید که فایل دودویی ‪ ،sales.dat‬مربوط به مثال‬
‫قبلی را خوانده و میانگین فروش روزانه شرکت را بر روی نمایشگر چاپ‬
‫نماید‪.‬‬
‫ ورودی و خروجی در فایلهای دودویی‬13-2-4-2
#include <stdio.h>
void main() {
FILE *inFile ;
int sales, counter=0 ;
float average = 0.0;
inFile = fopen("sales.dat", "rb");
if (!inFile) {
printf("can't open file!");
return ;
}
fread(&sales , sizeof(int), 1, inFile) ;
while ( ! feof(inFile) ) {
average += sales ;
counter ++;
fread(&sales, sizeof(int), 1, inFile) ;
}
fclose(inFile);
average /= counter ;
printf("average = %f",average);
}
‫‪ 13-2-4-3‬سایر توابع ورودی و خروجی فایل‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫به جز توابع گفته شده‪ ،‬توابع دیگری نیز برای ورودی‪/‬خروجی در فایلها وجود دارند‪.‬‬
‫توابع ‪ fgets‬و ‪ fputs‬برای خواندن و نوشتن رشتهها در فایل بکار میروند‪.‬‬
‫اعالن تابع ‪ fgets‬به شکل زیر است‪:‬‬
‫; )‪char* fgets(char string[], int n, FILE *f‬‬
‫این تابع یک رشته کاراکتری را از فایل ‪ f‬خوانده و در رشته ‪ string‬قرار میدهد‪ .‬خواندن‬
‫رشته تا زمانی ادامه می یابد که یا در فایل به خط جدید )‪ (new line‬برسیم‪ ،‬و یا به تعداد‬
‫‪ n-1‬کاراکتر خوانده شود‪ .‬یک کاراکتر باقیمانده نیز برای '‪ '\0‬درنظر گرفته شده است‪.‬‬
‫مقدار بازگشتی این تابع‪ ،‬آدرس رشته خوانده شده است‪.‬‬
‫اعالن تابع ‪puts‬نیز به شکل زیر است‪:‬‬
‫; )‪int fputs(char string[], FILE *f‬‬
‫دسته دیگر‪ ،‬توابع ‪ fgetc‬و ‪fputc‬هستند که به ترتیب برای خواندن و نوشتن یک کاراکتر در‬
‫فایل به کار می روند‪ .‬این توابع هنگامی استفاده می شوند که قصد دارید یک فایل را به‬
‫صورت بایت به بایت (کاراکتر به کاراکتر) پردازش نمایید‪ .‬اگرچه این دو تابع معموال در‬
‫فایلهای دودویی استفاده می شوند‪ ،‬اما گاهی از آنها برای پردازش فایلهای متنی نیز‬
‫استفاده می شود‪.‬‬
‫اعالن این دو تابع بصورت زیر است‪:‬‬
‫; )‪int fgetc(FILE *f‬‬
‫; )‪int fputc(int c, FILE *f‬‬
‫‪ 13-3‬دسترسی مستقیم به فایلها‬
‫‪‬‬
‫به دو شکل می توان فایلها را مورد پردازش قرار داد‪:‬‬
‫‪‬‬
‫‪‬‬
‫پردازش ترتیبی ‪ :‬در این روش‪ ،‬پردازش از ابتدای فایل شروع‬
‫می شود و دادهها یک به یک پردازش می گردند تا به انتهای فایل‬
‫برسیم‪ .‬این روش در مواقعی مناسب است که بخواهیم عملیاتی‬
‫را بر روی کل فایل و یا قسمت بزرگی از آن انجام دهیم‪.‬‬
‫پردازش مستقیم )تصادفی( ‪ :‬در این روش می توان به طور‬
‫مستقیم به یک مکان (آدرس) مشخص از فایل دسترسی پیدا‬
‫کرد‪ .‬این روش در مواردی موثر است که بخواهیم عملیاتی را بر‬
‫روی برخی داده های مشخص فایل انجام دهیم‪.‬‬
‫‪ 13-3‬دسترسی مستقیم به فایلها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫با توجه به مطالب گفته شده قبلی‪ ،‬دسترسی مستقیم فقط در مورد‬
‫فایلهای دودویی معنا دارد‪.‬‬
‫چرا که در این فایلها با توجه به ثابت بودن اندازه هر داده‪ ،‬مکان هر یک‬
‫از دادهها کامال مشخص است‪.‬‬
‫مثال چنانچه فایلی از اعداد صحیح به صورت دودویی داشته باشیم و‬
‫فرض کنیم هر عدد صحیح ‪ 2‬بایت باشد‪ ،‬برای خواندن دهمین عنصر‬
‫کافی است اشاره گر مکان جاری فایل را به بایت شماره ‪ 18‬برده‬
‫(ابتدای دهمین عدد) و سپس عمل خواندن را انجام دهیم‪.‬‬
‫اما آیا می توان همین روش را در مورد یک فایل متنی از اعداد صحیح نیز‬
‫بکار برد؟ با توجه به اینکه در چنین فایلی‪ ،‬تعداد بایتهای تخصیص یافته‬
‫به هر عدد بستگی به تعداد ارقام آن دارد‪ ،‬چگونه می توان مکان‬
‫(آدرس) دهمین عدد را تعیین کرد؟ مسلما تنها روش آن است که از اول‬
‫فایل شروع کرده و عدد اول را بخوانیم تا به جداکننده برسیم‪ ،‬سپس‬
‫عدد دوم و ‪ ...‬تا به دهمین عدد برسیم‪ .‬بنابراین معموال در فایلهای‬
‫متنی از پردازش ترتیبی استفاده می شود‪.‬‬
‫‪ 13-3‬دسترسی مستقیم به فایلها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫به یاد دارید که هر فایل دارای یک اشاره گر به مکان فعلی فایل‬
‫است‪ .‬این اشاره گر به مکانی از فایل اشاره می نماید که‬
‫عمل خواندن یا نوشتن بعدی در آن انجام خواهد شد‪ .‬برای‬
‫دسترسی مستقیم به داده های فایل ‪ ،‬نیاز به تابعی داریم که‬
‫به ما امکان تغییر مکان این اشاره گر را بدهد‪.‬‬
‫این تابع ‪ fseek‬نام دارد و اعالن آن به صورت زیر است‪:‬‬
‫; )‪int fseek(FILE *f, long int offset, int place‬‬
‫مقادیر مجازی برای پارامتر ‪ place‬در جدول صفحه بعد آمده‬
‫است‪:‬‬
‫‪ 13-3‬دسترسی مستقیم به فایلها‬
‫مقدار ثابت نمادی‬
‫‪‬‬
‫‪‬‬
‫مفهوم‬
‫مقادیر مجاز تعداد بایت‬
‫‪0‬‬
‫‪SEEK_SET‬‬
‫‪1‬‬
‫عدد مثبت ‪ :‬حرکت رو به جلو‬
‫‪ SEEK_CUR‬جابجایی از مکان فعلی‬
‫اشاره گر شروع شود عدد منفی ‪ :‬حرکت رو به عقب‬
‫‪2‬‬
‫‪ SEEK_END‬جابجایی از انتهای فایل‬
‫شروع شود‬
‫جابجایی از ابتدای فایل‬
‫شروع شود‬
‫‪ 0‬یا یک عدد مثبت برای حرکت‬
‫رو به جلو‬
‫‪ 0‬یا یک عدد منفی برای حرکت‬
‫رو به عقب‬
‫توجه کنید که دومین پارامتر‪ ،‬یعنی ‪ offset‬می تواند منفی نیز باشد که به‬
‫معنای حرکت رو به عقب است‪.‬‬
‫این تابع در صورت موفقیت مقدار ‪ ،0‬و در غیر اینصورت یک مقدار غیر صفر باز‬
‫می گرداند‪ .‬به عنوان نمونه به مثال زیر توجه نمایید‪.‬‬
‫ دسترسی مستقیم به فایلها‬13-3
‫) برنامه ای بنویسید که میانگین فروش ماه مهر و همچنین میزان فروش در آخرین‬13-5 ‫مثال‬
.‫) استخراج کرده و چاپ نماید‬13-3 ‫(مربوط به مثال‬sales.dat ‫روز سال را از فایل‬
#include <stdio.h>
void main() {
FILE *inFile ;
int i, sales, lastDaySales ;
float averageMehr = 0.0;
inFile = fopen("sales.dat", "rb");
if (!inFile) {
printf("can't open file!");
return ;
}
fseek(inFile, 186 * sizeof(int), SEEK_SET);
//‫پرش به ابتدای اطالعات ماه مهر‬
//‫حلقه مربوط به خواندن میزان فروش ماه مهر و محاسبه میانگین آنها‬
for (i=0; i<30; i++) {
fread(&sales , sizeof(int), 1, inFile) ;
averageMehr += sales ;
}

‫ دسترسی مستقیم به فایلها‬13-3
averageMehr /= 30 ;
fseek(inFile, -1L * sizeof(int), SEEK_END);
‫آخرین روز سال‬
fread(&lastDaySales , sizeof(int), 1, inFile) ;
printf("average = %f\n",averageMehr);
printf("last day sales = %d",lastDaySales) ;
fclose(inFile);
}
// ‫پرش به میزان فروش‬
‫‪ 13-3‬دسترسی مستقیم به فایلها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫یکی از مهمترین کاربردهای تابع ‪ ،fseek‬بروز رسانی (انجام تغییرات) فایلهای‬
‫دودویی است‪.‬‬
‫به عنوان مثال فرض کنید اطالع پیدا کرده ایم که در فایل ‪ ،sales.dat‬یکی از‬
‫فروشهای روزانه به طور اشتباه زیر ‪ 100‬عدد وارد شده است‪ ،‬و باید به آن ‪100‬‬
‫واحد اضافه شود‪.‬‬
‫برای انجام این کار باید از ابتدای فایل شروع به خواندن نماییم تا به اولین داده‬
‫کوچکتر از ‪ 100‬برسیم‪ .‬اما پس از پیدا کردن داده موردنظر‪ ،‬ابتدا باید اشاره گر‬
‫مکان فعلی فایل را به اندازه یک عدد صحیح به عقب بازگردانده و سپس عدد‬
‫جدید را بنویسیم‪ ،‬در غیراینصورت عدد جدید بر روی فروش روز بعد نوشته‬
‫خواهد شد‪.‬‬
‫شکل زیر نحوه انجام کار را بر روی یک فایل نمونه نشان می دهد‪.‬‬
‫اشاره گر‬
‫های‬
‫دادهاشاره‬
‫بازگرداندن‬
‫‪ :4‬خواندن‬
‫‪3‬‬
‫مرحله ‪2‬‬
‫جدیدگربر‬
‫نوشتن‬
‫کردن‬
‫داده ‪1‬‬
‫مرحله‬
‫فایلاده‬
‫داده‬
‫اولین د‬
‫ابتدای‬
‫بازبه‬
‫رسیدن‬
‫جاری ‪:‬به‬
‫مکانتا‬
‫فایل‬
‫نظر‬
‫مورد‬
‫روی‬
‫اینجا ‪)75‬‬
‫‪fseek‬‬
‫تابع (در‬
‫موردنظرازبا‪100‬‬
‫کوچکتر‬
‫…‬
‫‪4231‬‬
‫اشاره گر‬
‫اشاره گر‬
‫‪175‬‬
‫‪75‬‬
‫‪567‬‬
‫‪2320‬‬
‫ دسترسی مستقیم به فایلها‬13-3
#include <stdio.h>
void main() {
FILE *updateFile ;
int sales ;
updateFile = fopen("sales.dat", "r+b");
if (!updateFile) {
printf("can't open file!");
return ;
}
fread(&sales , sizeof(int), 1, updateFile) ;
while ( ! feof(updateFile) ) {
if (sales < 100) {
sales += 100 ;
//‫بازگرداندن اشاره گر به روی داده قبلی‬
fseek(updateFile, -1L * sizeof(int), SEEK_CUR) ;
//‫نوشتن مقدار جدید بر روی داده قبلی‬
fwrite(&sales, sizeof(int), 1, updateFile);
break ;
//‫خروج از حلقه در صورت پیدا شدن عدد موردنظر‬
}
fread(&sales, sizeof(int), 1, updateFile) ;
}
fclose(updateFile);
}
‫‪ 13-4‬خواندن و نوشتن ساختارها در فایل‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫در بسیاری از موارد ممکن است بخواهید یک ساختار را در یک فایل بنویسید‪ ،‬یا‬
‫آن را از یک فایل بخوانید‪ .‬نحوه انجام این کار در فایلهای متنی و دودویی کامال‬
‫متفاوت است‪.‬‬
‫برای خواندن یا نوشتن یک ساختار در یک فایل متنی‪ ،‬باید هریک از اعضای آن‬
‫را به طور جداگانه از فایل خواند و یا در آن نوشت‪.‬‬
‫دلیل این مسئله نیز کامال مشخص است‪ .‬برای ارسال یک ساختار به یک فایل‬
‫متنی‪ ،‬باید نحوه و قالب نوشتن هر عضو‪ ،‬به طور جداگانه مشخص شود‪.‬‬
‫مثال آیا هر عضو در یک ردیف جدا چاپ شود و یا با یک فاصله از عضو بعدی جدا‬
‫گردد‪.‬‬
‫همین مسئله در هنگام خواندن نیز باید رعایت گردد‪ .‬بنابراین نمیتوان کل یک‬
‫ساختار را به طور یکجا در فایل نوشت یا از آن خواند‪.‬‬
‫مثال ‪ )13-6‬برنامهای بنویسید که مشخصات تعدادی دانشجو را از کاربر دریافت‬
‫و آنها را دریک فایل متنی بنام ‪ student.txt‬قرار دهد‪ .‬اطالعات هر دانشجو شامل‬
‫شماره دانشجویی‪ ،‬نام‪ ،‬سن و معدل دانشجو میباشد‪.‬‬
‫ خواندن و نوشتن ساختارها در فایل‬13-4
#include <stdio.h>
struct student {
long int id;
char name[40] ;
int age;
float average ;
};
void main() {
student s;
FILE *studentFile;
int i, n;
studentFile = fopen("student.txt","wt");
if (! studentFile) {
printf("can't open file.");
return ;
}
printf("please enter number of students: ");
scanf("%d", &n);
for (i=0; i<n; i++) {
printf("student %d: enter id, name, age and average: ", i+1);
scanf("%ld %s %d %f", &s.id, s.name, &s.age, &s.average);
fprintf(studentFile,"%ld %s %d %5.2f\n",s.id, s.name, s.age, s.average);
}
fclose(studentFile);
}
‫‪ 13-4‬خواندن و نوشتن ساختارها در فایل‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫اما خوشبختانه روش کار در فایلهای دودویی بسیار ساده تر است‪.‬‬
‫برای خواندن یا نوشتن یک ساختار در یک فایل دودویی‪ ،‬میتوان آن‬
‫ساختار را به صورت یکجا از فایل خواند یا در آن نوشت‪.‬‬
‫هنگامی که یک ساختار را در یک فایل دودویی مینویسیم‪ ،‬کلیه‬
‫بایتهای عناصر آن بصورت پشت سرهم و بدون هیچ فاصلهای در فایل‬
‫نوشته میشوند‪.‬‬
‫همانطور که قبال نیز گفتیم‪ ،‬از آنجا که تعداد بایتهای هر یک از عناصر‬
‫ساختار کامال مشخص است‪ ،‬نیازی به استفاده از یک جداکننده نیست‪.‬‬
‫در هنگام خواندن یک ساختار از فایل نیز‪ ،‬همان عناصر (با تعداد بایتهای‬
‫مشخص) به صورت پشت سرهم از فایل خوانده میشوند‪.‬‬
‫مثال ‪ )13-7‬برنامهای بنویسید که مشخصات تعدادی دانشجو را از کاربر‬
‫دریافت و آنها را دریک فایل دودویی بنام ‪ student.dat‬قرار دهد‪ .‬اطالعات‬
‫هر دانشجو شامل شماره دانشجویی‪ ،‬نام‪ ،‬سن و معدل دانشجو‬
‫میباشد‪.‬‬
‫ خواندن و نوشتن ساختارها در فایل‬13-4
#include <stdio.h>
struct student {
long int id;
char name[40] ;
int age;
float average ;
};
void main() {
student s;
FILE *studentFile;
int i, n;
studentFile = fopen("student.dat","wb");
if (! studentFile) {
printf("can't open file.");
return ;
}
printf("please enter student number : ");
scanf("%d", &n);
for (i=0; i<n; i++) {
printf("student %d: enter id, name, age and average: ", i+1);
scanf("%ld %s %d %f", &s.id, s.name, &s.age, &s.average);
fwrite(&s, sizeof(student), 1, studentFile) ;
}
fclose(studentFile);
}
‫‪ 13-4‬خواندن و نوشتن ساختارها در فایل‬
‫‪‬‬
‫مثال ‪ )13-8‬برنامهای برای بروزرسانی اطالعات دانشجویان بنویسید‪ .‬این برنامه باید یک شماره‬
‫دانشجویی را دریافت و در فایل دودویی دانشجویان )‪ (student.dat‬به دنبال آن جستجو نماید‪ .‬سپس‬
‫در صورت پیدا شدن دانشجو‪ ،‬اطالعات قبلی شامل نام‪ ،‬سن و معدل وی را با اطالعات جدیدی که‬
‫از کاربر دریافت میکند‪ ،‬جایگزین نماید‪.‬‬
‫>‪#include <stdio.h‬‬
‫تعریف ساختار‪ student‬همانند برنامه مثال ‪ 13-7‬میباشد‪//‬‬
‫{ )(‪void main‬‬
‫;‪student s‬‬
‫;‪FILE *studentFile‬‬
‫;‪int i‬‬
‫; ‪long int searchId‬‬
‫;)"‪studentFile = fopen("student.dat","r+b‬‬
‫{ )‪if (! studentFile‬‬
‫;)"‪printf("can't open file.‬‬
‫; ‪return‬‬
‫}‬
‫;)" ‪printf("please enter student id :‬‬
‫;)‪scanf("%ld", &searchId‬‬
‫دریافت شماره دانشجویی موردنظر‪//‬‬
‫ خواندن و نوشتن ساختارها در فایل‬13-4
//‫جستجو به دنبال شماره دانشجویی موردنظر در فایل دانشجویان‬
do
fread(&s, sizeof(student), 1, studentFile) ;
while (!feof(studentFile) && s.id != searchId) ;
if (feof(studentFile)) //‫دانشجو پیدا نشده است‬
printf("student not found!\n");
else {
//‫دانشجو پیدا شده است‬
printf("student found!\n");
printf("enter new name, age and average : ");
scanf("%s %d %f", s.name, &s.age, &s.average);
//‫پرش بر روی اطالعات دانشجوی قبلی‬
fseek(studentFile, -1L * sizeof(student), SEEK_CUR);
//‫نوشتن اطالعات دانشجوی جدید بر روی اطالعات دانشجوی قبلی‬
fwrite(&s, sizeof(student), 1, studentFile) ;
}
fclose(studentFile);
}
‫‪ 13-5‬خواندن و نوشتن آرایهها در فایل‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫یکی دیگر از مسائلی که ممکن است با آن مواجه شوید‪ ،‬خواندن یا نوشتن کل‬
‫محتویات یک آرایه در یک فایل است‪.‬‬
‫به عنوان یک مثال ساده فرض کنید یک آرایه از اعداد اعشاری به صورت زیر‬
‫تعریف شده است‪:‬‬
‫; ]‪float data[100‬‬
‫پس از آنکه در برنامه به نحوی به این آرایه مقداردهی کرده ایم‪ ،‬قصد داریم آن‬
‫را به طور کامل در یک فایل ذخیره کنیم‪.‬‬
‫در اینجا نیز نحوه ذخیره آرایه دریک فایل متنی با یک فایل دودویی متفاوت‬
‫است‪.‬‬
‫برای ذخیره یک آرایه در یک فایل متنی‪ ،‬باید هریک از عناصر آرایه را به طور‬
‫جداگانه در فایل نوشت‪ .‬بنابراین برای این کار نیاز به استفاده از یک حلقه داریم‪.‬‬
‫قطعه برنامه زیر نحوه انجام این کار را نشان میدهد‪:‬‬
‫;)"‪outFile = fopen("data.txt", "wt‬‬
‫)‪for (i=0; i<100; i++‬‬
‫; )]‪fprintf(outFile, "%f ", data[i‬‬
‫‪ 13-5‬خواندن و نوشتن آرایهها در فایل‬
‫‪‬‬
‫‪‬‬
‫خواندن آرایه از یک فایل متنی نیز به همین صورت انجام میپذیرد‪ .‬یعنی عناصر آرایه باید‬
‫به صورت تک به تک از آرایه خوانده شوند‪.‬‬
‫به عنوان مثال قطعه برنامه زیر مجددا آرایه ‪ data‬را از همان فایل میخواند‪:‬‬
‫;)"‪inFile = fopen("data.txt", "rt‬‬
‫)‪for (i=0; i<100; i++‬‬
‫; )]‪fscanf(inFile, "%f", &data[i‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫اما برای نوشتن یک آرایه در یک فایل دودویی‪ ،‬میتوان آن را با استفاده از تابع ‪ fwrite‬به‬
‫صورت یکجا در فایل نوشت و نیازی به استفاده از حلقه نمیباشد‪.‬‬
‫برای این کار کافی است در هنگام فراخوانی تابع ‪ ،fwrite‬نام آرایه را به تنهایی (بدون‬
‫استفاده از عالمت &) به عنوان آرگومان اول و تعداد عناصر آرایه را به عنوان آرگومان سوم‬
‫(که مشخص کننده تعداد دادهها میباشد) ارسال نماییم‪.‬‬
‫به عنوان مثال‪ ،‬قطعه برنامه زیر آرایه ‪ data‬را در یک فایل دودویی مینویسد‪:‬‬
‫;)"‪outFile = fopen("data.dat", "wb‬‬
‫;)‪fwrite(data, sizeof(float), 100, outFile‬‬
‫‪ 13-5‬خواندن و نوشتن آرایهها در فایل‬
‫‪‬‬
‫‪‬‬
‫خواندن یک آرایه از یک فایل دودویی نیز به طور مشابه و فقط با یک بار‬
‫فراخوانی تابع ‪ fread‬انجام میشود‪.‬‬
‫قطعه برنامه زیر آرایه ‪ data‬را از همان فایل دودویی باال میخواند‪:‬‬
‫;)"‪inFile = fopen("data.dat", "rb‬‬
‫;)‪fread(data, sizeof(float), 100, inFile‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫موارد فوق برای سایر نوع داده ها‪ ،‬از جمله نوع دادههای تعریف شده‬
‫توسط کاربر (مانند ساختارها) نیز برقرار است‪.‬‬
‫به عنوان مثال اگر ساختاری مانند ساختار دانشجو را تعریف کرده‬
‫باشید‪ ،‬میتوانید یک آرایه از نوع دانشجو را به طور یکجا در یک فایل‬
‫دودویی بنویسید و یا از آن بخوانید‪.‬‬
‫مثال سیستم تصحیح آزمون از سایت مطالعه شود‪.‬‬
‫مبانی کامپیوتر و برنامه سازی‬
‫فصل‌چهاردهم‌‪ :‬اشاره‌گرها‬
‫مدرس‌‪ :‬رضا‌رمضانی‬
‫‪ 14‬اشاره گرها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫اشاره گرها یکی از قدرتمند ترین جنبههای زبان ‪ C‬هستند‪.‬‬
‫اشاره گرها امکان پشتیبانی از مواردی همچون تخصیص حافظه پویا‪،‬‬
‫ایجاد لیستهای پیوندی و ‪ ...‬را به برنامهنویس میدهند‪.‬‬
‫اما همیشه از مبحث اشاره گرها بعنوان یک موضوع پیچیده و مشکل‬
‫یاد میگردد‪ .‬البته این مسئله تا حدی درست است چرا که کار با‬
‫اشاره گرها مستلزم دقت بسیار باالیی است‪.‬‬
‫معموال امکان وجود خطاهای منطقی در برنامههایی که از اشاره گر‬
‫استفاده میکنند‪ ،‬باال است و پیدا کردن محل خطا و دلیل آن نیز‬
‫بسیار مشکل است‪.‬‬
‫بنابراین در هنگام استفاده از اشاره گرها‪ ،‬برنامه باید با دقت نوشته‬
‫شده و سپس اشکال زدایی گردد‪.‬‬
‫در این فصل‪ ،‬ابتدا مفاهیم آدرس و اشاره گر را توضیح داده و سپس‬
‫به بررسی کاربردهای اشاره گرها خواهیم پرداخت‪.‬‬
‫‪ 14-1‬آدرسهای حافظه‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫همانطور که میدانید یک برنامه به همراه متغیرهای آن برای اجرا باید وارد‬
‫حافظه اصلی گردند‪.‬‬
‫حافظه اصلی از تعدادی بایت تشکیل شده است که هر بایت دارای یک آدرس‬
‫منحصر بفرد است‪ .‬این آدرسها از صفر شروع شده و به ترتیب تا آخرین بایت‬
‫حافظه ادامه مییابد‪.‬‬
‫بعنوان مثال اگر حافظه کامپیوتر شما ‪ 64 KB‬باشد‪ ،‬آدرس بایتهای حافظه از ‪0‬‬
‫تا ‪ 65535‬خواهد بود‪.‬‬
‫هنگامی که برنامه خود را اجرا میکنید‪ ،‬قسمتی از حافظه اصلی به برنامه‬
‫شما تخصیص پیدا میکند‪ .‬بخشی از این حافظه به توابع برنامه و بخش دیگر‬
‫به متغیرها تخصیص مییابد‪.‬‬
‫در حقیقت با تعریف هر متغیر در برنامه‪ ،‬بسته به نوع آن‪ ،‬قسمتی از حافظه به‬
‫آن تخصیص مییابد که آدرس اولین بایت تخصیص یافته به آن را‪ ،‬آدرس آن متغیر‬
‫میگوییم‪.‬‬
‫در زبانهای سطح پایین (زبان ماشین)‪ ،‬از آدرس یک متغیر برای دسترسی به آن‬
‫استفاده میگردد‪ .‬اما از آنجا که کار با آدرسها بسیار مشکل است‪ ،‬در زبانهای‬
‫سطح باال مانند ‪ ،C‬به هر متغیر یک نام نمادین نسبت داده شده و سپس از آن‬
‫نام برای دسترسی به متغیر استفاده میگردد‪.‬‬
‫‪ 14-1‬آدرسهای حافظه‬
‫‪‬‬
‫بعنوان مثال دستور زیر را در نظر بگیرید‪:‬‬
‫;‪float average‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫این دستور ‪ 4‬بایت از حافظه را به متغیر ‪ average‬تخصیص میدهد‪.‬‬
‫بعنوان مثال فرض کنید بایتهای موجود در آدرسهای ‪ 102 ،101 ،100‬و ‪ 103‬به این‬
‫متغیر نسبت داده شوند‪ .‬در این حالت آدرس متغیر ‪ average‬برابر ‪( 100‬اولین‬
‫بایت تخصیص یافته به آن) درنظر گرفته میشود‪.‬‬
‫کامپایلر زبان ‪ C‬در یک جدول ویژه‪ ،‬نام‪ ،‬نوع و آدرس کلیه متغیرها را نگهداری‬
‫میکند‪ .‬اکنون برنامهنویس میتواند در برنامه خود فقط از نام متغیر استفاده‬
‫نماید‪ .‬در هنگام تبدیل برنامه به زبان ماشین‪ ،‬کامپایلر با مراجعه به جدول‬
‫مذکور‪ ،‬آدرس هر متغیر را استخراج کرده و از آن برای دسترسی به متغیر‬
‫استفاده مینماید‪.‬‬
‫بنابراین در حالت عادی‪ ،‬یک برنامهنویس زبان ‪ ،C‬نیازی به دانستن آدرس‬
‫متغیرها ندارد و میتواند عملیات خود را تنها از طریق نام نمادین متغیرها انجام‬
‫دهد‪.‬‬
‫اما در برخی موارد و برای نوشتن برنامههای پیشرفته‪ ،‬نیاز به دانستن آدرس‬
‫یک متغیر در حافظه داریم‪ .‬در این صورت میتوان از عملگر ویژهای که برای‬
‫بدست آوردن آدرس یک متغیر در زبان ‪ C‬در نظر گرفته شده است‪ ،‬استفاده‬
‫نمود‪.‬‬
‫‪ 14-2‬عملگر آدرس )&(‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫برنامهنویسان میتوانن با استفاده از عملگر آدرس یعنی & قبل از نام متغیر‪،‬‬
‫آدرس آن متغیر را به دست آورند‪.‬‬
‫بعنوان مثال ‪ &average‬مشخص کننده آدرس متغیر ‪ average‬است‪.‬‬
‫می توان با استفاده از تابع ‪ printf‬آدرس یک متغیر را نیز چاپ کرد‪ .‬برای این کار‬
‫باید از مشخصه تبدیل ‪ %p‬در رشته تبدیل استفاده نمود‪ .‬به مثال زیر توجه‬
‫نمایید‪:‬‬
‫{ )(‪void main‬‬
‫;‪int a = 25‬‬
‫;‪float b = 110.46‬‬
‫; )‪printf("a=%d and address of a=%p\n",a, &a‬‬
‫;)‪printf("b=%f and address of b = %p", b, &b‬‬
‫}‬
‫‪a=25 and address of a = FFF4‬‬
‫‪b=110.460000 and address of b = FFF0‬‬
(&) ‫ عملگر آدرس‬14-2
Memory
0
1
…
FFEF
FFF0
FFF1
b
FFF2
FFF3
FFF4
FFF5
a
…
‫‪ 14-3‬متغیرهای اشاره گر‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫یک متغیر اشاره گر یا بطور خالصه اشاره گر‪ ،‬متغیری است که در خود یک‬
‫آدرس از حافظه را نگهداری میکند‪.‬معموال این آدرس‪ ،‬موقعیت یک متغیر دیگر‬
‫در حافظه است‪.‬‬
‫شکل کلی تعریف یک اشاره گر بصورت زیر است‪:‬‬
‫; >‪<type> *<var-name‬‬
‫بعنوان مثال دستور زیر ‪:‬‬
‫;‪int *ptr‬‬
‫متغیر ‪ ptr‬را از نوع اشاره گر به یک عدد صحیح معرفی مینماید‪.‬‬
‫بعبارت بهتر‪ ،‬متغیر ‪ ptr‬به مکانی از حافظه اشاره میکند که در آن یک عدد‬
‫صحیح ذخیره شده است‪.‬‬
‫البته از آنجا که هنوز متغیر ‪ ptr‬مقداردهی نشده است‪ ،‬حاوی یک آدرس بی‬
‫معنی است‪ .‬پیش از آنکه از این متغیر استفاده شود‪ ،‬باید آن را مقدار دهی‬
‫نمود‪.‬‬
‫مقدار متغیر ‪ ptr‬میتواند آدرس هر متغیر صحیحی باشد‪ .‬بعنوان مثال تکه برنامه‬
‫زیر این متغیر را مقدار دهی مینماید‪:‬‬
‫; ‪int a‬‬
‫;‪ptr = &a‬‬
‫ متغیرهای اشاره گر‬14-3
:‫به برنامه زیر توجه کنید‬
void main() {
int *ptr;
int a;
a = 20;
ptr = &a ;
printf("a=%d and address of a=%p", a, &a) ;
printf("ptr=%p and address of ptr=%p", ptr, &ptr);
}
a=20 and address of a=FFF2
ptr=FFF2 and address of ptr=FFF4

‫ متغیرهای اشاره گر‬14-3
0
…
FFF2
20
FFF3
FFF4
FFF2
FFF5
…
a
ptr
ptr
a
20
‫‪ 14-3‬متغیرهای اشاره گر‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫اکنون این سوال مطرح میگردد که آیا میتوان از طریق آدرس یک‬
‫متغیر‪ ،‬به مقدار آن دسترسی پیدا کرد؟‬
‫بعنوان مثال‪ ،‬در برنامه باال‪ ،‬متغیر ‪ ptr‬به متغیر ‪ a‬اشاره میکند‪ ،‬بنابراین‬
‫آیا میتوان از طریق اشاره گر ‪ ptr‬به مقدار متغیر ‪ a‬دسترسی پیدا نمود؟‬
‫برای دسترسی به محتوای یک متغیر از طریق آدرس آن میتوان از‬
‫عملگر یکانی * که به آن عملگر دسترسی غیر مستقیم یا عملگر‬
‫محتوا نیز گفته میشود‪ ،‬استفاده نمود‪.‬‬
‫این عملگر‪ ،‬قبل از یک اشاره گر استفاده میشود و مقدار متغیری را که‬
‫آن اشاره گر به آن اشاره میکند‪ ،‬باز میگرداند‪.‬‬
‫بعنوان مثال در برنامه فوق‪ ،‬برای دسترسی به مقدار متغیر ‪ a‬میتوان از‬
‫عبارت ‪*ptr‬استفاده نمود‪ .‬بعبارت بهتر‪ *ptr ،‬برابر ‪ 20‬خواهد بود‪.‬‬
‫ متغیرهای اشاره گر‬14-3
void main() {
int *ptr;
int a, b;
a =10;
b = 20;
ptr = &a ;
printf("a=%d b=%d and *ptr=%d\n", a, b, *ptr) ;
*ptr = 50;
printf("a=%d b=%d and *ptr=%d\n", a, b, *ptr) ;
ptr = &b;
(*ptr) ++ ;
printf("a=%d b=%d and *ptr=%d\n", a, b, *ptr) ;
}
a=10 b=20 and *ptr=10
a=50 b=20 and *ptr=50
a=50 b=21 and *ptr=21
‫‪ 14-3‬متغیرهای اشاره گر‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫اندازه یک متغیر اشاره گر بستگی به اندازه آدرسها در کامپیوتر و سیستم‬
‫عاملی دارد که از آن استفاده میکنید و ممکن است ‪ 2‬یا ‪ 4‬بایت باشد‪.‬‬
‫ممکن است این نکته به ذهن شما برسد که با توجه به اینکه در هرحال اندازه‬
‫آدرسها در یک کامپیوتر و سیستم عامل خاص ثابت است‪ ،‬بنابراین اندازه کلیه‬
‫اشاره گرها با یکدیگر برابر است‪.‬‬
‫بعبارت بهتر تفاوتی بین اندازه یک اشاره گر به کاراکتر )*‪ (char‬با یک اشاره گر‬
‫به عدد اعشاری )*‪ (float‬نیست و اندازه هردو برابر است‪.‬‬
‫بنابراین چرا فقط از یک نوع داده مشترک برای تعریف کلیه اشاره گرها (مانند‬
‫نوع داده ‪ )pointer‬استفاده نکنیم؟‬
‫جواب این است که کامپایلر باید از نوع دادهای که اشاره گر به آن اشاره‬
‫میکند‪ ،‬مطلع باشد‪ .‬بعنوان نمونه اگر به برنامه فوق دقت کنیم‪ ،‬در مییابیم‬
‫هنگامی که کامپایلر به دستوری مانند ;‪ (*ptr) ++‬میرسد‪ ،‬باید از تعداد بایتهای‬
‫متغیری که ‪ ptr‬به آن اشاره میکند مطلع باشد تا بتواند عملیات الزم را انجام‬
‫دهد‪.‬‬
‫از آنجا که ‪ ptr‬بصورت اشاره گر به عدد صحیح معرفی شده است‪ ،‬بنابراین‬
‫کامپایلر متوجه میگردد که در آدرسی که ‪ ptr‬به آن اشاره میکند‪ ،‬یک عدد‬
‫صحیح قرار دارد و به تعداد بایتهای یک عدد صحیح عملیات الزم را انجام‬
‫میدهد‪.‬‬
‫‪ 14-3‬متغیرهای اشاره گر‬
‫‪ ‬متغیرهایی که تاکنون استفاده نمودیم‪ ،‬از نوع اشاره گر به عدد صحیح‬
‫بودند‪ .‬اما میتوان اشاره گرهایی به سایر نوع دادههای زبان ‪ C‬نیز‬
‫تعریف کرد‪.‬‬
‫‪ ‬بعنوان مثال به موارد زیر توجه کنید‪:‬‬
‫اشاره گر به کاراکتر ‪char *charPtr; //‬‬
‫اشاره گر به عدد اعشاری ‪float *floatPtr; //‬‬
‫اشاره گر به ساختار دانشجو ‪student *stdPtr; //‬‬
‫‪‬‬
‫توجه کنید که فضای تخصیص یافته به هریک از اشاره گرهای فوق با‬
‫یکدیگر برابر میباشد‪.‬‬
‫‪ 14-3‬متغیرهای اشاره گر‬
‫‪‬‬
‫در هنگام کار با اشاره گرها به نکات زیر توجه کنید‪:‬‬
‫‪‬‬
‫قبل از کار با یک متغیر اشاره گر مطمئن شوید که آن را‬
‫مقداردهی کرده اید‪ .‬چنانچه یک اشاره گر مقداردهی نشده‬
‫باشد‪ ،‬دارای یک مقدار بی معنی است که ممکن است آدرس‬
‫یک مکان حساس از حافظه باشد‪.‬‬
‫در چنین حالتی ممکن است استفاده از این اشاره گر باعث ایجاد‬
‫مشکل در اجرای برنامه گردد‪.‬‬
‫معموال به برنامهنویسان توصیه میگردد که به محض تعریف یک‬
‫اشاره گر‪ ،‬آن را برابر ‪ NULL‬قرار دهند‪ NULL .‬یک ثابت تعریف‬
‫شده در زبان ‪ C‬است که برابر ‪ 0‬میباشد‪ .‬این کار باعث میشود‬
‫که اشاره گر مورد نظر به مکان خاصی از حافظه اشاره نکند‪.‬‬
‫بنابراین چنانچه اشتباها پیش از مقداردهی‪ ،‬استفاده گردد‬
‫مشکل خاصی پیش نخواهد آمد‪.‬‬
‫‪ 14-3‬متغیرهای اشاره گر‬
‫‪ ‬برای تعریف یک متغیر اشاره گر‪ ،‬دو روش وجود دارد‪ .‬برخی عالمت * را‬
‫چسبیده به نوع داده مینویسند و عدهای دیگر آن را به نام متغیر‬
‫میچسبانند‪ .‬به تفاوت این دو توجه کنید‪:‬‬
‫;‪int* ptr‬‬
‫;‪int *ptr‬‬
‫هردو روش از نظر نحوی درست هستند‪ ،‬اما پیشنهاد میگردد که از‬
‫روش دوم استفاده نمایید‪ ،‬یعنی عالمت * به نام متغیر بچسبد‪ .‬دلیل‬
‫این مسئله آن است که هنگامیکه قصد تعریف چندین اشاره گر در یک‬
‫دستور را دارید‪ ،‬باید برای هریک بطور جداگانه از عالمت * استفاده‬
‫نمایید‪.‬‬
‫بعنوان مثال فرض کنید قصد داریم ‪ 3‬متغیر ‪ b ،a‬و ‪ c‬را از نوع اشاره گر به‬
‫عدد صحیح تعریف نماییم‪ .‬دستور زیر نحوه انجام این کار بصورت صحیح را‬
‫نشان میدهد‪:‬‬
‫; ‪int *a, *b, *c‬‬
‫‪// this is correct‬‬
‫اما ممکن است یک برنامهنویس به اشتباه از دستور زیر استفاده نماید‪:‬‬
‫‪int* a, b, c; //this is incorrect‬‬
‫‪ 14-3‬متغیرهای اشاره گر‬
‫‪ ‬به تفاوت معنای عملگر * در قسمتهای مختلف برنامه توجه کنید‪.‬‬
‫اگر از عملگر * در هنگام تعریف یک متغیر استفاده شود‪ ،‬به این‬
‫معنا است که متغیر از نوع اشاره گر است‪.‬‬
‫اما در سایر قسمتهای برنامه ممکن است به معنای عملگر محتوا‬
‫(اگر قبل از یک اشاره گر استفاده شود) و یا عملگر ضرب باشد‪.‬‬
‫متاسفانه یکسان بودن عملگر ضرب و محتوا گاهی باعث‬
‫سردرگمی برنامهنویسان مبتدی میگردد‪.‬‬
‫‪ ‬ممکن است این سوال به ذهنتان رسیده باشد که آیا یک متغیر‬
‫میتواند آدرس یک متغیر اشاره گر را در خود جای دهد؟ پاسخ‬
‫مثبت است و به چنین متغیری اشاره گر به اشاره گر گفته‬
‫میشود! به مثال زیر توجه کنید‪:‬‬
‫;‪int a = 10‬‬
‫;‪int *p = &a‬‬
‫;‪int **q = &p‬‬
‫‪ 14-4‬اعمال مجاز بر روی اشاره گرها‬
‫‪ ‬اشاره گرها میتوانند در یک عبارت محاسباتی استفاده گردند‪.‬‬
‫‪ ‬با این وجود فقط دو عمل محاسباتی برای آنها تعریف شده است‪ :‬اعمال جمع و‬
‫تفریق‪ .‬برنامهنویس میتواند یک اشاره گر را با یک عدد صحیح‪ ،‬جمع یا تفریق‬
‫نماید‪.‬‬
‫‪ ‬اما معنای عمل جمع و تفریق بر روی اشاره گرها کمی متفاوت با سایر نوع‬
‫دادهها است‪.‬‬
‫‪ ‬فرض کنید متغیر ‪ p‬یک اشاره گر به اعداد اعشاری )* ‪ (float‬است که حاوی‬
‫مقدار ‪ 100‬است (یا به عبارت بهتر به آدرس ‪ 100‬حافظه اشاره میکند)‪ .‬دستور‬
‫زیر‪ ،‬یک دستور مجاز در زبان ‪ C‬است‪:‬‬
‫;‪p = p + 1; // or p++‬‬
‫‪ ‬اما نتیجه آن ممکن است برخالف انتظار شما باشد‪ ،‬چرا که پس از اجرای این‬
‫دستور مقدار متغیر ‪ p‬برابر ‪ 104‬خواهد شد!‬
‫‪ ‬چرا که در زبان ‪ C‬این دستور به این معنا است که اشاره گر باید به عدد‬
‫اعشاری بعدی اشاره نماید‪ .‬از آنجا که اندازه هر عدد اعشاری ‪ 4‬بایت است‪،‬‬
‫بنابراین با هربار افزایش متغیر ‪ ،p‬مقدار آن ‪ 4‬بایت افزایش خواهد یافت‪.‬‬
‫‪ 14-4‬اعمال مجاز بر روی اشاره گرها‬
‫‪ ‬همین مسئله برای کاهش نیز برقرار است‪ .‬بعنوان مثال چنانچه مقدار متغیر ‪p‬‬
‫برابر ‪ 100‬باشد‪ ،‬با اجرای دستور زیر‪:‬‬
‫;‪p = p - 5‬‬
‫مقدار ‪ p‬برابر ‪ 80‬خواهد شد ‪.‬‬
‫‪ ‬موارد فوق در مورد سایر نوع دادهها نیز برقرار است‪ .‬به این معنا که با هربار‬
‫افزایش یک متغیر اشاره گر به یک نوع داده خاص‪ ،‬اشاره گر مزبور به ابتدای‬
‫عنصر بعدی از همان نوع داده در حافظه اشاره خواهد کرد‪.‬‬
‫‪ ‬بنابراین باید مقدار اشاره گر مورد نظر به اندازه تعداد بایتهای نوع داده مورد نظر‬
‫افزایش یابد‪.‬‬
‫‪ ‬خوشبختانه این عمل توسط خود زبان ‪ ،C‬به طرز هوشمندانهای صورت‬
‫میپذیرد‪.‬‬
‫‪ ‬در مورد کاهش یک متغیر اشاره گر نیز عملیات مشابهی صورت میپذیرد‪.‬‬
‫‪ ‬البته در مورد اشاره گرهایی که از نوع اشاره گر به کاراکتر باشند‪ ،‬اعمال جمع و‬
‫تفریق بصورت عادی انجام میگردند‪ ،‬چرا که اندازه هر کاراکتر برابر ‪ 1‬بایت‬
‫است‪.‬‬
‫‪ 14-4‬اعمال مجاز بر روی اشاره گرها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫ممکن است این سوال به ذهن برسد که این عملیات چه کاربردی‬
‫دارند؟‬
‫کاربرد آنها در مواردی است که برنامهنویس یک مجموعه از دادههای‬
‫همنوع بصورت پشت سرهم در حافظه داشته باشد (همانند یک آرایه از‬
‫اعداد صحیح)‪.‬‬
‫در این صورت کافی است یک اشاره گر به آدرس ابتدای این دادهها‬
‫داشته باشیم‪ .‬اکنون با هربار افزایش مقدار این اشاره گر‪ ،‬آن را بر روی‬
‫داده بعدی منتقل نماییم‪.‬‬
‫بجز جمع و تفریق یک اشاره گر با یک عدد صحیح‪ ،‬هیچ عمل دیگری بر‬
‫روی اشاره گرها تعریف نشده است‪ .‬بعنوان مثال شما اجازه جمع یک‬
‫اشاره گر با یک عدد اعشاری و یا ضرب و تقسیم آن بر یک عدد دلخواه‬
‫دیگر را ندارید‪.‬‬
‫البته میتوانید مقدار یک اشاره گر را از یک اشاره گر همنوع آن تفریق‬
‫نمایید‪ .‬در اینصورت حاصل برابر تعداد عناصری از همان نوع خواهد بود‬
‫که بین این دو اشاره گر قرار دارد‪ .‬اما توجه کنید که اجازه جمع دو اشاره‬
‫گر را با یکدیگر ندارید‪.‬‬
‫‪ 14-4‬اعمال مجاز بر روی اشاره گرها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫عمل مجاز دیگر بر روی اشاره گرها‪ ،‬مقایسه آنها با یکدیگر است‪.‬‬
‫کلیه عملگر های مقایسه ای مانند <‪ > ،‬و == بر روی اشاره گرها عمل‬
‫می کنند‪.‬‬
‫در حقیقت این عملگرها‪ ،‬آدرسهای ذخیره شده در اشاره گرها را با‬
‫یکدیگر مقایسه می کنند‪.‬‬
‫بعنوان مثال اگر اشاره گر ‪ p‬حاوی آدرس ‪ 100‬و اشاره گر ‪ q‬حاوی آدرس‬
‫‪ 200‬باشد‪ ،‬آنگاه عبارت )‪ (p < q‬برابر درست ارزیابی خواهد شد‪.‬‬
‫اما مسلما مقایسه دو متغیر اشاره گر وقتی منطقی است که آن دو به‬
‫نحوی با یکدیگر در ارتباط باشند‪.‬‬
‫بعنوان مثال هنگامی که دو اشاره گر به عناصر یک آرایه اشاره کنند‪ ،‬با‬
‫توجه به اینکه عناصر آرایه در مکانهای پشت سرهم حافظه ذخیره می‬
‫گردند‪ ،‬مقایسه آنها منطقی خواهد بود‪ .‬در قسمت بعدی کاربرد‬
‫مقایسه اشاره گرها بررسی خواهد شد‪.‬‬
‫‪ 14-5‬کاربردهای اشاره گرها‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫تاکنون در یافته ایم که یک اشاره گر می تواند آدرس یک‬
‫متغیر دیگر را در خود جای دهد‪ ،‬و بنابراین می توان از‬
‫طریق آن اشاره گر‪ ،‬مقدار متغیر مورد اشاره را تغییر داد‪.‬‬
‫اما این موارد چه کاربردی برای برنامهنویس دارند؟‬
‫در این قسمت سعی می شود کاربردهای اشاره گرها در‬
‫برنامهنویسی پیشرفته مورد بررسی قرار گیرد‪.‬‬
‫‪ 14-5-1‬اشاره گرها و ارسال متغیرها به توابع‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫همانگونه که در فصل مربوط به توابع گفته شد‪ ،‬دو روش برای ارسال آرگومانها‬
‫به توابع وجود دارد‪ :‬فراخوانی توسط مقدار و فراخوانی توسط ارجاع‪.‬‬
‫اما حقیقت آن است که در ‪ C‬استاندارد‪ ،‬فقط روش فراخوانی توسط مقدار وجود‬
‫دارد و روش فراخوانی توسط ارجاع تنها در نسخه های جدیدتر ‪( C‬در واقع در‬
‫زبان ‪ )C++‬پیاده سازی شده است‪.‬‬
‫بنابراین سوال اینجا است که در نسخه های قدیمی زبان ‪ ،C‬چگونه می‬
‫توانستیم مقدار آرگومان ارسالی به یک تابع را تغییر دهیم؟‬
‫گفتیم که در نسخه های اولیه زبان ‪ ،C‬تنها روش فراخوانی توسط مقدار وجود‬
‫داشت‪ .‬به این معنا که برای ارسال آرگومانها به یک تابع‪ ،‬یک کپی از آنها تهیه‬
‫شده و به تابع ارسال می شد‪ .‬در نتیجه چنانچه تغییری بر روی آرگومانها در‬
‫داخل تابع صورت بپذیرد‪ ،‬این تغییر به تابع فراخواننده منتقل نمی گردد‪.‬‬
‫به همین دلیل برنامهنویسان‪ ،‬هنگامی که قصد داشتند مقدار یک آرگومان را در‬
‫داخل یک تابع تغییر دهند‪ ،‬آدرس آن آرگومان را بجای خودش به تابع ارسال می‬
‫نمودند‪.‬‬
‫همانطور که قبال نیز گفته شد‪ ،‬چنانچه آدرس یک متغیر را داشته باشیم‪ ،‬می‬
‫توانیم از طریق عملگر دسترسی غیر مستقیم یا * به مقدار آن دسترسی پیدا‬
‫کنیم‪ .‬بنابراین برنامهنویس می توانست در داخل تابع‪ ،‬از طریق آدرس آرگومان‪،‬‬
‫به مقدار اصلی آن دسترسی پیدا کرده و آن را تغییر دهد‪.‬‬
‫‪ 14-5-1‬اشاره گرها و ارسال متغیرها به توابع‬
‫‪‬‬
‫برای روشن شدن موضوع‪ ،‬به پیاده سازی تابع ‪ swap‬با استفاده از‬
‫این روش دقت کنید‪ .‬اگر به یاد داشته باشید‪ ،‬این تابع دو متغیر صحیح‬
‫را بعنوان ورودی دریافت کرده و جای آن دو را با یکدیگر عوض می کرد‪.‬‬
‫>‪#include <stdio.h‬‬
‫{ )‪void swap(int *a, int *b‬‬
‫;‪int temp‬‬
‫;‪temp = *a‬‬
‫;‪*a = *b‬‬
‫;‪*b = temp‬‬
‫}‬
‫ اشاره گرها و ارسال متغیرها به توابع‬14-5-1
void main() {
int x, y;
x = 10;
y = 20;
printf("now x=%d and y=%d\n", x, y);
swap(&x, &y) ;
printf("after swapping x=%d and y=%d\n", x, y);
}
now x=10 and y=20
after swapping x=20 and y=10
‫‪ 14-5-1‬اشاره گرها و ارسال متغیرها به توابع‬
‫)(‪swap‬‬
‫‪a‬‬
‫‪*a = x‬‬
‫‪FFF4‬‬
‫‪b‬‬
‫‪*b = y‬‬
‫‪FFF2‬‬
‫)(‪main‬‬
‫‪x‬‬
‫‪FFF4 10‬‬
‫‪y‬‬
‫‪FFF2 20‬‬
‫‪ 14-5-1‬اشاره گرها و ارسال متغیرها به توابع‬
‫‪ ‬همانگونه که می بینید این روش نسبتا پیچیده است و برنامهنویس را مجبور‬
‫می کند عالوه بر ارسال آدرس متغیرها در هنگام فراخوانی تابع‪ ،‬در داخل تابع‬
‫نیز مرتبا از عملگر * استفاده نماید‪.‬‬
‫‪ ‬به همین دلیل در نسخه جدید زبان ‪ C‬یعنی ‪ ،C++‬روش فراخوانی توسط ارجاع‬
‫به زبان اضافه گردید تا از پیچیدگی آن کاسته شود‪.‬‬
‫‪ ‬اما توجه کنید که در تمامی کتابخانه های استاندارد زبان ‪ C‬از همان روش قبلی‬
‫استفاده می گردد‪ .‬بعنوان مثال اگر به یاد داشته باشید‪ ،‬در هنگام فراخوانی‬
‫تابع ‪ scanf‬باید آدرس متغیرهایی که قصد خواندن آنها را داریم‪ ،‬به تابع ارسال‬
‫شوند‪.‬‬
‫‪ ‬بعنوان مثال برای دریافت یک عدد صحیح مانند ‪ k‬از کاربر‪ ،‬این تابع بصورت زیر‬
‫فراخوانی میگردید‪:‬‬
‫;)‪scanf("%d", &k‬‬
‫‪ ‬اکنون بهتر میتوانید دلیل این مسئله را درک نمایید‪ .‬از آنجا که آرگومانهای‬
‫ارسالی به این تابع‪ ،‬در داخل آن تغییر داده میشوند (چراکه مقدار دریافتی از‬
‫کاربر در آنها قرار داده میشود)‪ ،‬بنابراین آدرس آرگومانها به تابع ارسال میگردد‬
‫تا بتوان مقدار آنها را تغییر داد‪.‬‬
‫‪ ‬سایر توابع کتابخانهای دیگری نیز که آدرس متغیرها را بعنوان ورودی دریافت‬
‫میکنند‪ ،‬دلیل مشابهی برای این کار دارند‪.‬‬
‫‪ 14-5-2‬اشاره گرها و آرایههای یک بعدی‬
‫‪ ‬اشاره گرها رابطه بسیار نزدیکی با آرایههای یک بعدی دارند‪ .‬میدانید‬
‫که در زبان ‪ ،C‬آرایه مجموعهای از داده همنوع است که در خانههای‬
‫متوالی حافظه ذخیره میگردند‪.‬‬
‫‪ ‬اما نکته جالب اینجاست که نام هر آرایه به تنهایی‪ ،‬اشاره گری به اولین‬
‫عنصر آرایه است‪ .‬بعنوان مثال تعریف زیر را در نظر بگیرید‪:‬‬
‫;]‪int data[10‬‬
‫‪ ‬کامپایلر زبان ‪ C‬به محض برخورد به چنین دستوری‪ 10 ،‬مکان متوالی‬
‫حافظه از نوع عدد صحیح را به این آرایه تخصیص داده و آدرس اولین‬
‫مکان را در متغیر ‪ data‬قرار میدهد‪.‬‬
‫‪ ‬بنابراین در حقیقت متغیر ‪ data‬یک اشاره گر به عدد صحیح )* ‪(int‬‬
‫میباشد‪.‬‬
‫‪FFE2‬‬
‫‪data‬‬
‫‪FFE2‬‬
‫‪ 14-5-2‬اشاره گرها و آرایههای یک بعدی‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫از این ویژگی میتوان برای دسترسی به عناصر آرایه استفاده کرد‪.‬‬
‫تا کنون از کروشه و اندیس برای دسترسی به هر عنصر آرایه استفاده‬
‫میکردیم‪ .‬بعنوان مثال برای دسترسی به ششمین عنصر آرایه‪ ،‬از‬
‫]‪ data[5‬استفاده میکردیم‪.‬‬
‫اما میتوان به طریق دیگری نیز به این عنصر دسترسی پیدا کرد‪ .‬از آنجا‬
‫که ‪ data‬به اولین عنصر آرایه اشاره میکند‪ ،‬بنابراین با توجه به آنچه که‬
‫راجع به جمع اشاره گرها و اعداد صحیح گفتیم‪ data + 1 ،‬به دومین‬
‫عنصر آرایه اشاره خواهد کرد‪.‬‬
‫فراموش نکنید که زبان ‪ C‬در هنگام افزایش یک اشاره گر به اعداد‬
‫صحیح‪ ،‬بطور خودکار آن را به اندازه تعداد بایتهای یک عدد صحیح افزایش‬
‫میدهد‪.‬‬
‫با یک استدالل مشابه‪ data + 5 ،‬به ششمین عنصر آرایه اشاره خواهد‬
‫کرد‪.‬‬
‫ اشاره گرها و آرایههای یک بعدی‬14-5-2
data
data + 1
data + 2
data + 3
data + 4
data + 5
FFE2
FFE3
FFE4
FFE5
FFE6
FFE7
FFE8
FFE9
FFEA
data[0]
data[1]
data[2]
data[3]
data[4]
FFEB
FFEC
FFED
data[5]
…
data + 9
FFF4
FFF5
data[9]
‫‪ 14-5-2‬اشاره گرها و آرایههای یک بعدی‬
‫‪ ‬حال که توانستیم آدرس عنصر مورد نظر خود را پیدا کنیم‪ ،‬میتوانیم به راحتی‬
‫با استفاده از عملگر دسترسی غیرمستقیم یا *‪ ،‬مقدار آن را به دست آوریم‪.‬‬
‫‪ ‬بعبارت بهتر عبارت )‪ *(data + 5‬معادل با ]‪ data[5‬بوده و هر دو مقدار عنصر‬
‫ششم آرایه را به ما باز میگردانند‪.‬‬
‫‪ ‬به مثال زیر توجه کنید‪:‬‬
‫{ )(‪void main‬‬
‫; }‪int data[10] = {8, 12, 6, -3, 5, 16, -7, 14, -20, 6‬‬
‫;‪int i , sum=0‬‬
‫)‪for (i=0; i<10; i++‬‬
‫; )‪sum += *(data + i‬‬
‫;)‪printf("sum = %d",sum‬‬
‫}‬
‫‪sum = 37‬‬
‫‪ 14-5-2‬اشاره گرها و آرایههای یک بعدی‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫برخی از برنامهنویسان ترجیح میدهند در هنگام کار با آرایهها از اندیس استفاده نکنند‪،‬‬
‫بلکه کلیه عملیات خود را منحصرا با اشاره گرها انجام دهند‪.‬‬
‫برای این کار کافی است یک متغیر اشاره گر تعریف کرده و آدرس ابتدای آرایه را در آن‬
‫قرار دهیم‪ ،‬سپس این اشاره گر را بر روی آرایه به سمت جلو حرکت داده و عملیات مورد‬
‫نظر را انجام دهیم‪.‬‬
‫برنامه زیر از این روش برای محاسبه مجموع عناصر آرایه ‪ data‬استفاده میکند‪:‬‬
‫{ )(‪void main‬‬
‫; }‪int data[10] = {8, 12, 6, -3, 5, 16, -7, 14, -20, 6‬‬
‫;‪int sum=0‬‬
‫; ‪int *ptr‬‬
‫)‪for (ptr = data; ptr < data+10; ptr++‬‬
‫; ‪sum += *ptr‬‬
‫;)‪printf("sum = %d",sum‬‬
‫}‬
‫‪sum = 37‬‬
‫‪ 14-5-3‬ارسال آرایهها به توابع‬
‫‪ ‬اجازه دهید بحث را با یک مثال ساده شروع نماییم‪.‬‬
‫‪ ‬فرض کنید از ما خواسته شده است تابعی بنویسیم که یک آرایه را‬
‫دریافت و میانگین عناصر آن را بازگرداند‪ .‬باتوجه به آنچه در مباحث قبلی‬
‫داشتیه ایم‪ ،‬این تابع بصورت زیر نوشته میشود‪:‬‬
‫{ )‪float computeaverage(int A[], int n‬‬
‫;‪float average = 0.0‬‬
‫;‪int i‬‬
‫)‪for (i=0; i<n; i++‬‬
‫;]‪average += A[i‬‬
‫;‪average /= n‬‬
‫;)‪return ( average‬‬
‫}‬
‫‪ 14-5-3‬ارسال آرایهها به توابع‬
‫‪ ‬اکنون فرض کنید قصد داریم تابعی بنویسیم که با استفاده از این تابع میانگین‬
‫آرایهای بنام ‪ data‬را محاسبه نماید‪ .‬به این برنامه توجه کنید‪:‬‬
‫>‪#include <stdio.h‬‬
‫{ )(‪void main‬‬
‫; }‪int data[10] = {8, 12, 6, -3, 5, 16, -7, 14, -20, 6‬‬
‫;‪float average‬‬
‫;)‪average = computeAverage(data, 10‬‬
‫;)‪printf("average = %f", average‬‬
‫}‬
‫‪ ‬همانطور که قبال نیز گفته شده است‪ ،‬برای ارسال یک آرایه به یک تابع باید از‬
‫نام آن آرایه به تنهایی استفاده نماییم‪.‬‬
‫‪ ‬اما میدانیم که نام هر آرایه به تنهایی‪ ،‬اشاره گری به اولین عنصر آرایه است‪.‬‬
‫‪ ‬بنابراین در هنگام فراخوانی تابع ‪ ،computeAverage‬در حقیقت آدرس اولین عنصر‬
‫آرایه ‪ data‬را به آن ارسال مینماییم‪.‬‬
‫‪ ‬بطور کلی‪ ،‬زبان ‪ C‬در هنگام ارسال آرایهها به توابع‪ ،‬آدرس اولین عنصر آنها را‬
‫ارسال مینماید‪.‬‬
‫‪ 14-5-3‬ارسال آرایهها به توابع‬
‫‪ ‬از آنجا که اولین پارامتر تابع ‪ computeAverage‬در حقیقت آدرس شروع‬
‫یک آرایه را بعنوان ورودی دریافت میکند‪ ،‬بنابراین میتوان تعریف این‬
‫تابع را بصورت زیر نیز نوشت‪:‬‬
‫… { )‪float computeAverage(int *A, int n‬‬
‫‪ ‬البته بسیاری از برنامهنویسان ترجیح میدهند از همان روش قبلی‬
‫برای تعریف پارامتر ‪ A‬استفاده نمایند‪.‬‬
‫‪ ‬اگر به یاد داشته باشید‪ ،‬در فصل آرایهها بیان کردیم که برخالف‬
‫متغیرهای عادی‪ ،‬آرایهها توسط ارجاع به توابع ارسال میگردند‪ .‬اکنون‬
‫دلیل این موضوع برای شما روشنتر شده است‪.‬‬
‫‪ ‬از آنجا که آدرس ابتدای آرایه به تابع ارسال میشود‪ ،‬تابع مورد نظر‬
‫میتواند از طریق این آدرس به کلیه عناصر آرایه اصلی دسترسی پیدا‬
‫نماید و هر گونه انجام تغییرات در آرایه اصلی نیز اعمال خواهد گردید‪.‬‬
‫‪ ‬اما چرا طراحان زبان ‪ C‬تصمیم گرفتند از این روش برای ارسال آرایهها به‬
‫توابع استفاده نمایند؟‬
‫‪ ‬دلیل این مسئله باال رفتن کارایی برنامهها بود‪.‬‬
‫‪ 14-5-3‬ارسال آرایهها به توابع‬
‫‪ ‬اما بعنوان نکته آخر‪ ،‬یک کاربرد بسیار جالب دیگر از اشاره گرها را بررسی‬
‫مینماییم‪.‬‬
‫‪ ‬همانطور که دیدید توابعی مانند ‪computeAverage‬که یک آرایه را بعنوان ورودی‬
‫دریافت میکنند‪ ،‬در حقیقت یک اشاره گر به ابتدای آرایه را دریافت میکنند‪.‬‬
‫حال اگر به جای آدرس ابتدای آرایه اصلی‪ ،‬آدرس مکان دیگری از آن را به این‬
‫توابع ارسال کنیم‪ ،‬چه اتفاقی خواهد افتاد؟‬
‫‪ ‬پاسخ این است که تابع‪ ،‬آدرس دریافتی را بعنوان آدرس ابتدای آرایه فرض کرده‬
‫و عملیات را از همان جا آغاز مینماید‪.‬‬
‫‪ ‬از این ویژگی جالب میتوان برای انجام عملیات موردنظر بر روی قسمتی از‬
‫یک آرایه استفاده کرد‪ .‬بعنوان مثال فرض کنید قصد دارید در برنامه مثال قبل‪،‬‬
‫تنها میانگین دادههای موجود در مکان ‪ 3‬تا ‪ 7‬آرایه ‪ data‬را محاسبه نمایید‪ .‬برای‬
‫اینکار کافی است از دستور زیر استفاده کنید‪:‬‬
‫;)‪average = computeAverage(data+3, 5‬‬
‫‪ ‬می توانستیم این تابع را بصورت زیر نیز فراخوانی نماییم‪:‬‬
‫; )‪average = computeAverage(&data[3], 5‬‬
‫‪ ‬می توانید از این خاصیت جالب در فراخوانی کلیه توابعی که بعنوان ورودی یک‬
‫آرایه را دریافت میکنند‪ ،‬استفاده نموده و فقط قسمتی از آرایه را به تابع ارسال‬
‫نمایید‪.‬‬
‫‪ 14-5-4‬اشاره گرها و رشته ها‬
‫‪ ‬می دانید که در زبان ‪ C‬هر رشته توسط آرایهای از کاراکترها تعریف میشود‪.‬‬
‫بنابراین هر متغیر رشتهای نیز در حقیقت اشاره گری به آدرس شروع آن رشته‬
‫است‪.‬‬
‫‪ ‬بعنوان مثال اگر متغیر ‪ name‬بصورت زیر تعریف شده باشد‪:‬‬
‫; "‪char name[10] = "ali‬‬
‫‪ ‬آنگاه متغیر ‪ name‬یک اشاره گر به کاراکتر است که به مکان عنصر اول رشته‬
‫یعنی '‪ 'a‬اشاره میکند‪.‬‬
‫‪ ‬بنابراین میتوان برای پردازش رشته ها‪ ،‬بجای اندیس از اشاره گرها استفاده‬
‫کرد‪.‬‬
‫‪ ‬استفاده از اشاره گرها برای پردازش رشته ها‪ ،‬در بین برنامهنویسان حرفهای‬
‫بسیار متداول است و به همین دلیل بسیاری از توابع رشتهای با استفاده از‬
‫اشاره گرها نوشته شده اند‪.‬‬
‫‪ ‬در اینجا برای آشنایی بیشتر شما‪ ،‬دو تابع رشتهای که قبال در فصل مربوط به‬
‫رشتهها با استفاده از اندیس نوشته شده اند‪ ،‬با استفاده از اشاره گرها‬
‫بازنویسی میگردند تا بتوانید این دو روش را با یکدیگر مقایسه نمایید‪.‬‬
‫‪ 14-5-4‬اشاره گرها و رشته ها‬
‫‪‬‬
‫‪‬‬
‫اولین تابع‪ strcpy ،‬نام دارد که دو رشته را بعنوان ورودی دریافت و‬
‫رشته دوم را در رشته اول کپی مینماید‪.‬‬
‫نسخه جدید این تابع بصورت زیر است‪:‬‬
‫{ )‪void strcpy(char *str1, char *str2‬‬
‫)‪for (; *str2; str1++, str2++‬‬
‫;‪*str1 = *str2‬‬
‫;'‪*str1 = '\0‬‬
‫}‬
‫‪ 14-5-4‬اشاره گرها و رشته ها‬
‫‪ ‬تابع بعدی که مورد بررسی قرار میدهیم‪ ،‬تابع ‪ strrev‬است‪ .‬این تابع یک رشته‬
‫را دریافت و آن را در داخل خودش معکوس مینماید‪.‬‬
‫‪ ‬پیاده سازی این تابع با اشاره گرها بصورت زیر است‪:‬‬
‫{ )‪void strrev(char *str‬‬
‫;‪char *end‬‬
‫; ‪char temp‬‬
‫; )‪for (end=str, *end, end++‬‬
‫;‪end --‬‬
‫{ )‪for (; str<end; str++, end--‬‬
‫;‪temp = *str‬‬
‫;‪*str = *end‬‬
‫; ‪*end = temp‬‬
‫}‬
‫}‬
‫‪ 14-5-5‬تخصیص حافظه پویا به آرایه ها‬
‫‪ ‬در این قسمت به بررسی راه حل یکی از بزرگترین مشکالت آرایه ها‪،‬‬
‫یعنی ثابت بودن اندازه آنها میپردازیم‪.‬‬
‫‪ ‬حتما به یاد دارید که اندازه آرایهها باید حتما توسط یک مقدار ثابت‬
‫مشخص گردد و این مقدار در زمان اجرا قابل تغییر نیست‪ .‬به همین‬
‫دلیل به این آرایه ها‪ ،‬آرایههای ایستا گفته میشود‪.‬‬
‫‪ ‬راه حل این مشکل‪ ،‬استفاده از آرایههای پویا است‪ .‬برای شروع به‬
‫تعریف آرایه ایستای زیر زیر دقت کنید‪:‬‬
‫;]‪int data[10‬‬
‫‪ ‬همانطور که گفته شده‪ ،‬متغیر ‪ data‬در حقیقت یک اشاره گر به اعداد‬
‫صحیح )* ‪ (int‬است که به اولین عنصر آرایه اشاره مینماید‪.‬‬
‫‪ ‬هنگامی که یک آرایه مانند ‪ data‬تعریف میگردد‪ ،‬ابتدا فضای مورد نظر‬
‫برای آرایه در حافظه تخصیص داده شده و سپس آدرس ابتدای این فضا‬
‫در متغیر ‪ data‬قرار داده میشود‪.‬‬
‫‪ 14-5-5‬تخصیص حافظه پویا به آرایه ها‬
‫‪ ‬اما برای تعریف یک آرایه پویا‪ ،‬ابتدا باید یک اشاره گر به نوع مورد نظر‬
‫آرایه تعریف نمود‪.‬‬
‫‪ ‬بعنوان مثال چنانچه بخواهیم آرایه ‪ data‬را بصورت پویا تعریف کنیم‪ ،‬از‬
‫دستور زیر استفاده مینماییم ‪:‬‬
‫;‪int *data‬‬
‫‪ ‬همانطور که میدانید این دستور تنها یک اشاره گر تعریف مینماید که‬
‫در حال حاضر به مکان مشخصی اشاره نمیکند‪.‬‬
‫‪ ‬اکنون میتوانیم در حین اجرای برنامه و پس از مشخص شدن اندازه‬
‫مورد نیاز آرایه‪ ،‬فضای مورد نظر را در حافظه تخصیص داده و سپس‬
‫آدرس ابتدای آن را در متغیر ‪ data‬قرار دهیم‪.‬‬
‫‪ ‬در این حالت متغیر ‪ data‬تبدیل به یک آرایه معمولی با اندازه موردنظر‬
‫خواهد شد که حتی میتوان با استفاده از اندیس به اعضای آن‬
‫دسترسی پیدا کرد!‬
‫‪ 14-5-5‬تخصیص حافظه پویا به آرایه ها‬
‫‪‬‬
‫اما برای تخصیص فضای مورد نیاز در حافظه اصلی دو روش وجود دارد‪.‬‬
‫‪‬‬
‫‪‬‬
‫روش اول که در کامپایلرهای جدید ‪( C‬و در حقیقت کامپایلرهای ‪ )C++‬استفاده‬
‫میگردد‪ ،‬از عملگری بنام ‪ new‬برای تخصیص حافظه استفاده مینماید‪.‬‬
‫اما روش قدیمی تر از یک تابع کتابخانهای بنام ‪ malloc‬برای این کار استفاده‬
‫مینماید‪.‬‬
‫‪ ‬کامپایلرهای جدید ‪ ،C‬دارای یک عملگر یکانی بنام ‪ new‬هستند که یک نوع داده‬
‫را دریافت و فضایی به اندازه همان نوع داده در حافظه تخصیص داده و آدرس آن‬
‫را باز میگرداند‪ .‬نحوه استفاده از این عملگر بصورت زیر است‪:‬‬
‫>‪new <type‬‬
‫‪ ‬از آنجا که این عملگر آدرس فضای تخصیص داده شده را باز میگرداند‪ ،‬باید‬
‫مقدار بازگشتی آن را به یک متغیر اشاره گر نسبت داد‪ .‬درغیراینصورت حافظه‬
‫تخصیص داده شده گم خواهد شد و دیگر نمیتوان به هیچ صورتی به آن‬
‫دسترسی پیدا کرد‪ .‬چنانچه عملگر ‪ new‬نتواند فضای کافی را تخصیص دهد‪،‬‬
‫مقدار ‪ NULL‬بازخواهد گرداند‪.‬‬
‫‪ ‬بعنوان مثال به دستورات زیر دقت کنید‪:‬‬
‫;‪int *p‬‬
‫;‪p = new int‬‬
‫;‪*p = 100‬‬
‫‪ 14-5-5‬تخصیص حافظه پویا به آرایه ها‬
‫‪ ‬نکته بسیار مهم دیگری که باید به آن اشاره کرد‪ ،‬این است که هر‬
‫فضایی که توسط خود برنامهنویس و با عملگر ‪ new‬تخصیص داده شود‪،‬‬
‫باید توسط عملگر دیگری بنام ‪delete‬باز پس گرفته شود‪.‬‬
‫‪ ‬در صورتیکه این عمل انجام نشود‪ ،‬حافظه تخصیص یافته آزاد نخواهد‬
‫گردید و باعث اتالف حافظه میگردد‪ .‬نحوه استفاده از این عملگر بصورت‬
‫زیر است‪:‬‬
‫>‪delete <pointer‬‬
‫‪ ‬بنابراین برنامهنویس میتواند در هر نقطه از برنامه‪ ،‬در صورت عدم نیاز‬
‫به حافظه تخصیص یافته آن را با استفاده از عملگر ‪ delete‬حذف نماید‪.‬‬
‫‪ ‬بعنوان مثال‪ ،‬در تکه برنامه فوق‪ ،‬پس از اتمام کار میتوانیم از دستور زیر‬
‫استفاده نماییم‪:‬‬
‫;‪delete p‬‬
‫‪ 14-5-5‬تخصیص حافظه پویا به آرایه ها‬
‫‪ ‬اما قدرت واقعی عملگر ‪ new‬در اینجا است که میتواند فضای الزم برای‬
‫تعدادی داده را نیز تخصیص دهد‪.‬‬
‫‪ ‬برای این کار کافی است از دستور زیر استفاده کنید‪:‬‬
‫; ]>‪new <type> [<size‬‬
‫‪ ‬در این حالت عملگر ‪ new‬فضای الزم برای ذخیره >‪ <size‬عدد داده از نوع‬
‫مورد نظر را بصورت پشت سرهم در حافظه تخصیص داده و آدرس اولین‬
‫عنصر آن را باز میگرداند‪.‬‬
‫‪ ‬حال میتوان آدرس بازگردانده شده را دریک اشاره گر قرار داد و آن‬
‫اشاره گر را تبدیل به یک آرایه پویا نمود‪ .‬بعنوان مثال به دستورات زیر‬
‫توجه کنید‪:‬‬
‫;‪int *data‬‬
‫;]‪data = new int[10‬‬
‫;‪data[2] = 34‬‬
‫‪ 14-5-5‬تخصیص حافظه پویا به آرایه ها‬
‫‪ ‬الزم به یاد آوری است که پس از اتمام کار با آرایه ‪ ،data‬باید حافظه‬
‫تخصیص داده شده به آن را آزاد کرد‪ .‬اما برای این کار باید عملگر ‪delete‬‬
‫را بصورت زیر استفاده کرد‪:‬‬
‫;‪delete[] data‬‬
‫‪ ‬عملگر ][ پس از ‪ ،delete‬مشخص میکند که فضای تخصیص داده شده‬
‫به اشاره گر ‪ ،data‬بصورت آرایهای (یعنی بیش از یک داده) بوده است‪.‬‬
‫‪ ‬اما نکته مهمی که در مورد عملگر ‪ new‬وجود دارد این است که الزم‬
‫نیست تعداد دادهها یا همان اندازه آرایه ثابت باشد‪ ،‬بلکه میتوان از یک‬
‫متغیر صحیح نیز برای مشخص نمودن >‪<size‬استفاده کرد‪.‬‬
‫‪ ‬با استفاده از این خاصیت‪ ،‬میتوان اندازه آرایه را در زمان اجرا تعیین‬
‫نمود و به همین دلیل به آنها آرایههای پویا گفته میشود‪.‬‬
‫‪ ‬بعنوان مثال اگر متغیر ‪ ،n‬تعداد عناصر آرایه باشد که از کاربر دریافت‬
‫شده باشد (و یا به هر شکل دیگری محاسبه شده باشد)‪ ،‬میتوان‬
‫نوشت‪:‬‬
‫;]‪data = new int[n‬‬
‫ تخصیص حافظه پویا به آرایه ها‬14-5-5
،‫ مثال) برنامهای بنویسید که شماره دانشجویی و معدل تعدادی دانشجو را دریافت‬
.‫سپس شماره دانشجویانی را که از میانگین کالس بیشتر است را چاپ نماید‬
#include <stdio.h>
void main() {
float *averageList;
long int *idList;
int i, n;
float totalAverage;
printf("please enter number of student : ");
scanf("%d",&n) ;
averageList= new float[n];
idList = new long int[n];
if (!idList || !averageList) {
printf("not enough memory!");
return ;
}
‫ تخصیص حافظه پویا به آرایه ها‬14-5-5
totalAverage = 0;
for (i=0; i<n; i++) {
printf("enter id and average : ");
scanf("%ld %f",&idList[i], &averageList[i]);
// or scanf("%ld %f", idList + i, averageList + i);
totalAverage += averageList[i];
}
totalAverag /= n;
printf("good students :\n");
for (i=0; i<n; i++)
if (averageList[i] > totalAverage)
printf("%ld\n", idList[i]);
delete[] averageList;
delete[] idList;
}
‫‪ 14-5-5‬تخصیص حافظه پویا به آرایه ها‬
‫‪‬‬
‫دو نکته مهم دیگر درمورد آرایههای پویا قابل بررسی است‪.‬‬
‫‪ ‬نکته اول این است که از آنجا که نام یک آرایه پویا‪ ،‬در حقیقت یک اشاره‬
‫گر عادی (غیر ثابت) به ابتدای آرایه است‪ ،‬میتوان مکان آن را تغییر داد‬
‫و یا بطور کلی آن را به جای دیگری اشاره داد‪.‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫اما پیش از انجام هر تغییری‪ ،‬ابتدا مطمئن شوید که اشاره دیگری مکان‬
‫شروع آرایه را در خود داشته باشد‪ ،‬در غیراینصورت مکان شروع آرایه گم‬
‫خواهد گردید‪.‬‬
‫توجه کنید که برای آزاد سازی حافظه تخصیص داده شده‪ ،‬باید مکان شروع‬
‫آن به عملگر ‪ delete‬داده شود‪ .‬بنابراین چنانچه مکان شروع آرایه گم شود‪ ،‬نه‬
‫تنها دیگر نمیتوان از آن آرایه استفاده نمود‪ ،‬بلکه حتی نمیتوان آن را آزاد‬
‫کرد؛ در نتیجه این فضا بصورت تخصیص یافته در حافظه باقی خواهد ماند‪،‬‬
‫درحالیکه هیچکس از آن استفاده مفیدی نمیکند‪.‬‬
‫به این فضاهای بدون استفاده‪ ،‬فضاهای گم شده گفته میشود و یک‬
‫خطای منطقی بسیار متداول در برنامهها میباشد‪.‬‬
‫‪ ‬نکته دیگر این است که میتوانید در صورت لزوم‪ ،‬اندازه یک آرایه پویا را‬
‫در حین اجرای برنامه تغییر دهید‪ .‬بعنوان مثال فرض کنید آرایه ‪data‬‬
‫بصورت پویا و با ‪ 100‬عنصر بصورت زیر تعریف شده است‪:‬‬
‫;]‪int *data = new int[100‬‬
‫‪ 14-5-5‬تخصیص حافظه پویا به آرایه ها‬
‫‪ ‬اکنون فرض کنید در حین اجرای برنامه متوجه شده ایم که اندازه آرایه ‪data‬کوچک بوده‬
‫و نیاز به ‪ 20‬عنصر دیگر نیز داریم‪.‬‬
‫‪ ‬توجه کنید که متاسفانه نمیتوان به سادگی اندازه آرایه ‪ data‬را ‪ 20‬عنصر افزایش داد؛‬
‫چرا که همانطور که میدانید فضای تخصیص یافته به یک آرایه باید بصورت پشت‬
‫سرهم باشد‪ ،‬اما به هیچ صورتی نمیتوان مطمئن شد که ‪ 20‬آدرس پس از انتهای‬
‫آرایه ‪ data‬آزاد باشند‪.‬‬
‫‪ ‬به همین دلیل باید ابتدا ‪ 120‬فضای آزاد از نوع عدد صحیح در مکان دیگری از حافظه‬
‫تخصیص داد و کلیه ‪ 100‬عنصر موجود در ‪ data‬را به آنجا منتقل نمود‪.‬‬
‫‪ ‬اکنون میتوانیم فضای تخصیص یافته به آرایه اولیه را آزاد نموده و اشاره گر ‪ data‬را به‬
‫اولین عنصر از فضای تخصیص یافته جدید‪ ،‬اشاره دهیم‪ .‬البته برای انجام این عملیات‪،‬‬
‫نیاز به یک اشاره گر کمکی نیز داریم‪ .‬تکه برنامه زیر نحوه انجام این عملیات را نشان‬
‫میدهد‪:‬‬
‫; ]‪int *temp = new int[120‬‬
‫)‪for (i=0; i<100; i++‬‬
‫; ]‪temp[i] = data[i‬‬
‫;‪delete[] data‬‬
‫;‪data = temp‬‬
‫; ‪temp = NULL‬‬
‫‪ 14-5-5‬تخصیص حافظه پویا به آرایه ها‬
‫‪ ‬اما روش دیگر تخصیص حافظه پویا که در کامپایلرهای قدیمی زبان ‪ C‬استفاده میگردید‪،‬‬
‫استفاده از دوتابع ‪ malloc‬برای تخصیص حافظه و ‪ free‬برای آزادسازی آن است‪.‬‬
‫‪ ‬این دو تابع در فایل سرآمد ‪ alloc.h‬تعریف شدهاند و برای استفاده از آنها باید این فایل را‬
‫در برنامه با استفاده از ‪ #include‬گنجانید‪ .‬شکل کلی تابع ‪ malloc‬بصورت زیر است‪:‬‬
‫)>‪void *malloc(<num-bytes‬‬
‫‪ ‬در فراخوانی این تابع >‪ <num-bytes‬مشخص کننده تعداد بایتهای مورد نیاز است که باید‬
‫تخصیص داده شود‪.‬‬
‫‪ ‬این تابع بعنوان خروجی یک اشاره گر به فضای تخصیص داده شده از نوع )* ‪ (void‬باز‬
‫میگرداند‪.‬‬
‫‪ (void *) ‬یک اشاره گر عمومی است که میتواند به هر نوع دادهای اشاره کند‪ .‬در‬
‫حقیقت چنانچه اشاره گری از نوع )* ‪ (void‬تعریف کنید‪ ،‬قادر خواهید بود آن را به هر‬
‫متغیری از هر نوع دادهای اشاره دهید‪.‬‬
‫‪ ‬از آنجا که تابع ‪ malloc‬تنها تعداد بایتهای مورد نیاز را بعنوان ورودی دریافت مینماید و از‬
‫نوع دادهها آگاه نیست (این یکی از مشکالت این تابع است)‪ ،‬بنابراین یک اشاره گر‬
‫عمومی به فضای تخصیص یافته را باز میگرداند‪ .‬اما برنامهنویس باید قبل از انتساب‬
‫اشاره گر بازگشتی تابع خود‪ ،‬آن را به نوع داده مورد نظر تغییر قالب دهد‪.‬‬
‫‪ ‬چنانچه تابع ‪ malloc‬نتواند حافظه موردنظر را تخصیص دهد‪ ،‬مقدار ‪ NULL‬را باز خواهد‬
‫گرداند‪.‬‬
‫‪ 14-5-5‬تخصیص حافظه پویا به آرایه ها‬
‫‪ ‬مثال فرض کنید قصد داریم یک آرایه از اعداد صحیح با ‪ n‬عنصر ایجاد کنیم که ‪n‬‬
‫یک متغیر صحیح است‪.‬تکه برنامه زیر نحوه انجام این کار را نشان میدهد‪:‬‬
‫;‪int *data‬‬
‫;))‪data = (int *)malloc(n * sizeof(int‬‬
‫‪ ‬می بینید که بعنوان تعداد بایتها از )‪ n*sizeof(int‬استفاده شده است‪.‬‬
‫‪ ‬در ضمن اشاره گر بازگشتی تابع قبل از انتساب به متغیر ‪ data‬توسط عملگر‬
‫)* ‪ (int‬به نوع مناسب تغییر قالب داده شده است‪.‬‬
‫‪ ‬پس از تخصیص حافظه توسط تابع ‪ ،malloc‬میتوانید حافظه تخصیص داده شده‬
‫را توسط تابع ‪ free‬باز پس بگیرید‪ .‬شکل کلی این تابع بصورت زیر است‪:‬‬
‫)>‪void free(<pointer‬‬
‫‪ ‬که در آن >‪ <pointer‬اشاره گر به مکانی از حافظه است که قصد آزاد سازی آن‬
‫مکان را داریم‪ .‬بعنوان مثال برای آزاد سازی حافظه تخصیص یافته به ‪ data‬داریم‪:‬‬
‫;)‪free(data‬‬
‫‪ 14-5-5‬تخصیص حافظه پویا به آرایه ها‬
‫‪‬‬
‫به دو دلیل بهتر است از عملگرهای ‪ new‬برای تخصیص‬
‫حافظه پویا استفاده نمایید‪.‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫این عملگر از نوع داده آگاه است و خودش بطور خودکار اندازه‬
‫حافظه الزم را محاسبه مینماید‪.‬‬
‫اشاره گر بازگشتی آن از نوع مناسب بوده و نیازی به تغییر قالب‬
‫آن نیست‪.‬‬
‫اما در هرصورت تنها از یکی از موارد فوق‪ ،‬یعنی‬
‫عملگرهای ‪ new‬و ‪ delete‬و یا توایع ‪ malloc‬و ‪ free‬استفاده‬
‫نمایید‪.‬‬
‫به دلیل عدم سازگاری این دو‪ ،‬استفاده همزمان از هر دو‬
‫روش موجب بروز مشکل خواهد شد‪.‬‬
‫‪ 14-5-6‬اشاره گرها و آرایههای چندبعدی‬
‫‪ ‬رابطه اشاره گرها و آرایههای چند بعدی کمی پیچیده است‪ .‬اجازه دهید‬
‫بحث را از آرایههای دوبعدی آغاز نماییم‪.‬‬
‫‪ ‬برای درک رابطه بین اشاره گرها و آرایههای دوبعدی‪ ،‬ابتدا باید نحوه‬
‫ذخیره سازی این آرایهها در زبان ‪ C‬را بررسی کنیم‪ .‬آرایه دوبعدی زیر را‬
‫در نظر بگیرید‪:‬‬
‫;]‪int A[4][5‬‬
‫‪ ‬ما تا کنون این آرایه را بصورت یک ماتریس دوبعدی تصور مینمودیم‪ .‬اما‬
‫زبان ‪ C‬چگونه میتواند یک آرایه دوبعدی را در حافظه اصلی که بصورت‬
‫خطی (یک بعدی) است ذخیره نماید؟‬
‫‪ ‬برای این کار ‪ C‬باید آرایه دوبعدی را بصورت یک بعدی درآورد‪ .‬برای این‬
‫کار دو روش وجود دارد‪ :‬روش سطری و روش ستونی‪.‬‬
‫‪ ‬در روش سطری‪ ،‬سطرهای آرایه دوبعدی به ترتیب پشت سرهم قرار‬
‫داده میشوند تا یک آرایه یک بعدی بوجود آید‪.‬‬
‫‪ ‬زبان ‪ C‬از این روش استفاده مینماید‪.‬‬
‫‪ 14-5-6‬اشاره گرها و آرایههای چندبعدی‬
‫‪4‬‬
‫‪3‬‬
‫‪2‬‬
‫‪1‬‬
‫‪0‬‬
‫‪0‬‬
‫‪1‬‬
‫‪A‬‬
‫‪2‬‬
‫‪3‬‬
‫نحوه تجسم آرایه ‪ A‬توسط برنامهنویس‬
‫‪A‬‬
‫‪ 3‬ردیف‬
‫‪2‬ردیف‬
‫‪ 1‬ردیف‬
‫نحوه ذخیره آرایه ‪ A‬در حافظه اصلی‬
‫‪ 0‬ردیف‬
‫‪ 14-5-6‬اشاره گرها و آرایههای چندبعدی‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫همانگونه که در شکل میبینید‪ ،‬متغیر ‪ A‬در حقیقت اشاره گری به عنصر اول‬
‫آرایه است‪ .‬اما سوال این است که ‪ A‬به چه نوع دادهای اشاره میکند؟‬
‫این متغیر اشاره گری به آرایههای ‪ 5‬عنصری از نوع عدد صحیح است که‬
‫میتوان آن را با )* ]‪ (int[5‬نشان داد!‬
‫می دانید که برای دسترسی به عناصر یک آرایه دوبعدی باید از دو اندیس‬
‫استفاده نماییم‪ .‬بعنوان مثال برای دسترسی به عنصر موجود در ردیف دوم‪،‬‬
‫ستون سوم از ]‪ A[2][3‬استفاده میکنیم‪.‬‬
‫هنگامیکه کامپایلر ‪ C‬به این عبارت میرسد‪ ،‬باید عنصر معادل آن در حافظه‬
‫اصلی را محاسبه کند‪.‬‬
‫برای انجام این کار‪ ،‬کامپایلر ابتدا اندیس اول یعنی ]‪ A[2‬آغاز مینماید‪.‬‬
‫در قسمت قبل گفتیم که ]‪ A[2‬معادل )‪ *(A+2‬میباشد‪ .‬اما برای محاسبه‬
‫مقدار )‪ *(A+2‬ابتدا توجه کنید که با هربار افزایش یک اشاره گر‪ ،‬مقدار آن به‬
‫اندازه تعداد بایتهای دادهای که به آن اشاره میکند افزایش خواهد یافت‪.‬‬
‫از آنجا که ‪ A‬اشاره گری به آرایههای ‪ 5‬عنصری از نوع عدد صحیح است؛ با‬
‫هربار افزایش‪ ،‬مقدار آن به اندازه طول ‪ 5‬عدد صحیح (یعنی ‪ 10‬بایت در‬
‫سیستمهای ‪ 16‬بیتی) افزایش خواهد یافت‪ .‬بنابراین توجه کنید که ‪ A+1‬به‬
‫ابتدای ردیف ‪ 1‬اشاره خواهد کرد و ‪ A+2‬به ابتدای ردیف ‪ 2‬و ‪...‬‬
‫‪ 14-5-6‬اشاره گرها و آرایههای چندبعدی‬
‫‪A+3‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪A+2‬‬
‫‪A+1‬‬
‫‪A‬‬
‫گرچه ‪ A+2‬به ابتدای ردیف ‪ 2‬اشاره میکند‪ ،‬اما هنوز هم‬
‫از نوع اشاره گر به آرایههای ‪ 5‬عنصری یا )* ]‪ (int[5‬است‪.‬‬
‫اما )‪ *(A+2‬یک اشاره گر به ابتدای ردیف ‪ 2‬است که از نوع‬
‫اشاره گر به عدد صحیح یا )* ‪ (int‬است‪.‬‬
‫بنابراین ]‪ A[2‬یا معادل آن )‪ *(A+2‬در حقیقت همانند یک‬
‫اشاره گر به آرایه یک بعدی موجود در ردیف ‪ 2‬هستند‪.‬‬
‫‪ 14-5-6‬اشاره گرها و آرایههای چندبعدی‬
‫‪ ‬حال نوبت به اندیس دوم عبارت میرسد‪ .‬گفتیم که ]‪ A[2‬معادل )‪ *(A+2‬است‪،‬‬
‫با استدالل مشابهی عبارت ]‪ A[2][3‬معادل )‪ *(*(A+2)+3‬میباشد‪.‬‬
‫‪ ‬دیدید که )‪ *(A+2‬یک اشاره گر به ابتدای ردیف دوم میباشد که از نوع )* ‪(int‬‬
‫است‪ ،‬بنابراین جمع آن با ‪ 3‬یعنی ‪ *(A+2)+3‬باعث میشود که ‪ 3‬مکان دیگر از‬
‫نوع عدد صحیح جلوتر رفته و دقیقا به آدرس داده موردنظر برسیم‪.‬‬
‫‪ ‬حال کافی است که محتوای این آدرس را با استفاده از عملگر * بدست آوریم‬
‫که برابر )‪ *(*(A+2)+3‬میگردد‪.‬‬
‫‪ ‬در حقیقت کامپایلر‪ ،‬عبارت ]‪ A[2][3‬را تبدیل به عبارت معادل )‪*(*(A+2)+3‬‬
‫نموده و با استفاده از آن مکان داده را به دست میآورد‪ .‬بطور کلی داریم‪:‬‬
‫)‪A[i][j] = *(*(A+i) + j‬‬
‫‪ ‬اما مطلب جالب دیگری که در مباحث باال وجود داشت‪ ،‬این نکته بود که‬
‫)‪*(A+2‬و ]‪ A[2‬در حقیقت اشاره گری به ردیف ‪2‬هستند‪ ،‬و یا بعبارت دیگر‬
‫نماینده آرایه صحیح ردیف ‪ 2‬هستند‪.‬‬
‫‪ ‬بنابراین چنانچه یک آرایه دوبعدی مانند ‪ A‬داشته باشیم‪ ،‬نام آرایه به همراه تنها‬
‫یک اندیس مانند ‪( i‬یعنی ]‪ )A[i‬نشاندهنده آرایه یک بعدی موجود در ردیف ‪i‬ام‬
‫است‪.‬‬
‫‪ 14-5-6‬اشاره گرها و آرایههای چندبعدی‬
‫‪ ‬از این خاصیت میتوانید برای ارسال یک ردیف خاص از یک آرایه‬
‫دوبعدی‪ ،‬بعنوان یک آرایه یک بعدی به یک تابع استفاده نمایید‪.‬‬
‫‪ ‬بعنوان مثال چنانچه بخواهید میانگین اعداد ردیف ‪ 2‬از آرایه ‪ A‬را بدست‬
‫آورده و در متغیری مانند ‪ average‬قرار دهید‪ ،‬میتوانید تابع‬
‫‪ computeAverage‬را به شکل زیر فراخوانی نمایید‪:‬‬
‫;)‪average = computeAverage(A[2], 5‬‬
‫‪ ‬برای آرایه های با ابعاد باالتر نیز مفاهیم مشابهی وجود دارد‪ .‬بعنوان‬
‫مثال چنانچه آرایه ‪ B‬بصورت زیر تعریف شده باشد‪:‬‬
‫;]‪int B[3][5][8‬‬
‫‪ ‬آنگاه آرایه ‪ B‬یک اشاره گر به آرایه های دوبعدی ‪ 5×8‬یا )* ]‪(int[5][8‬‬
‫است‪.‬‬
‫‪ ‬برای طوالنی نشدن موضوع از ذکر جزئیات بیشتر خودداری می گردد و‬
‫بررسی بیشتر را به خود شما واگذار می نماییم‪.‬‬
‫‪ 14-5-6‬اشاره گرها و آرایههای چندبعدی‬
‫‪ ‬فرض کنید قصد داریم یک آرایه دوبعدی از اعداد به ابعاد ‪ 5×8‬ایجاد نماییم‪.‬‬
‫‪ ‬همانطور که در بخش قبل گفتیم‪ ،‬نام چنین آرایه ای یک اشاره گر به آرایه های‬
‫‪ 8‬عنصری یا )* ]‪ (int[8‬است‪ .‬برای تعریف چنین اشاره گری می توان بصورت زیر‬
‫عمل کرد‪:‬‬
‫;]‪int (*ptr)[8‬‬
‫‪ ‬توجه کنید که قرار دادن پرانتزها الزامی است‪ ،‬چرا که چنانچه از پرانتز استفاده‬
‫نشود (یعنی بصورت ]‪ int ptr[8‬نوشته شود)‪ ،‬آنگاه متغیر ‪ ptr‬یک آرایه ‪ 8‬عنصری‬
‫از نوع اشاره گر به عدد صحیح خواهد بود‪.‬‬
‫‪ ‬حال که اشاره گر مورد نظر خود را تعریف نمودیم‪ ،‬می توانیم بصورت برای‬
‫تخصیص حافظه پویا به آن از عملگر ‪ new‬استفاده نماییم‪ .‬به دستور زیر توجه‬
‫کنید‪:‬‬
‫;]‪ptr = new int[5][8‬‬
‫‪ ‬نکته بسیار مهم در این است که بعد اول آرایه دوبعدی پویا‪ ،‬می تواند یک متغیر‬
‫صحیح نیز باشد‪ .‬بعنوان مثال به دستورات زیر توجه کنید‪:‬‬
‫;]‪int (*ptr)[8‬‬
‫; ‪int n‬‬
‫تعیین مقدار‪// n‬‬
‫;]‪ptr = new int[n][8‬‬
‫‪ 14-5-6‬اشاره گرها و آرایههای چندبعدی‬
‫‪ ‬آخرین نکته نیز آن است که برای آزاد کردن حافظه یک‬
‫آرایه دوبعدی‪ ،‬از همان عملگر ][‪ delete‬استفاده نمایید‪.‬‬
‫‪ ‬بعنوان مثال ‪:‬‬
‫;‪delete[] ptr‬‬
‫‪ ‬چنانچه بخواهیم آرایه ‪ n×8‬فوق را با استفاده از تابع‬
‫‪ malloc‬ایجاد کنیم‪ ،‬می توانیم بصورت زیر عمل نماییم‪:‬‬
‫;]‪int (*ptr)[8‬‬
‫;))‪ptr = (int[8] *) malloc(n*8*sizeof(int‬‬
‫‪ ‬برای آزاد سازی این حافظه نیز می توان تابع ‪ free‬را‬
‫بصورت عادی فراخوانی نمود‪:‬‬
‫;)‪free(ptr‬‬
‫‪ 14-5-6‬اشاره گرها و آرایههای چندبعدی‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫اما روش دیگری نیز برای ایجاد آرایه های دوبعدی بصورت پویا وجود دارد‪.‬‬
‫مجددا یک آرایه دوبعدی از اعداد صحیح به ابعاد ‪ 5×8‬را در نظر بگیرید‪ .‬در‬
‫روش جدید‪ ،‬ابتدا یک آرایه یک بعدی از اشاره گر به اعداد صحیح با ‪5‬‬
‫عضو ایجاد می گردد‪ .‬سپس بازای هر عضو این آرایه‪ ،‬یک آرایه یک بعدی‬
‫از اعداد صحیح با ‪ 8‬عنصر ایجاد شده و آدرس شروع آن در عضو مورد‬
‫نظر قرار می گیرد‪.‬‬
‫شکل زیر نحوه انجام کار را نشان می دهد‪:‬‬
‫‪ 14-5-6‬اشاره گرها و آرایههای چندبعدی‬
‫‪ ‬با توجه به روش گفته شده‪ ،‬دستورات الزم برای انجام این کار به شرح زیر می‬
‫باشد‪:‬‬
‫;]‪int *ptr[5‬‬
‫)‪for (i=0 ;i<5; i++‬‬
‫;]‪ptr[i] = new int[8‬‬
‫‪ ‬توجه کنید که در این روش‪ ،‬برخالف روش قبل تعداد ردیفها ثابت بوده و باید در‬
‫هنگام تعریف متغیر ‪ ptr‬بصورت ایستا تعیین گردد‪ ،‬اما تعداد ستونها می تواند‬
‫بصورت پویا و در زمان اجرا تعیین گردد‪.‬‬
‫‪ ‬بعنوان مثال چنانچه متغیر صحیح ‪ m‬حاوی تعداد ستونها باشد‪ ،‬می توان حلقه‬
‫‪ for‬در تکه برنامه باال را بصورت زیر نوشت‪:‬‬
‫)‪for (i=0 ;i<5; i++‬‬
‫;]‪ptr[i] = new int[m‬‬
‫‪ ‬توجه داشته باشید که برای آزاد سازی فضای تخصیص یافته به این آرایه‪ ،‬باید‬
‫فضای تخصیص یافته به هر اشاره گر را جداگانه آزاد نمایید‪ .‬تکه برنامه زیر این‬
‫عمل را انجام می دهد‪:‬‬
‫)‪for (i=0; i<5; i++‬‬
‫;]‪delete[] ptr[i‬‬
‫‪ 14-5-6‬اشاره گرها و آرایههای چندبعدی‬
‫‪‬‬
‫اما این روش دارای چندین مزیت عمده نسبت به روش قبلی می باشد‪.‬‬
‫‪ ‬اولین مزیت این است که حتما الزم نیست آرایه دوبعدی شما بصورت‬
‫مستطیل تعریف گردد! بعبارت دیگر می توانید برای هر ردیف‪ ،‬آرایه ای با‬
‫اندازه متفاوت ایجاد کنید‪.‬‬
‫‪‬‬
‫‪‬‬
‫از این حالت معموال برای ذخیره سازی آرایه ای از رشتهها استفاده می‬
‫شود‪ .‬بعنوان مثال فرض کنید می خواهیم آدرس کلیه دانشجویان یک کالس‬
‫را در یک آرایه نگهداری کنیم‪ .‬از آنجا که طول آدرس هر دانشجو با دیگری‬
‫متفاوت است‪ ،‬می توان با استفاده از این روش برای هر دانشجو یک رشته‬
‫کاراکتری به اندازه آدرس وی ایجاد کرده و به وی نسبت داد‪.‬‬
‫مزیت دوم‪ ،‬سادگی و سرعت جابجایی دو ردیف از آرایه است‪.‬‬
‫‪‬‬
‫در مواردی که نیاز زیادی به جابجایی دو ردیف از آرایه است‪ ،‬بهتر است از این‬
‫روش استفاده شود‪ .‬چرا که در این روش فقط اشاره گرهای دو ردیف با‬
‫یکدیگر جابجا می شوند‪ ،‬ولی در روش قبلی باید کل عناصر دو ردیف با‬
‫یکدیگر جابجا شوند‪.‬‬
‫‪ 14-5-6‬اشاره گرها و آرایههای چندبعدی‬
‫‪‬‬
‫و اما آخرین مزیت این روش در این است که می توان تعداد ردیفها و ستونها را‬
‫بصورت پویا تعیین نمود‪.‬‬
‫‪‬‬
‫;]‪int *ptr[5‬‬
‫‪‬‬
‫بار دیگر نگاهی به تعریف متغیر ‪ ptr‬در این روش بیندازید‪.‬‬
‫‪ ptr‬یک آرایه ‪ 5‬عنصری از نوع اشاره گر به عدد صحیح است‪ .‬بنابراین خود ‪ ptr‬به‬
‫تنهایی‪ ،‬اشاره گری به اولین عنصر این آرایه است‪ .‬از آنجا که اولین عنصر خود یک‬
‫اشاره گر است‪ ،‬بنابراین ‪ ptr‬یک اشاره گر به اشاره گر به عدد صحیح است‪ .‬بنابراین‬
‫چنانچه بخواهیم آرایه ‪ ptr‬را بصورت پویا تعریف کنیم داریم‪:‬‬
‫;‪int **ptr‬‬
‫;]‪ptr = new int*[5‬‬
‫‪ ‬حال می توانیم با استفاده از یک حلقه ‪ ،for‬فضای الزم برای هر ردیف را به آن تخصیص‬
‫دهیم‪.‬‬
‫‪ ‬بطور کلی چنانچه بخواهیم یک آرایه دوبعدی کامال پویا به ابعاد ‪ n×m‬ایجاد نماییم‪،‬‬
‫بصورت زیر عمل می کنیم‪:‬‬
‫; ‪int n,m‬‬
‫تعیین مقدار‪ m‬و‪// n‬‬
‫;‪int **ptr‬‬
‫;]‪ptr = new int*[n‬‬
‫)‪for (i=0; i<n; i++‬‬
‫;]‪ptr[i] = new int[m‬‬
‫‪ 14-5-6‬اشاره گرها و آرایههای چندبعدی‬
‫‪ ‬حتما به یاد دارید که برای تعریف یک پارامتر از تابع بعنوان یک آرایه دوبعدی‪ ،‬باید‬
‫حتما تعداد ستونهای آن مشخص گردد اما نیازی به تعیین تعداد ردیفهای آن‬
‫نیست‪.‬‬
‫‪ ‬بعنوان مثال اگر تابعی مانند ‪ test‬یک آرایه دوبعدی با ‪ 5‬ستون را بعنوان و رودی‬
‫دریافت میکرد‪ ،‬تعریف آن بصورت زیر بود‪:‬‬
‫… { )]‪void test(int A[][5‬‬
‫‪ ‬با توجه به مطالبی که در مورد اشاره گرها و رابطه آنها با آرایههای دوبعدی ذکر‬
‫شد‪ ،‬دلیل این نحوه تعریف کامال روشن میگردد‪.‬‬
‫‪ ‬گفتیم که نام یک آرایه دوبعدی با ‪ 5‬ستون (و هر تعداد ردیف دلخواه) اشاره گری‬
‫به آرایههای ‪ 5‬عنصری است‪ ،‬بنابراین تابع ‪ test‬نیز یک )* ]‪ (int[5‬را بعنوان ورودی‬
‫دریافت میکند‪ .‬میتوان این تابع را بصورت زیر نیز تعریف کرد‪:‬‬
‫… { )]‪void test(int (*A)[5‬‬
‫‪ ‬که معادل با همان تعریف قبلی است‪.‬‬
‫‪ ‬هنگامی که در داخل تابع از اندیس برای دسترسی به یک عنصر از آرایه‬
‫دوبعدی استفاده میکنیم‪ ،‬کامپایلر ‪ C‬از تعداد ستونهای مشخص شده برای‬
‫محاسبه مکان عنصر در حافظه اصلی استفاده میکند‪.‬‬
‫‪ 14-6‬اشاره گرها و ساختارها‬
‫از آنجا که هر ساختار همانند یک نوع داده جدید میباشد‪ ،‬میتوان متغیری از نوع اشاره گر به ساختار تعریف کرد‪.‬‬
‫‪‬‬
‫بعنوان مثال ساختار دانشجو را در نظر بگیرید‪.‬‬
‫‪‬‬
‫{ ‪struct student‬‬
‫;‪long int id‬‬
‫;]‪char name[30‬‬
‫;‪float average‬‬
‫;‪int age‬‬
‫;}‬
‫برای تعریف متغیری که به این ساختار اشاره نماید‪ ،‬همانند اشاره گرهای دیگر از عالمت * استفاده میکنیم‪.‬‬
‫‪‬‬
‫;‪student *stdPtr‬‬
‫حال میتوان این اشاره گر را به یک متغیر دیگر از نوع ‪ student‬اشاره داد‪ .‬به مثال زیر توجه کنید‪:‬‬
‫‪‬‬
‫; }‪student s = {82121020, "reza", 17.2, 20‬‬
‫;‪stdPtr = &s‬‬
‫حال میتوان از طریق ‪ stdPtr‬به اجزای ‪ s‬دسترسی پیدا کرد‪ .‬برای دسترسی به ‪ id‬از طریق اشاره گر ‪ stdPtr‬باید‬
‫‪‬‬
‫ابتدا از عملگر محتوا یا * بصورت ‪ (*stdPtr).id‬استفاده نماییم‪.‬‬
‫دستور زیر باعث میشود که شماره دانشجویی ‪ s‬به ‪ 83101010‬تغییر کند‪:‬‬
‫‪‬‬
‫;‪(*stdPtr).id = 83101010‬‬
‫از آنجا که نوشتن این عبارت کمی سخت است‪ ،‬در زبان ‪ C‬از عملگر دیگری بنام فلش یا >‪ -‬برای دسترسی به‬
‫‪‬‬
‫اجزای یک اشاره گر به ساختار استفاده میشود‪ .‬این عملگر در حقیقت متشکل از یک عالمت منها ‪ -‬و یک عالمت‬
‫بزرگتر < است که در کنار یکدیگر قرار گرفته اند‪ .‬بعنوان مثال دستور فوق را میتوان بصورت زیر نیز نوشت‪:‬‬
‫;‪stdPtr->id = 83101010‬‬
‫‪ 14-6‬اشاره گرها و ساختارها‬
‫‪‬‬
‫‪‬‬
‫می توان از عملگر ‪ new‬برای تخصیص حافظه به یک اشاره گر به ساختار استفاده کرد‪.‬‬
‫در تکه برنامه زیر پس از تخصیص حافظه الزم به یک دانشجو‪ ،‬آن را مقداردهی نیز نموده‬
‫ایم‪:‬‬
‫‪‬‬
‫; ‪student *stdPtr‬‬
‫;‪stdPtr = new student‬‬
‫;‪stdPtr->id = 84102010‬‬
‫;)"‪strcpy(stdPtr->name, "reza‬‬
‫;‪stdPtr->average = 18.0‬‬
‫;‪stdPtr->age = 19‬‬
‫‪ ‬برای آزاد سازی فضای تخصیص یافته نیز میتوان از عملگر ‪ delete‬استفاده نمود‪.‬‬
‫;‪delete stdPtr‬‬
‫‪ ‬تخصیص حافظه و باز پس گیری آن به ساختارها با استفاده از توابع ‪ malloc‬و ‪ free‬نیز‬
‫امکان پذیر است‪ .‬نحوه انجام این کار بصورت زیر است‪:‬‬
‫;))‪stdPtr = (student *) malloc(sizeof(student‬‬
‫;)‪free(stdPtr‬‬