زبان C – تابع (جلسه ۱۰)
زمان تقریبی مطالعه این مقاله: ۱۵ دقیقه
سلام به میکرولرنیهای عزیز 🙂 🙂 🙂
این مقاله در ۳۰ اردیبهشت ۱۴۰۰ به روز رسانی شده.
یه جلسه دیگه از زبان C هم شروع شد و مثل جلسههای قبل اومدیم تا کلی مطلب جدید با هم دیگه یاد بگیریم. پس محکم پشت لپتاپ هاتون بشینید که با سرعت بریم جلو 😀
یکی از مهمترین مباحث برنامه نویسی سی موضوع تابع در زبان C هست. در واقع این زبان بر اساس توابع ساخته شده و شما به عنوان یک برنامه نویس باید به خوبی با توابع کار کنید و از ریزه کاریهاشون خبر داشته باشید. این جلسه خیلی مفصل هست و توصیه میکنم حتما چند بار و در زمانهای مختلف این مقاله رو بخونید تا به همهی نکاتش مسلط بشید. پس با میکرولرن، مرجع برنامه نویسی C همراه باشید.
قبل از شروع این مقاله میتونید در صورت نیاز جلسههای قبلی آموزش زبان C را مطالعه کنید:
فهرست مطالب
تابع در زبان C
قبل از بررسی تابع در زبان C، بهتر است مفهوم آن را درک کنیم. اگر یک اتومبیل را در نظر بگیریم؛ از اجزای زیادی تشکیل شده که هر کدام وظیفهی خاصی انجام میدهند. مثلا موتور خودرو وظیفهی تولید نیروی مکانیکی برای به حرکت در آوردن خودرو را به عهده دارد. موتور خودرو برای این کار سوخت، روغن و الکتریسیته را تحویل گرفته و نیرو مکانیکی تولید میکند. در واقع ورودی را تحویل گرفته و خروجی مناسب را تحویل میدهد.
در زبان C هم، تابع چنین کاری را انجام میدهد. یعنی ورودی دریافت کرده و خروجی مناسب تحویل میدهد. استفاده از توابع در برنامهنویسی مزایایی دارد که برخی از آنها به ترتیب زیر هستند:
- خرد کردن برنامه به اجزای کوچکتر جهت سادهتر شدن حل مساله
- طراحی، تست و اشکال زدایی اجزای برنامه به طور مستقل
- امکان استفاده دوباره از برنامه به طور مستقل
- انجام پروژه به صورت تیمی
- افزایش خوانایی برنامه و درک راحتتر آن
- سادهتر شدن اشکال یابی برنامه
- کاهش حجم برنامه
اجزای تابع در زبان C
یک تابع از ۴ قسمت ورودی، خروجی، نام تابع و بدنه تابع تشکلیل شده است که در ادامه آنها را توضیح میدهیم.
ورودی تابع، مقداری است که تابع در برنامه دریافت میکند.
خروجی تابع مقداری است که به برنامه باز میگرداند.
نام تابع برای فراخوانی تابع در برنامه به کار میرود و از قوانین نام گذاری زبان C پیروی میکند.
بدنه تابع شامل دستوراتی است که تابع قرار است آنها را انجام دهد.
تذکر ۱: ورودی و خروجی یک تابع در زبان برنامهنویسی C، انواع متغیرها، آرایهها، رشتهها و… هستند.
تذکر ۲: توابع به صورت پیش فرض میتوانند چندین ورودی بگیرند و تنها یک خروجی تحویل بدهند. اما برای داشتن چند خروجی هم راه حلی وجود دارد که اگر صبور باشید تا انتهای بحث به آن خواهیم پرداخت.
تذکر ۳: توابع با هر نامی که معرفی شوند (حروف بزرگ یا کوچک) با همان نام باید در برنامه مورداستفاده قرار گیرند.
تعریف تابع
اگر جلسات قبل را مرور کرده باشید، متوجه شده اید که هر جزئی از زبان سی نیاز به تعریف کردن دارد. پس قبل از هر چیزی باید تابع تعریف شود. بعد از تعریف تابع، هر جای برنامه که نیاز بود آن را فراخوانی (صدا) میکنیم.
اما به نظر شما تابع را کجا می توان تعریف کرد؟ ممکن است بپرسید مگر محل تعریف تابع هم مهم است؟
بله بسیار مهم است. اصولا در زبان C باید هر عضوی را در جای مشخصی تعریف کرد و گرنه دچار خطا میشویم. محل تعریف تمام اجزای زبان C به تابع main بستگی دارد.
تابع main تابعی است که در تمام برنامههایی که نوشته میشود حضور دارد و سایر توابع و بدنه اصلی برنامه درون این تابع اجرا میشود. در واقع بدون تابع ()main برنامه شما قابل اجرا شدن نیست.
در زبان C، تابع را میتوان در ۳ ناحیه معرفی کرد:
- قبل از تابع main
- بعد از تابع main
- در فایلهای کتابخانه (در مورد کتابخانه توضیح خواهیم داد.)
اگر توابع درون فایلهای کتابخانهای تعریف شوند، با شروع برنامه، فایلهای حاوی توابع به برنامه ضمیمه (include) میشود.
توابع را میتوان قبل از تابع main تعریف کرد؛ بنابراین با شروع برنامه، توابع توسط برنامه رؤیت شده و میتوان از آنها در برنامه استفاده کرد.
در زیر قالب کلی تعریف تابع را مشاهده می کنید:
<output type> <function name>(parameter1, parameter2, ...)
{
//function body
}
اکنون برای درک موضوع به مثال زیر دقت کنید:
#include <stdio.h>
//تعریف تابع
void myFunc(void){
//محل نوشتن بدنه این تابع
}
int main(){
//محل نوشتن بدنه تابع
}
همانطور که مشاهده میکنید، تابع ()myFunc قبل از تابع ()main تعریف شده است.
اما اگر توابع بعد از تابع main تعریف شوند، باید قبل از شروع تابع main توسط اعلان (الگوی تابع)، به برنامه اطلاع داده شود که در زیر تابع main توابعی وجود دارد، زیرا وقتی یک برنامه آغاز میشود، فقط کدهایی که قبل و درون تابع main وجود دارد، برای برنامه قابل رؤیت هست. پس ایجاد تابع در روش دوم شامل دو مرحله است:
- تعریف تابع(function definition)
- پیشتعریف یا الگوی تابع(function prototype)
حال قالب کلی الگوی تابع را مشاهده کنید:
<output type> <function name>(parameter1, parameter2, ...)
تفاوتی که تعریف و الگوی تابع با هم دارند در بدنه تابع است. برای الگو نیازی به نوشتن بدنه تابع نیست و فقط نوشتن خروجی، نام و پارامترهای تابع کافی است. برای درک بهتر موضوع به مثال زیر دقت کنید:
#include <stdio.h>
//الگوی تابع
void myFunc(void);
int main(){
//محل نوشتن بدنه تابع
}
//بدنه اصلی یا تعریف تابع
void myFunc(void){
//محل نوشتن بدنه این تابع
}
تا این جا آموختیم که تابع چیست، چه اجزایی دارد، چگونه باید تعریف شود و الگوی تابع برای چه کاری استفاده میشود. اما میتوان تابع در زبان C را از دیدگاه ورودی و خروجی به ۴ دسته تقسیم کرد:
- تابع با ورودی، با خروجی
- تابع با ورودی، بدون خروجی
- تابع بدون ورودی، با خروجی
- تابع بدون ورودی، بدون خروجی
۱ – تابع با ورودی، با خروجی
این نوع تابع را میتوان به دستگاهی تشبیه کرد که مواد اولیه را از ورودی دریافت میکند. اعمال مشخصشده را بر روی آن انجام میدهد و سپس محصول نهایی را به خروجی باز میگرداند.
همانطور که پیشتر بیان شد، منظور از ورودی در یک تابع، مقداری است که تابع در برنامه دریافت میکند و منظور از خروجی در یک تابع، مقداری است که پس از عملیات موردنظر در تابع، به برنامه بازگردانده میشود.
قالب یک تابع دارای ورودی و خروجی را در زیر مشاهده میکنید:
<function output> <function name>(<function parameters>)
{
//معرفی متغیرهای محلی
//کدهای تابع
return مقداری که باید به خروجی بازگردانده شود;
}
- در خط یک نوع خروجی، نوع ورودی ها و نام تابع معلوم می شود.
- در خط سه محل تعریف متغیرهای محلی که درون تابع استفاده میشود مشخص شده است که می توان چندین خط را به این تعاریف اختصاص داد. این متغیر شامل انواع دادههایی که در جلسات گذشته مرور شد (مانند کاراکتر، عدد صحیح و …) می شود.
- منظور از خط چهار این است که بعد از تعریف متغیرها می توان دستورات بدنه تابع را نوشت.
- همان طور که در خط پنج مشاهده می کنید با استفاده از دستور return تابع مقادیر تولید شده را به خروجی می دهد و روال اجرای تابع متوقف خواهد شد.
به مثال زیر توجه کنید:
int fx (unsigned char x, unsigned char y){
int s;
s = x * y;
return s;
}
- تابع fx دو مقدار را در ورودی دریافت میکند و ضرب دو مقدار را به خروجی تابع باز میگرداند.
- نوع داده خروجی تابع از نوع int و دارای دو ورودی از نوع unsigned char هست.
- درون تابع یک متغیر محلی از نوع int با نام s معرفی شده است که نتیجه عمل ضرب دو ورودی x و y را در خود نگه میدارد.
- در خط انتهایی تابع، متغیر s توسط دستور return به خروجی تابع منتقل میشود.
- این تابع را میتوان در بالای تابع main یا در فایلهای سرآمد (Header) و یا در زیر تابع main تعریف کرد.
- اگر تابعی در زیر تابع main تعریف شود، باید الگوی تابع قبل از تابع main قرار گیرد.
اعلان تابع تعریفشده در مثال بالا را، در زیر مشاهده میکنید:
int fx (unsigned char x, unsigned char y);
بخش ابتدایی تابع به همراه یک ; در انتهای آن، اعلان تابع نامیده میشود که قبل از تابع main قرار داده میشود. به هر تعداد تابع که در زیر تابع main تعریف شود، باید اعلان آنها را در بالای تابع main قرار گیرد.
مثال زیر نحوه استفاده از تابع تعریفشده در بالا را که دارای ورودی و خروجی است، در خطوط اصلی برنامه نشان میدهد:
#include <stdio.h>
int fx (unsigned char x, unsigned char y);
int w,z;
unsigned char i=4,j=6;
void main(void){
w = fx(4,5);
z = fx(i,j);
printf("%d, %d", w, z);
}
int fx (unsigned char x, unsigned char y)
{
int s;
s = x * y;
return s;
}
- در خط یک کتابخانه استاندارد ورودی خروجی زبان C به برنامه include یا ضمیمه شده است. برای کار با ورودی خروجیها به این فایل نیاز است، چرا که پارامترهای لازم برای کار با ورودی خروجیها در این فایل نوشته شده است.
- در خط سه الگوی تابع fx معرفی شده است.
- در خط ۴ و ۵ متغیرهای عمومی (global) که در تمام طول برنامه قابل شناسایی هستند تعریف شده اند.
- خطوط ۷ تا ۱۰ مربوط به تابع main و دستورات آن است. این تابع نه ورودی دارد و نه خروجی. می دانید چرا؟
- خطوط ۱۲ تا ۱۷ هم که تعریف تابع fx است.
- چون محدوده دید کدها از ابتدای include ها تا پایان تابع main است، اگر الگوی تابع fx در بالا نوشته نشود کامپایلر تعریف تابع fx را نخواهد دید.
- به عنوان تمرین بررسی کنید که بعد از اجرای این کد متغیرها چه مقداری خواهند داشت. صحت آن را در محیط کد بلاکس هم بررسی کنید.
۲ – تابع با ورودی، بدون خروجی
تابع با ورودی بدون خروجی تابعی است که مقادیر عددی را از ورودی دریافت کرده، اما مقداری را به خروجی باز نمیگرداند. برای مثال این نوع تابع میتواند مقادیر دریافت شده از ورودی را بر روی صفحه نمایش چاپ کند.
قالب یک تابع با ورودی، بدون خروجی را در زیر مشاهده میکنید:
void <function name> (<function parameters>)
{
//معرفی متغیرهای محلی
//کدهای تابع
}
چون این تابع دارای خروجی نیست، در خروجی آن از کلمه void استفاده شده است.
به مثال زیر توجه کنید:
void gx (unsigned char x, unsigned char y)
{
unsigned char s;
s = x & y;
PORTD = s;
}
- تابعی بانام gx دو مقدار را از ورودی دریافت میکند و مقدار and بیتی آنها را بر روی پورت D میکروکنترلر AVR قرار میدهد.
- در خروجی تابع عبارت void قرارگرفته است و دارای خروجی نیست.
- تابعی که دارای خروجی نیست، فاقد دستور return است، زیرا مقداری به خروجی بازگردانده نمیشود.
مثال زیر نحوه استفاده از تابع تعریف شده در بالا را که دارای ورودی و بدون خروجی است، در خطوط اصلی برنامه نشان میدهد:
#include <stdio.h>
void gx (unsigned char x, unsigned char y)
{
unsigned char s;
s = x & y;
PORTD = s;
}
void main(void){
unsigned char i = 32, j = 144;
gx(i, j);
}
- تابع gx قبل از تابع main تعریف شده، به همین دلیل نیازی به استفاده از الگوی تابع وجود ندارد.
- در تابع gx مقادیر دریافت شده، پس از عمل and بیتی بر روی PORTD میکروکنترلر AVR قرار میگیرد.
- چون تابع gx خروجی ندارد، وقتی در تابع main فراخوانی میشود به هیچ متغیری نسبت داده نشده است.
۳ – تابع بدون ورودی، با خروجی
تابع بدون ورودی، با خروجی تابعی است که مقادیری را از ورودی دریافت نمیکند، اما مقداری را به خروجی باز میگرداند. برای مثال این نوع تابع میتواند مقادیر قرار گرفته بر روی رجیستر های موردنظر را بخواند و به خروجی بازگرداند. در این نوع تابع، کلمه void در ناحیه ورودی تابع قرار میگیرد.
قالب یک تابع بدون ورودی، با خروجی را در زیر مشاهده میکنید:
<output type> <function name>(void)
{
//معرفی متغیر های محلی
//کدهای تابع
return مقداری که باید به خروجی بازگردانده شود;
}
به مثال زیر توجه کنید:
usigned char kx (void)
{
unsigned char a, b, c;
a = PINA;
b = PINB;
c = a | b;
return c;
}
- این تابع رجیسترهای PINA و PINB میکروکنترلر AVR را میخواند اما از برنامه چیزی به عنوان ورودی تحویل نمیگیرد.
- چون تابع kx دارای خروجی است، از دستور return استفاده شده است.
۴ – تابع بدون ورودی، بدون خروجی
تابع بدون ورودی، بدون خروجی تابعی است که نه مقادیر را از ورودی دریافت میکند و نه مقادیری را به خروجی باز میگرداند. معمولاً توابعی که فقط عملیاتی را بر روی رجیسترها انجام میدهند از این قبیل هستند. در این نوع تابع، کلمه void هم در ورودی و هم خروجی تابع قرار میگیرد.
قالب یک تابع بدون ورودی بدون خروجی را در زیر مشاهده میکنید:
void نام تابع( void)
{
//معرفی متغیر های محلی
//کدهای تابع
}
به مثال زیر توجه کنید:
void dx (void)
{
PORTD = 0b00001111;
PORTC = 0b00001111;
}
- تابعی بانام dx مقدار باینری ۰۰۰۰۱۱۱۱ را بر روی دو رجیستر میکروکنترلر AVR قرار میدهد.
- در ناحیه ورودی و خروجی تابع کلمه void قرارگرفته است که بیانگر آن است که تابع فاقد ورودی و فاقد خروجی است.
طول مناسب برای خروجی تابع
نوع دادههایی که توابع به خروجی باز میگردانند باید بهگونهای انتخاب شود که نتیجه محاسبات را با بیشترین مقدار در خود حفظ کنند. برای درک موضوع به مثال زیر دقت کنید:
مثال: تابعی را در نظر بگیرید که دو مقدار از نوع unsigned char را در ورودی دریافت میکند و ضرب آنها را به خروجی تابع منتقل میکنند.
همانطور که میدانیم، نوع داده unsigned char از نوع ۸ بیتی بدون علامت بوده و میتواند مقادیر عددی ۰ تا ۲۵۵ را در خود حفظ کند.
اگر این دو ورودی بیشترین مقدار خود (۲۵۵) را داشته باشند، ضرب این دو عدد ۸ بیتی نتیجهای با طول ۱۶ بیت را به همراه خواهد داشت؛ بنابراین خروجی این تابع باید از نوع unsigned int تعریف شود تا خطایی در زمان اجرا رخ ندهد.
فراخوانی تابع در زبان C
تا این جای بحث درباره انواع توابع و خصوصیات و نحوه تعریف آنها صحبت کردیم. بعد از تعریف یک تابع، لازم است از آن استفاده کنیم. در واقع میخواهیم تابع را صدا بزنیم تا برایمان کاری انجام دهد. سوال این است که چطور تابع را صدا بزنیم؟
فراخوانی یا صدا زدن به معنای ارسال آرگومان(مقدار) به تابع و تحویل گرفتن خروجی است. فراخوانی به دوشکل تعریف میگردد:
- فراخوانی تابع با مقدار(call by value)
- فراخوانی تابع با ارجاع(call by reference)
فراخوانی تابع با مقدار
در فراخوانی تابع با مقدار یک کپی از مقدار آرگومانهای تابع روی پارامترهای آن نوشته میشود، بنابراین تغییر آرگومان تاثیری روی مقدار پارامتر نمیگذارد.
به مثال زیر دقت کنید:
#include <stdio.h>
float tempconv(float centigrade);
float c, f;
void main(void){
c = 38.7;
f = tempconv(c);
}
float tempconv(float centigrade){
float i;
i = 1.8 * centigrade + 32;
return i;
}
- در این مثال تابع tempconv تبدیل دما برحسب سانتیگراد به فارنهایت را انجام میدهد.
- آیا میتوانید تعریف و الگوی تابع را بر اساس مثالهایی که بررسی شد توضیح دهید؟
- متغیرهای f و c از نوع متغیر عمومی یا global هستند و در تمام برنامه و توابع قابل استفاده هستند. چون مقدار اولیه به آنها داده نشده، به صورت پیش فرض صفر خواهند بود.
- متغیر i یک متغیر محلی است که فقط در محدوده ی تابع tempconv تعریف شده است و بعد از خروج از تابع از بین میرود.
- اگر بخواهیم متغیر i با خروج از تابع از بین نرود باید قبل از تعریف متغیر از کلمه static استفاده کنیم.
- در زبان C متغیرهای عمومی مقدار اولیه صفر دارند در حالی که متغیرهای محلی مقدار اولیه نامشخصی دارند.
- خط ۹ جایی است که تابع tempconv فراخوانی یا صدا زده شده است.
- مقدار متغیری که به عنوان ورودی به تابع داده شده ( متغیر C آرگومان تابع است) روی ورودی تابع ( متغیر centigrade پارامتر تابع است) کپی می شود.
- بعد از انجام این کپی، هر تغییری در طول برنامه، روی متغیر C انجام شود هیچ اثری روی مقدار پارامتر centigrade ندارد و برعکس. چرا که فقط یک کپی از متغیر C به تابع تحویل داده شده و نه آدرس آن.
فراخوانی تابع با ارجاع
اما حالت دیگری هم در زبان c وجود دارد که در آن بدنه تابع مستقیما دستری به آرگومانها داشته باشد. این قابلیت مفیدی است که به آن فراخوانی با ارجاع میگویند. در این روش آدرس آرگومان روی پارامتر کپی میشود. در بدنه تابع از آدرس ارسال شده برای دسترسی به آرگومان استفاده میشود، به این ترتیب تغییرات ایجاد شده در متغیر پارامتر، روی آرگومان نیز تاثیر میگذارد.
به مثال زیر دقت کنید:
#include <stdio.h>
void tempconv(float centigrade, float *farenheit, float *kelvin);
float c, f, k;
void main(void){
c = 38.7;
tempconv(c, &f, &k);
}
void tempconv(float centigrade, float *farenheit, float *kelvin){
*farenheit = 1.8 * centigrade + 32;
*kelvin = centigrade + 273.15;
}
- در این برنامه دما بر حسب سانتیگراد دریافت شده و بر حسب فارنهایت و کلوین تحویل داده میشود.
- تابع tempconv مسئول انجام این تبدیل است.
- این تابع هیچ گونه خروجی ندارد و در عوض سه پارامتر دارد.
- پارامترهای این تابع متغیر اعشاری centigrade و اشاره گرهای farenheit و kelvin هستند که به نوع اعشاری اشاره میکنند.
- اگر نیاز به مطالعه بحث اشاره گر دارید حتما از قسمت یادآوری در ابتدای همین پست اقدام کنید.
- آرگومان هایی که به این تابع در زمان فراخوانی تحویل داده میشود، مقدار متغیر C (فراخوانی با مقدار) و آدرس متغیرهای f و k (فراخوانی با ارجاع) است.
- می توانید دلیل استفاده از اشاره گر به عنوان پارامترهای تابع را توضیح دهید؟
- چون از فراخوانی با ارجاع استفاده شده است می توان مقدار متغیرهای f و k را که خروجیهای این تابع هستند، از درون تابع تغییر داد. چیزی که در فراخوانی با مقدار ممکن نبود.
در روش فراخوانی با مقدار، تابع میتواند یک یا چند ورودی و تنها یک خروجی داشته باشد. اما در بسیاری از کاربردها لازم است که تابع چندین خروجی داشته باشد.
یکی از کاربردهای مفید فراخوانی تابع با ارجاع، حل همین مشکل است که در ابتدای همین پست آن را مطرح کرده بودیم.
تابع printf
در جلسه امروز چندین بار به کتابخانه اشاره شد و با توضیحاتی که داده شد توانستید یک درک نسبی به دست آورید. حال از کتابخانه استاندارد C دو تابع مهم و پرکاربرد را بررسی می کنیم. تابع printf و sprintf که در فایل header با نام stdio.h قرار گرفتهاند. این توابع به ترتیب برای چاپ بر روی صفحه نمایش و ساخت رشتهها به کار میرود.
قالب استفاده از تابع printf را در زیر میبینید:
printf("مقدار ,"<کاراکتر کنترلی/> کاراکتر فرمت % عبارتی که باید عیناً نمایش داده شود);
برای درک بهتر نحوه استفاده از این تابع، به مثال زیر توجه کنید:
int i = 75;
printf(“value = %d\n”, i);
نتیجه مثال بالا را در زیر مشاهده کنید:
value = 75
- در ناحیه ورودی تابع printf، ابتدا یک عبارت درون ” و ” قرارگرفته است.
- حروف و کاراکترهای قرارگرفته در این عبارت تا قبل از علامت d% عیناً در خروجی چاپ میشود.
- در محلی که d% قرار دارد، مقدار متغیر i بهجای آن قرار میگیرد.
- کاراکتر بعد از علامت % را اصطلاحاً کاراکتر فرمت مینامند.
- این کاراکترها مشخص میکنند که نوع نمایش مقدار متغیر مربوطه در محل موردنظر به چه شکل باشد. کاراکترهای فرمت مشخص میکنند که مقدار متغیر i بهصورت اعداد مبنای ۱۰، بدون علامت، اعشاری، کدهای اسکی یا … باشد.
- مثلاً اگر بهجای d% کاراکتر فرمت f% قرار گیرد، نمایش مقدار متغیر i بهصورت اعشاری می شود.
- کاراکتری که بعد از علامت \ قرار دارد را اصطلاحاً کاراکتر کنترلی مینامند. کاراکترهای کنترلی شکل نمایش جایگاه عبارات را مشخص میکنند که عبارات در یک سطر بافاصله قرار گیرند یا هر عبارت در یک سطر قرار گیرد و یا … .
- در مثال بالا n\ باعث میشود نمایش هر بار در یک سطر جدید انجام شود.
- قرار دادن کاراکتر کنترلی اجباری نیست.
- علاوه بر این روش میتوان یک رشته کامل به تابع printf تحویل داد تا در خروجی چاپ کند.
تابع sprintf
قالب تابع sprintf مشابه تابع printf است. با این تفاوت که تابع sprintf بهجای نمایش در خروجی، یک رشته تولید میکند. این رشته میتواند برای نمایش به هر محلی همچون صفحهنمایش، کامپیوتر یا … ارسال شود. درون این تابع باید یک رشته با طول مناسب قرار گیرد.
قالب تابع sprintf را در زیر مشاهده میکنید:
sprintf(مقدار ,"کاراکتر فرمت % عبارتی که باید عیناً نمایش داده شود" , نام آرایه );
برای درک بهتر نحوه استفاده از این تابع، به مثال زیر توجه کنید:
char str1[20] ,str2[20];
int i = 75;
float j = 12.789;
sprintf(str1, ”value1 = %d value2 = %f\n“, i, j);
sprintf(str2, “value1 = %4.4d value2 = %2.1f\n“, i, j);
printf(str1);
printf(str2);
خروجی این برنامه به شکل زیر است:
value1 = 75 value2 = 12.789
value1 = 0075 value2 = 12.8
- ساختار ورودی تابع sprintf مشابه تابع printf است با این تفاوت که یک رشته نیز در ورودی تابع sprintf قرار میگیرد و این تابع، رشته را با آرایش موردنظر پر میکند.
- در این مثال دو متغیر وجود دارد، این تابع میتوانند متغیرهای زیادی را نمایش دهد.
- در خط ۴ متغیر i در محل d% و متغیر j در محل f% قرار میگیرد. نمایش مقدار متغیر j بهصورت اعشاری خواهد بود.
- میتوان تعداد ارقام قابل نمایش را با قرار دادن عددی بین علامت% و کاراکتر فرمت، مشخص کرد.
- در خط ۵ عبارت ۴٫۴d به معنای چاپ حداقل ۴ رقم و ۲٫۱f به معنای دو رقم برای عدد صحیح و یک رقم برای اعشار است.
- وقتی یک رقم برای اعشار در نظر میگیریم، باعث گرد شدن و تبدیل ۷ به ۸ میشود.
- در نهایت به وسیله printf این دو رشته در خروجی چاپ شدهاند.
کاراکتر فرمت
انواع کاراکترهای فرمت در جدول زیر قابل مشاهده است:
در اینجا لازم است در رابطه با عددی که بین کاراکتر فرمت و علامت % قرار میگیرد توضیحی داده شود. در این راستا سه مورد مهم را تعریف میکنیم:
جداکننده
یک علامت .(نقطه) برای جداکردن طول میدان از دقت مقدار که در ادامه بیان خواهد شد.
طول میدان
یک عدد صحیح در سمت چپ جدا کننده که حداقل تعداد ستون مورد استفاده برای چاپ را مشخص میکند. در صورتی که اندازه مقداری که قرار است چاپ شود کوتاهتر از طول میدان باشد، به تعداد لازم فاصلهی خالی در سمت چپ اضافه میشود.
اگر اندازهی مقداری که قرار است چاپ شود، بیشتر از طول میدان باشد به جای طول میدان داده شده از طول میدان مناسب برای چاپ مقدار به صورت کامل استفاده میشود.
در مورد اعداد منفی، برای علامت هم باید یک ستون در نظر گرفته شود.
دقت مقدار
یک عدد صحیح در سمت راست جدا کننده که برای اعداد اعشاری تعداد ارقام اعشاری مورد نیاز برای چاپ را مشخص میکند. در صورتی که عدد دارای ارقام اعشاری بیشتری باشد، عدد قبل از چاپ تا این تعداد رقم گرد میشود و در صورتی که عدد دارای تعداد ارقام اعشاری کمتری باشد به تعداد لازم صفر در سمت راست عدد اضافه میگردد.
اگر دقت صفر قرار داده شود باعث حذف شدن قسمت اعشاری و ممیز اعشاری و گرد شدن قسمت صحیح میگردد.
دقت مقدار برای اعداد صحیح، حداقل تعداد رقمی که باید چاپ شود را تعیین میکند. اگر تعداد رقمها از این عدد( دقت مقدار ) کمتر باشد به تعداد لازم در سمت چپ صفر اضافه میگردد و سپس چاپ میشود.
سخن آخر
دوستان عزیز امیدوارم که از مقاله راضی بوده باشید 🙂
بحث خیلی مهم تابع در زبان C هم تموم شد. اول از همه بهتون یه خسته نباشید میگم و بعدش یه تبریک جانانه چون دیگه چیزی نمونده بتونید به زبان C کد نویسی کنید و میکروکنترلر مورد نظرتون را راه اندازی کنید.
اما یه نکته کوچولو و یه خواهش دوستانه. برای تهیه این مطالب وقت و انرژی زیادی صرف می شه تا شما بتونید ازشون لذت و استفاده کافی را ببرید. اگه شما زیر مطالب نظر بدید یا از لینک شبکه های اجتماعی که همین جا زیر پست اومده، ما رو به دوستاتون هم معرفی کنید خستگی از تنمون در میاد و تشویق می شیم تا مطالب بهتری که شایسته ی مخاطبانمون هست تولید کنیم. 🙂 جلسه بعد با بحث دستورات پیشپردازنده ادامه میدیم.
به عنوان آخرین حرف، یه پیشنهاد ویژه دارم برای اون دسته از دوستانی که علاقه دارند خیلی عمیقتر زبان C را یاد بگیرن و برای میکروکنترلرها یا کارهای نرم افزاری ازش استفاده کنند.دوره جامع زبان C را براتون تهیه کردم که فوق العاده پروژه محور و کاربردیه. خوش حال میشم در اون دوره شما رو ببینم. برای دسترسی به دوره میتونید روی عکس زیر کلیک کنید.
درباره احسان عبداللهی
مدیر و موسس میکرولرن | برنامه نویسی پاسخی ظریف به یک نیاز در دنیای واقعی هست.
نوشته های بیشتر از احسان عبداللهیمطالب زیر را حتما مطالعه کنید
دوره های آموزشی مرتبط
آموزش جنگو
آموزش پایتون
آموزش الگوریتم و فلوچارت – تفکر برنامه نویسی
آموزش برنامه نویسی C برای میکروکنترلر
آموزش الکترونیک دیجیتال
68 دیدگاه
به گفتگوی ما بپیوندید و دیدگاه خود را با ما در میان بگذارید.
عالی عالی و عالی
چقدر تمیز و خوب نوشته شده بود.
تشکر
درود بر شما
خیلی کمکم کرد ممنونم ومناسب برای همه ی سطح ها میباشد.
از شما متشکرم
سلام مهندس جان. واقعا سایت حرفه ای داری هم از نظر گرافیک هم از نظر محتوا که به درد طیف وسیعی از مخاطبان با سطوح دانش مختلف میخوره. واقعا دمت گرم
سلام
خواهش میکنم جناب دلاوری پور. نظر لطف شما هست. امیدوارم که براتون مفید باشه.
با سلام
اول از همه تشکر میکنم بابت وقتی که گذاشتین و این مطالب را نوشتین.
من دارم با استفاده از برنامه c++ یه برنامه واسه یه ایستگاه آب و هوا مینویسم
باید بگم که همه چیز رو با روش سعی و خطا شروع کردم و در برنامه نویسی به جز یه درس ۳ وحدی دوره لیسانس هیج جیز نمیدونم
بگذریم
توی این ایستگاه آب و هوایی سنسور های مختلفی هست مه یکیشون جهت وزش باد را نشون میدهخروجی این سنسور عدد هگزادسیماله که بعدا به جهت تبدیل میشه ( مثلا جهت N برای شمال )حالا کاری که من میخوام بکنم اینه که این جهت را در خروجی آردوینو نشون بدم…
از دستور sprintf میخوام استفاده کنم چون جهت باد تغییر میکنه
چیزی ک به عنوان دستور نوشتم اینه :
sprintf( Windrichtung, “\n %p”, (Windrichtung));
این windrichtung یعنی جهت باد 🙂
چیزی که توی خروجی به عنوان خطا دریافت میکنم
cannot convert ‘double’ to ‘char*’ for argument ‘1’ to ‘int sprintf(char*, const char*, …)’
میشه کمکم کنین؟؟
ممنون
سلام
خواهش میکنم
دستور sprintf برای این استفاده میشه که شما یک رشته را به صورت دلخواه فرمت کنید و درون یک string با نام دلخواه ذخیره کنید.
حالا اینجا فرمت کردن درست رشته خیلی مهمه. شما الان دارید تبدیل double به char انجام میدید که اشتباه هست.
باید جنس یکسان داشته باشند.
سلام.
مناز MikroC نسخه ۷٫۶٫۰ استفاده میکنم. ولی تابع SPrintf رو نداره واز اون خطا میگیره.
البته ۲ تابع دیگه به نامهای sprinti و sprintl داره.
مشکل از چی میتونه باشه؟ یا چه کاری باید انجام بدم؟
ممنون
سلام
mikroc یک محیط استاندارد کدنویسی نیست و برای نیاز کاربران تغییراتی در کدهای اصلی داده. بهتره از atmel studio و کدهای استاندارد استفاده کنید.
ممنون. ولی برای PIC میخوام برنامه نویسی کنم.
سلام.یه برنامه هست که اگه از روش عددی بدید میشه فهمید که اول هست یا نه. حالا بعد دستورات پرینت اف و اینا که برای کاربر متن رو چاپ میکنه و بعدشم تابع اسکن اف و بعدشم که یه سری تعریف متغیر و لوپ for. اما قبل از نوشتن حلقه for یه جا هستش که نوشته : prime = TRUE ; . توی بخش هدر هم البته تعریف شده که TRUE مساوی ۱ هست. الان من اصلا این رو نمیتونم درک کنم که این چی هست(منظورم همون prime = TRUE; هستش) برای چی نوشته میشه و اگه نباشه چی میشه. لطفا کمکم کنین.
سلام
باید کدتون را ببینم تا بتونم قطعی بگم.
اما prime که به معنی عدد اول هست. احتمالا در برنامه میخواد بگه متغیر prime درسته یعنی اول هست مگر اینکه در فرآیند تشخیص عدد اول بفهمه که false هست.
مرسی که انقدر زود جواب دادید. بله همینه دلیلش و من هم دقیقا همینو نمیتونم بفهمم. خواهشا یه مقدار بیشتر توضیح بدید. مثلا الان من بخوام خودم یه برنامه بنویسم باید از کجا بفهمم که این رو باید بنویسم. کاملا روی این موضوع من گیج شدم
خواهش میکنم.
برای اینکه بهتر راهنماییتون کنم از قسمت پایین سمت راست روی گزینه واتساپ کلیک کنید تا وارد چت بشه و به شما پاسخ بهتری بدم.
اگر به هر دلیلی پیداش نکردید به این شماره در واتساپ پیام بدید. ۰۹۰۱۷۰۵۸۲۳۶
عالی….
واقعا ممنون. با زبان شیرین ، مطالب رو به بهترین وجه بیان میکنید و آموزش میدین.
من مطلبی رو تو سایتهای مختلف خوندم و خیلی متوجه نمیشدم. تو سایت شما فهمیدم که مطالب در اون سایت ها یا کامل ارائه نشده بود و یا اینکه خوب توضیح داده نشده بود.
موفق و پیروز باشید.
سلام دوست عزیز
خوشحالیم که براتون مفید بوده. تمام تلاشمون اینه که بهترین آموزش ها را ارئه بدیم. همیشه در کنارتون هستیم.
با سلام و عرض تشکر بابت مطالب مفید و مختصری که بصورت روان و خوب بیان می کنید
خدا قوت ، خدا خیرتون بده
با عرض سلام وحتما خسته نباشید مطالب رو مطالعه کردم و خیلی یاد گرفتم از لطفتون ممنونم .خدا خیرتون بده
خواهش میکنم. موفقیت شما باعث افتخار ما هست.
سلام.خیلی ممنون از مطالب مفیدتون. من یک پروژه تاکوتر انجام دادم ولی در بخش ارسال اطلاعات به روی lcd برام مشکل پیش امده وقتی مقدار دور بالاتر از ۶۵۵۳۵ که مقدار int باشه می رود ال سی دی ریست میکه کثلا برای دور ۷۰۰۰۰ مقدار ۴۵۳۵ رو نمایش میده فکر کنم اشکال از دستور sprintf باشه که به کار بردم چون با دستور if امتحان دکردم سرریز نمی شد میشه لطفا راهنمایی کنید
کار با تابع sprintf را گوگل کنید.
مطالب بسیار خوب و جالبی بود اما
توضبح درباره تابع نوشتن در فایل های کتابخانه ای که اول مبحث
اشاره شد چیزی کفته نشد
ممنون میشم اگر این مورد هم گته بشه.
با سلام و درود
میتونید از لینک زیر به مقاله کتابخانه دسترسی داشته باشید:
https://microlearn.ir/5347/library.html
بابا دمتون خیلی گرمه خلی استفاده کردم خیلی دوست دارم به نحوی به این کارها کمک کنم که ادامه دار باشه اگه راهنمایی بفرمایید ممنون میشم
سلام دوست عزیز
مرسی از لطف شما. همین که با کامنت هاتون به ما انرژی میدید خودش ارزشمنده.
با سلام و احترام
صمیمانه از این مطلب عالی که گذاشته بودین متشکرم. لذت بردم.
بسیار کامل و جامع بود. خوشحالم که سایتتون رو پیدا کردم.
من یک کارشناس ارشد مهندسی مکانیک و علاقمند به کد نویسی هستم و از توابع بشدت استفاده میکنم.
درود بر شما.
عالی بود ، استادمون هم انقدر کامل توضیح نداده بود
خوشحالم که براتون مفید بوده
با سپاس از شما. مفید بود
با سلام. توضیحاتتون در مورد مبحث تابع خیلی خوب و موثر بود و برای بنده مفید واقع شد. خیلی ممنونم بابت این آموزش خوبتون.
خیلی عالی و خوب.از بهترین توضیحات و آموزشایی بود که در سطح اینترنت و حتی کتب مختلف دیدم.خسته نباشید.امیدوارم روز به روز پیشرفت کنید و آموزشهای بیشتری ازتون ببینم.
عالی بود ، کامل و کاربردی . ممنون و خسته نباشید
سلام.
سپاس فراوان بابت این مطلب آموزشی.
در زبان برنامه نویسی C جهت {ویرایش/حذف/جستجوی} اطلاعات بعنوان مثال اطلاعات دانشجویان(نام و نام خانوادگی و شماره دانشجویی و نمرات دروس همراه واحد ها) که توسط کاربر قبلا وارد برنامه شده اند از چه دستوری استفاده می شود؟
سپاس بابت توجه شما.
سلام
معولا برای چنین کاری یک دستور از قبل آماده شده وجود نداره و شما باید اقدام به نوشتن تابع مناسب کنید.
خسلس عالی وبد
امیدوارم بازم به اموزش های عای تون ادامه بدید و ما استفاده کنیم
سلام
خداقوت
قسمت آخر که یاد میداد در یک تابع چطور چند تا خروجی داشته باشیم عالی بود
سلام.
خوشحالیم که براتون مفید بوده
خیلی عالی بود خدا قوت
سلام.
ممنون عالی بود
این هم عالی بود.
در مورد این بخش:
” همانطور که میدانیم، نوع داده unsigned char از نوع ۸ بیتی بدون علامت بوده و میتواند مقادیر عددی ۰ تا ۲۵۵ را در خود حفظ کند. اگر این دو ورودی بیشترین مقدار خود (۲۵۵) را داشته باشند، ضرب این دو عدد ۸ بیتی نتیجهای با طول ۱۶ بیت را به همراه خواهد داشت؛ بنابراین خروجی این تابع باید از نوع unsigned int تعریف شود تا خطایی در زمان اجرا رخ ندهد.”
در کدام بخش از آموزش به این موارد اشاره شده ؟
با سلام
در خطوط بالاتر همین بخش توضیح داده شده:
“بهعنوانمثال تابعی را در نظر بگیرید که دو مقدار از نوع unsigned char را در ورودی دریافت میکند و ضرب آنها را به خروجی تابع منتقل میکنند.”
ممنون خیلی عالی بود خسته نباشید
مهندس سلام من خیلی سوال میپرسم میدونم ببخشید
مهندس توی فراخوانی تابع با ارجاع اونجا که گفتین در فراخوانی با ارجاع میشه از چند خروجی استفاده کرد
میشه یک مثال بزنید
سلام
سوال پرسیدن مشکلی نداره. ولی شما چیزی را که داخل متن کامل توضیح دادیم باز میاید همونو میپرسید. مثالی که میخواهید داخل متن وجود داره. همون تابعی که داره ورودی سانتیگراد میگیره و خروجی کلوین و فارنهایت میده.
سلام.
من هرچی از دستور printf استفاده میکنم کار نمیکنه و روی lcd چیزی نشون داده نمیشه.ایا باید تنظیمات خاصی را در کدویژن انجام بدم؟
مثلا من این دستور رو مینویسم اما کار نمیکنه:
printf(“adad = %d”,student1.avrage)
در ضمن student1 یک structure هست.
میشه راهنمایی کنید. واقعا گیج شدم.
سلام
دستور printf چیزی روی lcd نشون نمیده بلکه اطلاعات را به پورت سریال میفرسته. برای نمایش روی lcd می تونید از دستور lcd_puts استفاده کنید.
سلام دوست عزیز
اول ممنون بابت آموزش های جامع و کامل شما
و دوم اینکه در مثالی که برای فراخوانی تابع با مقدار زدین در خط ۶ ,c=38.7 یک مقدار دلخواه است که در خط ۷ در f ذخیره شده؟میشه این دو خط رو بیشتر توضیح بدین؟ممنون
سلام به شما
بله یک مقدار دلخواه هست. چون این تابع کار تبدیل دما از سانتیگراد به فارنهایت را انجام میده، یک دمای دلخواه در نظر گرفته شده تا اون را تبدیل کنه.
سلام
من دانشجوی رشته Computer Science در لهستان هستم.
مطالبتون به من خیلییییییییییی کمک کرده تا حالا.
واقعا ازتون ممنونم و واقعا خسته نباشید.
سلام و عرض ادب
خیلی خوشحالیم که مطالب برای شما مفید بوده. حتما آموزش های ما را دنبال کنید و نظرات خودتون را بفرمایید.
خیلی خوشحالم دوست عزیز. موفق باشید
سلام
ممنون از مطلب فوقالعادتون
من میخام برنامه نویسی رو در حد کاربردی برای کار مثلا شرکت یا کارخونه یاد بگیرم برای برنامه ریزی
آیا این مطالب کافیه؟
اگه کافی نیست باید چیکار کنم ؟
با سلام و درود خدمت شما دوست عزیز
قطعا هیچ موقع مطالعه یک کتاب یا شرکت در یک دوره برای برنامه نویس شدن کافی نیست. حتی برنامه نویس های حرفه ای هم بعد از چند سال کار هنوز با نکات جدید روبه رو میشن. اما برای اینکه شما بتونید به یک حد قابل قبول برای شروع کار برسید میتونید از کتاب های برنامه نویسی استفاده کنید و یا در دوره های آموزش برنامه نویسی شرکت کنید. میکرولرن دوره های آموزشی به صورت آموزش خصوصی و تضمینی برگذار میکنه. برای اطلاع از کم و کیف این دوره ها میتونید با شماره زیر تماس بگیرید. در ضمن روی این شماره ایدی تلگرام هم فعال هست.
۰۹۰۱-۷۰۵۸۲۳۶
عالی بود… عالی
عالی بود دستتون درد نکنه
با عرض سلام
من تازە می خواهم برنامە نوسی زبان س را شروع کنم و میخواهم مرا راهنمایی کنید از کجا شروع کنم در ضمن اگر کتاب بە صورت پی دی اف دارید برام ایمیل کنید ممنون میشم
شاهین از کردستان
سلام به شاهین عزیز 🙂
در مرحله اول برای شروع زبان سی توصیه می کنم که ۱۲ جلسه ای که در سایت میکرولرن تهیه شده را مطالعه کنید تا دید کلی و خوبی نسبت به این زبان به دست آورید.
لینک جلسات آموزش زبان سی :http://microlearn.ir/category/programming/c
اما یک سوال : می خواید زبان سی را فقط در حد میکروکنترلرها یاد بگیرید یا برای کامپیوتر هم نیاز دارید باهاش کار کنید؟
جواب این سوال رو بدید تا بهتر شما را راهنمایی کنم.
دمتون گرم
کاش زودتر سایتتون رو پیدا میکردم
لطف داری دوست من. هر سوالی داشتی حتما بپرس تا بهش جواب داده بشه 🙂
دمت گرم مهندس جان
واقعا ممنونم بخاطر این سایت بی نظیرتون از همه نظر عالیه
اگه امکان داره برای ARM هم دقیقا مثله AVR کار کنید
با تشکر
لطف داری دوست خوبم. با این همه سوالای چالشی که شما می پرسی 😀 انگیزه ما هم بیشتر از قبل شده. حتما برای ARM هم در آینده آموزش های لازم ار قرار می دیم.
ببخشید در مثال فراخوانی تابع با ارجاع همانطور که گفتین برای چندین خروجی بکار میرود
همان طور که در ابنجا از اشارگر استفاده شده است یعنی تایع میره از آدرس مشخص f& , &d مقدار رو برمیدارد و نتیجه خروجی رو در کجا ذخیره میکند؟
سلام.
من ابتدا به شما توصیه می کنم که بحث اشاره گرها را از آدرس زیر با دقت بخونید:
http://microlearn.ir/199/c-pointers.html
اما در باره سوالتون :
در فراخوانی تابع با ارجاع، تعدادی از پارامترهای تابع اشاره گر هستند ولی هنگامی که می خواهیم تابع را فراخوانی کنیم باید به این اشاره گرها یک سری آدرس اختصاص بدیم. این آدرس ها همون f& و k& هستند. داخل بدنه تابع داریم با اشاره گرها کار می کنیم که حاوی آدرس این متغیرها هستند و هنگامی که مقداری را به اشاره گر داریم اختصاص می دیم در واقع داخل همون متغیرهای f و k ذخیره میشه. به این ترتیب شما می تونید قبل از تابع هر تuداد متغیر که دوس داشتی تعریف کنید و آدرسشون را به تابع بدید. با اجرای تابع نتیجه در این متغیرها ذخیره میشه چون تابع آدرسشون را داره. حالا با این توضیحات یک بار دیگه مثال فراخوانی با ارجاع را بررسی کن. 🙂
چیزی که شما نتیجه رو به این صورت نوشتید بنظر من باید sprintf هم به این صورت تعریف شود
Str1–> value1=75 value2=12.78900
Str1–> value1=0075 value2=12.7
——————————————————–
;(sprintf (str1,”value1=%d value2=%f “ , i , j
;(sprintf (str2 , “value1 = %4d value2 = %2.1f “ , i, j
برای spirintf اول حرفتون درست بود و اصلاح شد. اما برای دومی درسته و مشکلی خاصی نداره. در واقع ۴ که بعد از جدا کننده اومده میگه ۴ تا رقم اعشار می تونم داشته باشم ولی خب چون عدد اعشاری نیست چیزی هم نمایش داده نمیشه دیگه.
در واقع اگر اعشاری بود به این صورت نمایش داده میشد
۰۰۷۵٫۰۰۰۰
اگه به جای ۷۵ یک عدد اعشاری داشتیم و می نوشتیم ۴٫۴f اون موقع ۴ تا رقم برای اعشار در نظر می گرفت. اما در این حالت اصلا درست نیست که از f استفاده کنیم چون خروجی ۰ بر می گرده و باید از d استفاده کنید.
یه توصیه برات دارم: این بحث یکمی نیاز به تمرین داره. تا الان که به صورت دستی تمرین کردی. الان داخل یک کامپایلر زبان C برو همین کدها را بنویس و تست کن تا بهتر متوجه بشی که چی به چی هست.