پرش به محتوا

زبان برنامه نویسی سی/تابع اصلی

ویکی‎کتاب، کتابخانهٔ آزاد

هر برنامه‌ای در سی دارای یک سند ( فایل ) اصلی است که داخل آن تابع اصلی قرار دارد و تابع اصلی برنامه ، تابعی است که برنامه را کنترل می کند تا شروع شده و کارها را انجام دهد و به پایان برسد و گذرگاه اصلی تمام توابع و بخش‌های دیگر برنامه است . در زبان سی تابع main از پیش در کامپایلر ( یعنی قسمت linker یا پیوند دهنده آن ) ، تعریف شده است که در برنامه نویسی سطح بالا هر برنامه باید دارای یک تابع main باشد که البته می‌توان با دستور به کامپایلر آن را نادیده گرفت ( که باعث بلا استفاده شدن برنامه می شود و یا می توان تابع دیگری را به عنوان تابع اصلی استفاده کرد که در ادامه توضیح می دهیم ) اما در برنامه‌نویسی سطح پائین معمولاً تابعی را می نویسند تا جایگزین تابع main شود و کنترل سخت‌افزار را به عهده بگیرد و آن را مدیریت کند . یکی از همین برنامه‌های سطح پائین ، کرنل سیستم عامل است . تابع main به معنی تابع اصلی ( main function ) دارای دو پارامتر از پیش تعریف شده است که به برنامه امکان دریافت آرگومان جهت انجام کارهای برنامه را می دهد . یکی argc که سرنام argument count است که می شمرد تابع اصلی ، چند آرگومان دارد و دیگری argv که سرنام argument vector است و برای تعیین آرگومان‌های ارسال شده به برنامه می باشد و البته شما می توانید پارامترهای دیگری را ( اختیاری ) نیز اضافه کنید

نکات :

تابع main نمی تواند inline تعریف شود
تابع main نمی تواند static تعریف شود
نمی توان آدرس تابع main را گرفت ( توسط اشاره‌گر )
نمی توان آن را در جایی از برنامه ، فراخواند
اولین عنصر آرایه پارامتر argv نام برنامه است
آخرین عنصر آرایه پارامتر argv مقدار NULL می باشد

شکل کلی نوشتن تابع main به صورت حرفه‌ای به شکل زیر :

int main (int argc, char *argv[])
{

  return 0;
}

یا به شکل زیر است :

int main (int argc, char **argv)
{

  return 0;
}

که تفاوتی با هم ندارند . اولین شکل یک اشاره‌گر از نوع کاراکتر آرایه‌ای است و شکل دوم یک اشاره‌گر به کاراکتر اشاره‌گر که می‌دانیم کاراکتر اشاره‌گر یک رشته است

مثال ساده :

#include <stdio.h>

int main (int argc, char *argv[])
{
  int count;

  printf ("This program was called with \"%s\".\n",argv[0]);

  if (argc > 1)
    {
      for (count = 1; count < argc; count++)
	{
	  printf("argv[%d] = %s\n", count, argv[count]);
	}
    }
  else
    {
      printf("The command had no other arguments.\n");
    }

  return 0;
}

حال اگر نام سند برنامه خود را test.c بگذاریم و در لینوکس آن را در پوشه Home قرار دهیم و به برنامه ابزار gcc دستور gcc -o ~/test ~/test.c را بدهیم برنامه gcc آرگومان‌های o- و test و test.c را خواهد گرفت و یا در ویندوز با کامپایلر ریز سی که در پوشه‌ای قرار دارد و ساخت یک فایل cmd با دستور tcc -o test.exe test.c آن را اجرا کنیم برنامه ابزار tcc آرگومان‌های o- و test.exe و test.c را خواهد گرفت و سپس با اجرای برنامه با آرگومان‌های test a b c خروجی زیر را در خروجی خط‌دستوری ( لینوکس ) مشاهده خواهیم نمود :

This program was called with "./test".

argv[1] = a
argv[2] = b
argv[3] = c

نکته : در ویندوز نیز مشابه همین خروجی را مشاهده خواهیم نمود

تشریح :

