۸ ویژگی منحصربهفرد زبان برنامهنویسی C
در این مقاله به معرفی زبان برنامهنویسی C و معرفی هشت ویژگی منحصربهفرد این زبان خواهیم پرداخت. تقریباً تمام زبانهای برنامهنویسی مورد استفاده امروزی، دارای ویژگیهایی هستند که نخستین بار در زبان C ارائه شدند. از اینرو، می توان C را به عنوان مادر تمام زبانهای برنامهنویسی مدرن معرفی کرد. این زبان، به منظور نوشتن نرمافزارهای مخصوص برای کوچکترین میکروکامپیوترها تا بزرگترین کامپیوترهای پرسرعت و ابرکامپیوترها کاربرد بسیار وسیعی داشته است.
اپلیکیشنهای نوشته شده در زبان C را می توان به گروههای زیادی از قبیل نرمافزارهای سیستم، نرمافزارهای دسکتاپ، نرمافزارهای سازمانی، پایگاه داده و غیره تقسیمبندی کرد. اگر با مبانی این زبان آشنا باشید، می توانید به خوبی در یک حرفه برنامهنویسی شروع به کار کنید.
تاریخچه زبان C
توسعه زبان C، ارتباط بسیار نزدیکی با ظهور سیستمعامل «یونیکس» (Unix) دارد. این زبان، در سال 1972 توسعه یافت و در سالهای بعد، بخشهای زیادی به آن اضافه شد. پیش از توسعه C، سیستمعامل یونیکس با استفاده از زبان اسمبلی خود، برای کامپیوتر «PDP-7» توسعه یافت. سپس، کامپایلری برای C و باز هم با استفاده از زبان اسمبلی در این پلتفرم ساخته شد. در این بازه زمانی، هدف از طراحی زبان C، تسهیل توسعه یونیکس بود. با استفاده از این کامپایلر، بازنویسی کامل یونیکس برای کامپیوتر «PDP-11» صورت گرفت. پس از این، کامپایلر C نیز به وسیله خود این زبان و با کمک ابزارهای بسیار خوب «Lex» و «Yacc» بازنویسی شد و در نهایت فرآیند «بوتاسترپ کردن» C/Unix خاتمه یافت.
دلیل اصلی بازنویسی یونیکس در C، بهبود «قابلیت انتقال» (Portability) آن بود (امکان اجرا در کامپیوترها و سیستمعاملهای متفاوت). از آنجایی که زبانهای اسمبلی برای معماریهای متفاوت CPU تفاوت دارند، پورت کردن سیستمعامل یونیکس به هر کدام از آنها، کار دشواری به حساب میآمد. این کار با توسعه یک زبان سیستمی مانند C و بازنویسی یونیکس در آن، بسیار راحتتر از قبل شد.
با توسعه زبان C، مشخص شد که میتوان آن را برای کارهایی بیش از نوشتن نرمافزارهای سیستمی به کار برد. به این ترتیب، این زبان برای نوشتن نرمافزارهایی مانند فایل سرورها، سرورهای پایگاه داده، پشتههای شبکه، نرمافزارهای دسکتاپ، وب سرورها و غیره مورد استفاده قرار گرفت.
در سال 1978، «برایان کرنیگان» (Brian Kernighan) و «دنیس ریچی» (Dennis Ritchie)، کتاب «زبان برنامهنویسی C» را منتشر کردند. این کتاب سالهای طولانی به عنوان یک استاندارد فنی غیر رسمی برای زبان C مورد استفاده قرار میگرفت تا اینکه در سال 1989، این زبان توسط «مؤسسه استانداردهای ملی آمریکا» به طور رسمی استانداردسازی شد. آخرین نسخه C، در سال 2011 و تحت عنوان «C11» منتشر شد. در ادامه برخی از ویژگیهای زبان C را مورد بررسی قرار میدهیم.
1. ارتباط C با ++C
با توسعه نرمافزارهای پیچیدهتر در C، مشخص شد که مفاهیم شیءگرا از قبیل «کپسولهسازی» (encapsulation)، «چندریختی» (polymorphism) و غیره، میتوانند در کنترل پیچیدگیهای نرمافزار مفید واقع شوند. این موضوع، منجر به توسعه زبان ++C به عنوان ابرمجموعه C شد.
++C، با افزودن یک سری ویژگی خاص برای نوشتن نرمافزارهای شیءگرا و با حفظ قابلیت سازگاری C ساخته شد. این زبان با هدف «پیشرفت روزافزون» (Progressive Enhancement) توسعه یافت. به این منظور، تغییراتی سازگار با زبان C اعمال شد تا ماژولهای این دو زبان بتوانند در یک برنامه یکسان با هم ترکیب و با یک کامپایلر یکسان کامپایل شوند.
با این کار و با اعمال تغییرات حداقلی در یک برنامه بزرگتر که با مفاهیم شیءگرا نوشته شده است، امکان استفاده از ماژولهای قدیمی مبتنی بر C فراهم شد. از نظر تئوری، یک برنامه C میتواند توسط کامپایلر ++C و بدون نیاز به هیچگونه تغییری، کامپایل شود. اگرچه در عمل، بررسی سختگیرانهتر در ++C، منجر به اعلام خطا میشود و باید یک سری تغییرات در کد اعمال شود.
2. کلمات کلیدی کم
زبان C، علیرغم قدرت بالا، زبان کوچکی محسوب میشود. دلیل این موضوع، وجود تنها 32 کلمه کلیدی با معنای بخصوص است. در طرف مقابل، ++C، جاوا و جاوااسکریپت، هر کدام به ترتیب 82، 50 و 63 کلمه کلیدی را در خود جای دادهاند. به علاوه، زبان «کوبول» (COBOL) دارای 357 کلمه کلیدی است. حتی تصور حفظ کردن این تعداد کلمه و به کارگیری آنها نیز دشوار است.
3. عدم وجود رشتههای صریح
برخلاف بیشتر زبانهای مدرن مانند جاوا، ++C و جاوااسکریپت، امکان نوشتن رشتهها به صورت جداگانه در زبان C فراهم نیست. رشته به آرایهای از کاراکترها اطلاق میشود که با یک «کاراکتر 0» به اتمام برسد (با «0\» علامتگذاری شده باشد). طول رشته با یک قرارداد مشخص میشود و آن، تعداد کاراکترها تا رسیدن به 0 است. شما میتوانید 0 را نادیده گرفته و کاراکترهای پیش از 0\ را شمارش یا ذخیره کنید. چنین کمبودی در وجود نوع رشته مناسب و قرارداد مذکور، باعث به وجود آمدن مشکلات زیادی در سالهای اخیر با عنوان «سرریز بافر» (Buffer Overflow) شده است.
در واقع، اولین کرمی که در اینترنت پخش شد، «کرم اینترنتی موریس» (Morris Internet Worm)، حاصل چنین باگی در بخش مهمی از یک نرمافزار سیستمی با نام «Finger Daemon» بود. در کد زیر، نمونهای از این باگ را مشاهده میکنید. این کد نشان میدهد که به چه راحتی میتوان چنین باگهایی را در یک برنامه قرار داد. برنامه به خوبی کامپایل میشود اما به دلیل سرریز بافر، کرش میکند.
# include <stdio.h> main() { char *buf = "hello world"; buf[12] = 'a'; printf("%s\n", buf); }
در طرف مقابل، اکثر زبانهای مدرن، با ارائه رشتههای صریح، ایجاد چنین حقههایی را غیر ممکن میکنند. حتی ++C که کد بالا را به خوبی کامپایل میکند نیز یک رشته مجزا از نوع «std» را ارائه میدهد.
4. دستکاری اشارهگر
یک اشارهگر، ارجاع یا «رفرنس» (Reference) محل مموری است. هنگامی که سخن از خواندن و نوشتن اختیاری محلهای مموری به میان میآید، زبان C کاملاً انعطافپذیر است. این انعطافپذیری هزینه بسیار بالایی در پی خواهد داشت و باعث ایجاد باگهای زیادی در نرمافزار میشود. وجود باگهایی در وبسرورها، ایمیل سرورها و FTP سرورها از قابل توجهترین و تأثیرگذارترین عواقبی هستند که میتوانند بر روی تمام اینترنت تأثیر بگذارند. حتی امروزه نیز گاهی خبرهایی در مورد باگهای ناشی از ارجاعدهی و بهروزرسانی محلهای نامعتبر مموری شنیده میشود.
باگ امنیتی «خونریزی قلبی» (Heartbleed) که در سال 2014 کشف شد، در اثر مدیریت نامناسب محلهای اشارهگر به وجود آمد. این مسئله، اهمیت حیاتی مدیریت صحیح اشارهگر در زبان C را نشان میدهد. حتی خطاهای «صفحه آبی مرگ» که گاهی اوقات در سیستمهای ویندوز دیده میشوند نیز ممکن است از رسیدگی نامناسب اشارهگر ناشی شوند. زبانهای دیگر (به غیر از ++C)، اجازه دستکاری اشارهگر را به کاربر نمیدهند و به همین دلیل، در مقابل این نوع باگها آسیبپذیر نیستند.
5. قابلیت تعویض محل اشارهگرها و آرایهها
در زبان C، اشارهگرها و آرایهها قابلیت جابجایی دارند. این زبان، امکان استفاده از یک آرایه در محلی که به یک اشارهگر احتیاج باشد و بالعکس را فراهم میکند. اگرچه این قابلیت، اجازه اعمال تغییرات قدرتمندی را به کاربر میدهد اما باعث ایجاد باگهای بدنام زیادی نیز میشود.
6. استفاده گسترده از ماکروهای «define»
ماکروها، به منظور جایگزینی یک نام برای یک گسترش جداگانه مورد استفاده قرار میگیرند. از ماکرو میتوان برای تغییر تعریف یک نام در هنگام کامپایل یا جایگزینی یک گسترش طولانیتر برای یک نام ساده استفاده کرد. سیستم ماکروی C، از عبارت شرطی «ifdef» استفاده میکند که امکان کامپایل شرطی را فراهم میکند. این امر، یکی از روشهای اتصال نرمافزار به معماریها و سیستمعاملهای مختلف است.
نامهای عمومی در هنگام کامپایل با گسترشهای متفاوت و برای شرایط متفاوت تعریف میشوند. علاوه بر این، امکان حذف کردن یا در نظر گرفتن تمام بخشهای کد برای کامپایل به وسیله این ماکروها نیز وجود دارد. این قابلیت در ++C نیز موجود اما منسوخ شده است. زبانهای دیگری مانند جاوا، جاوااسکریپت، پایتون و غیره، چنین قابلیتی را فراهم نمیکنند.
7. ماژولهای کپسولهشده در فایلها
در زبان C، مفهومی به عنوان کلاس وجود ندارد. از اینرو، از قابلیتهای مشاهده عمومی (Public)، خصوصی (Private) و محافظت شده (Protected)، در این زبان پشتیبانی نمیشود. تنها کپسولهسازی که توسط C فراهم میشود، برای فایلها است. توابع، متغیرهای سراسری و حروفی که درون یک فایل منبع تعریف شدهاند، تنها برای آن فایل قابل مشاهده خواهند بود مگر اینکه از اسامی آنها خروجی گرفته شود. کلمات کلیدی «extern» و «static» با هدف کنترل کردن قابلیت مشاهده اسامی متغیرها و توابع در اختیار کاربر قرار دارند. از طرف دیگر، زبانهای دیگری مانند ++C، جاوا و پایتون، معمولاً از کلاسها و کپسولهسازی در کنار یکدیگر پشتیبانی میکنند.
8. کتابخانههای خارجی
به غیر از ساختارهای پایهای یک زبان، عملکردهای پیچیدهتر در C، به کتابخانههای خارجی محول شدهاند. دستکاری رشتهها، انجام محاسبات ریاضی، ورودی-خروجی، ایجاد شبکه و غیره، همگی توسط کتابخانههای خارجی فراهم میشوند. در طرف مقابل، معمولاً دیگر زبانها به همراه کتابخانهای مملو از ماژولهای مختلف ارائه میشوند.
#