ابتدا فایل سرآیند stdio سرنام standard input/output به معنی ورودی/خروجی استاندارد را به برنامه خود ضمیمه می کنیم تا بتوانیم از تابع کتابخانه‌ای printf جهت چاپ در خروجی خط‌دستوری که در فایل stdio.h تعریف شده است استفاده نمائیم . سپس تابع اصلی برنامه یعنی main را تعریف نموده‌ایم که پارامتر اول آن یک صحیح با نام argc است که استاندارد زبان C می باشد و تعداد آرگومان‌های ارسال شده در برنامه در آن ذخیره می شود و پارامتر رشته‌ای آرایه argv که آرگومان‌های ارسال شده به برنامه را دریافت می نماید . سپس یک متغیر از نوع صحیح با نام count به معنی شمردن ، اعلان نموده‌ایم . سپس با تابع کتابخانه‌ای printf چاپ کرده‌ایم که این برنامه با نام ... اجرا شده است که یک رشته است که نام برنامه ما می باشد و s% برای فرمت کردن رشته به کار می رود که در فصل بعدی مفصلاً به آنها پرداخته خواهد شد و اگر به یاد داشته باشید برخی کاراکترها ، کاربرد خاصی دارند که اگر بخواهیم آنها را در رشته چاپ کنیم باید از دنباله‌های خروج یا همان دنباله‌های فرار Escape Sequences استفاده نمائیم که ما در اینجا برای چاپ دابل کوت « " » از "\ استفاده نموده‌ایم . در پایان نیز خط را شکسته و خط جدیدی new line ایجاد نموده‌ایم . سپس اگر تعداد آرگومان‌ها بیشتر از یکی بود ( که یکی از آنها و اولین آنها نام برنامه است ) از count مساوی یک تا آخرین آرگومان چاپ می شود که آرگومان شمارهٔ ... برابر مقدار رشته‌ایِ ... است . در غیر این صورت برنامه چاپ می کند که این برنامه آرگومان دیگری ندارد . دقت کنید که اگر از IDE استفاده کنید ، به صورت پیش‌فرض نمی توانید آرگومان دیگری را به برنامه بفرستید . پس یا باید دستور کامپایل را در IDE عوض کنید یا برنامه را از طریق ورودی/خروجی خط‌دستوری اجرا کنید و به آن آرگومان بفرستید . در پایان با نوشتن ;return 0 تابع main با موفقیت به پایان می رسد و برنامه تمام شده و منابع اشغال شده در سیستم آزاد می شوند

حال اگر بخواهیم برنامه بدون تابع main اجرا شود باید از تابع ()start_ برای لینک شدن یا همان پیوند خوردن برنامه استفاده کنیم و به کامپایلر gcc دستور nostartfiles- را بدهیم . مثال :

#include<stdio.h>
#include<stdlib.h>

int nomain()
{
	printf("Hello, World!\n");
	return 0;
}

void _start()
{
	
	nomain();
	exit(0);
}

این همان برنامه !Hello, World ساده است که بارها تا بدینجا نوشته شده است اما به جای تابع main از تابع ()start_ استفاده نموده‌ایم که تابع nomain یعنی بدون main را فراخوانی می‌نماید . فقط از تابع کتابخانه‌ای exit به معنی خروج با پارامتر 0 که نشان‌دهنده پایان موفقیت‌آمیز می‌باشد استفاده نموده‌ایم که در فایل سرآیند stdlib که سرنام standard library به معنی کتابخانه استاندارد می باشد تعریف شده است . حال در هنگام کامپایل برنامه توسط GCC باید دستور بدهیم gcc -o test test.c -nostartfiles تا برنامه بدون مشکل اجرا شود . ولی اگر تابع ()start_ را نیز جذف کنید و با همان دستور کامپایل کنید ، برنامه پایان نمی یابد و خود سیستم عامل به آن با گزارش Segmentation Fault پایان می دهد ( چون تابع اصلی که برنامه کنترل کند وجود ندارد و برنامه بلاتوقف می شود )

تابع اصلی در برنامه‌نویسی سطح پائین

در برنامه‌نویسی سطح پائین ، شما معمولاً باید تابعی را به عنوان تابع اصلی تعریف کنید اما این تابع باید در کتابخانه‌های کامپایلر همچون Visual Studio یا GCC تعریف شده باشد . برای این کار باید به راهنمای کامپایلر خود مراجعه کنید و یا در صورتی که می خواهید یک Module برای Linux بنویسید به کتابخانه‌های کرنل مراجعه کنید یا اگر می خواهید یک Driver برای ویندوز بنویسید باید یک پروژه سطح پائین برای نوشتن درایور ایجاد کنید تا تابع mian ورودی‌ها و خروجی‌های متناسب را اضافه کند تا بتوانید از گذرگاه کرنل ، دستگاه Device مورد نظر خود را هدایت کنید و برنامه‌ای بنویسید تا آن را مدیریت کند

خروجی تابغ اصلی

طبق استاندارد زبان سی C خروجی 0 یعنی ;return 0 به مفهوم موفقیت‌آمیز بودن برنامه و تابع و پایان آن می باشد . اما استاندارد ، خروجی‌های دیگر را تعیین نکرده است و هر خروجی به غیر از 0 به مفهوم ناموفق بودن تابع می باشد . اما هر کامپایلر مطابق با استاندارد خود ، مقادیر بازگردانده شده را ترجمه می نماید . در کامپایلر GCC مقادیر ۱ تا ۲۵۵ به مفهوم خروجی غیر موفقیت‌آمیز می باشد . همچنین GCC اجازه مقادیر منفی را نیز می دهد نظیر 1- یا 5- که در هر صورت برنامه ناموفق خواهد بود ( این مقادیر زمانی می توانند کاربرد داشته باشند که برنامه شما شرطی است و در صورتی که نتوانست کاری را انجام بدهد ، خروجی غیر 0 داشته باشد )